/** * Handle zcode-initiated server→client requests during a turn. * * ZCode's interaction broker dispatches three request kinds, all bridged onto * ACP `session/requestPermission` (Zed supports it natively; elicitation is * not supported): * - interaction/requestPermission (tool auth) → direct option mapping * - interaction/requestUserInput (ExitPlanMode) → approve/reject options * - interaction/requestUserInput (AskUserQuestion) → per-question popups * (single-select: one popup; multi-select: per-option Include/Skip) * * Reannounce dedup: ZCode reannounces unanswered requests every ~1s sharing * the same requestId/toolCallId. The first request forwards to the client; * reannounces either get the cached result (if it arrived) or just record * their zcode id for a later unified reply. * * Reconnect resend: a client-side request fired while a remote client was * offline never reaches it. Undecided waits are tracked (ActiveInteraction) * and re-sent to a client when its session/load / session/resume completes — * the re-send joins the existing first-response-wins race. */ import type * as acp from "@agentclientprotocol/sdk"; import type { ZcodeBackend } from "../backend/client.js"; import type { ZcodeInteractionResponse, ZcodeInteractionUserInputParams } from "../backend/types.js"; import { type ClientLike } from "../remote/broadcast.js"; import type { PendingTurn, ZcodeAcpServer } from "../server.js"; /** A dedup entry tracking reannounced zcode ids + the cached result. */ interface DedupEntry { zcodeIds: Array; result?: ZcodeInteractionResponse; } /** Per-server reannounce dedup state (lazy-initialised). */ export declare function getPendingInteractions(server: ZcodeAcpServer): Map; /** * Re-send this session's still-undecided interaction requests to a client that * just (re)connected, so an agent question that fired while the client was * offline becomes answerable there. Fire-and-forget, best-effort: each re-send * joins the existing first-response-wins race; if another client answers first, * the re-send is cancelled and the reconnected client's dialog (if any) drops. */ export declare function resendPendingInteractions(server: ZcodeAcpServer, client: ClientLike, acpSid: string): void; /** * Drain and handle pending zcode server→client requests for THIS session only. * Returns true if any were handled (used by the turn loop to refresh the * no-progress timer). * * The backend's `serverRequests` queue is shared across all sessions (a single * subprocess serves them all). Without filtering, session A's turn loop could * pop session B's permission request and forward it to A's client — the popup * lands in the wrong session. When `turn` is available we filter by * `params.sessionId` so each turn loop only consumes its own requests; others * are re-queued for their owner. Without `turn` (tests / non-turn callers) we * process everything (legacy behaviour). */ export declare function handleServerRequests(server: ZcodeAcpServer, backend: ZcodeBackend, cx: acp.AgentContext, acpSid: string, turn?: PendingTurn): Promise; /** * Claim interaction requests no turn loop will ever poll. * * `handleServerRequests` runs ONLY inside a session's turn loop and claims * only that turn's session (everything else is requeued) — an ask from any * OTHER session, above all a background workflow run's execution, used to sit * in the arrival queue forever: never forwarded, never pushed, until the * backend's ask timeout killed the run (observed 2026-09-30: app-launched * runs died on an unanswered CreateWorkflow permission with no notification * at all — the "errored" rows in the run list). * * Armed on a 2s interval in the server constructor. Per queued request: * - owned by a running turn (or sid-less while any turn runs — a turn * claims sid-less asks) → requeued untouched, the turn keeps its claim; * - session maps to an ACP alias → forwarded through the full ask path * (client popup + offline hold + watchdog push) under ITS acpSid; * - unmappable — an internal run session no client can ever answer: * the workflow-graph tools auto-allow (launch consent covers building * the run's own graph; there is no human channel to ask), anything * else is declined fast with an offline push, so the run fails LOUD * instead of hanging silently. */ export declare function sweepOrphanedServerRequests(server: ZcodeAcpServer): Promise; /** * AskUserQuestion: sequential per-question, multi-select per-option. * * Single-select: one popup per question; Skip/cancel → overall decline. * Multi-select: one Include/Skip popup per option; Include picks comma-joined. */ export declare function handleAskUserQuestion(server: ZcodeAcpServer, cx: acp.AgentContext, acpSid: string, params: ZcodeInteractionUserInputParams, turn?: PendingTurn): Promise; /** * One candidate row for the `/resume` session picker. */ export interface SessionPickItem { /** Backend (zcode) session id — the value returned on selection. */ sessionId: string; /** Human label (title + date) shown in the dropdown / popup. */ label: string; } /** * `/resume` session picker: ask the user to choose a past session via the * editor's interaction UI — an `elicitation/create` enum dropdown when the * client supports forms, else `session/request_permission` option buttons. * Returns the chosen backend session id, or null when declined/cancelled or * the request failed (no turn context — the slash path has no PendingTurn). */ export declare function askSessionPick(server: ZcodeAcpServer, cx: acp.AgentContext, acpSid: string, items: SessionPickItem[]): Promise; /** * Marker returned when a wait was interrupted (connection close, env timeout, * or turn cancel) as opposed to the client deliberately returning null. Callers * flip `turn.cancelled` on this so the backend turn is stopped rather than * allowed to continue after the decline reply. */ declare const INTERRUPTED: unique symbol; type InteractionResult = unknown | typeof INTERRUPTED; /** * Interruption test for external {@link requestWithTimeout} callers * (sandbox-allow): distinguishes "wait broke" (connection close / turn * cancel) from a client response, so a no-decision outcome is not mistaken * for a user rejection. */ export declare function isInteractionInterrupted(value: unknown): boolean; /** * Send a client-side request and wait for the response. By default waits * indefinitely (see {@link INTERACTION_TIMEOUT_MS}). Resolves to the client * response, `null` if the client returned null/errored, or {@link INTERRUPTED} * if the wait was broken by connection close / env timeout / turn cancel. * * The client request runs inside a tracked first-response-wins race (see * {@link ActiveInteraction}): the broadcast proxy races the clients connected * at fire time, and `resendPendingInteractions` can add targeted attempts at * clients that attach mid-wait. After the wait settles (answered OR * interrupted — the caller replies decline), the entry is dropped, every * still-open attempt is aborted (best-effort only — see below), and * `$/zcode/ask_settled` tells the clients whose dialogs are still open to * dismiss them (the SDK ignores cancellationSignal, so the abort alone never * reaches the wire). */ export declare function requestWithTimeout(server: ZcodeAcpServer, cx: acp.AgentContext, method: string, params: unknown, label: string, timeoutMs?: number, turn?: PendingTurn): Promise; /** * Answer an `interaction/requestProviderRuntimeHeaders` request; returns false * when `method` is not that request (caller keeps its own handling). * * Shared by the turn-loop queue path (handleOne) and the ARRIVAL-TIME * responder wired onto the backend (ZcodeBackend.providerRuntimeHeadersResponder): * the backend asks before EVERY model request on a zhipu-account provider, and * a request that lands while no turn loop is polling the queue — compact's * internal turn, session/goal set, any backend-owned generation — timed out at * the backend's 180s cap as "Captcha verification request timed out" and the * compaction silently failed while the bridge still reported "✓ compressed" * (observed 2026-09-19, backend log `querySource: "compact"`). */ export declare function answerProviderRuntimeHeaders(backend: ZcodeBackend, reqId: number | string, method: string, params: Record): boolean; export {}; //# sourceMappingURL=server-requests.d.ts.map