@agentproto/acp 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts"],"names":["value"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * AIP-44 ACP client — drives a subprocess agent via stdio JSON-RPC.\n *\n * Wraps `@agentclientprotocol/sdk`'s `ClientSideConnection`. Owns the\n * connection lifecycle, runs `initialize` on construction, and exposes\n * a session API where each `prompt()` call returns an\n * `AsyncIterable<StreamEvent>` fed by the SDK's `session/update`\n * notification handler.\n */\n\nimport {\n ClientSideConnection,\n ndJsonStream,\n type Client as AcpClientHandlers,\n type Stream,\n} from \"@agentclientprotocol/sdk\"\nimport type { AcpMcpServer, StreamEvent } from \"../types.js\"\n\nconst PROTOCOL_VERSION_DEFAULT = 1\n\n/**\n * Map our internal `AcpMcpServer` (`{ name, transport, ref, headers, credentialRef }`)\n * onto the `@agentclientprotocol/sdk` wire shape expected by\n * `session/new.mcpServers` (and `session/load.mcpServers`).\n *\n * The ACP `McpServer` union is transport-tagged and field-named\n * differently from our compact internal form:\n * - http → `{ type: \"http\", name, url, headers: [{ name, value }, …] }`\n * - sse → `{ type: \"sse\", name, url, headers: [{ name, value }, …] }`\n * - stdio → `{ name, command, args: [], env: [] }` (untagged variant)\n *\n * Without this mapping the raw `{ transport, ref }` entry reaches the\n * agent verbatim and `session/new` rejects with `Invalid params` — the\n * symptom that broke orchestrator spawns (the auto-injected scoped\n * gateway is an `http` entry).\n *\n * Retro-compat: an entry that already carries an ACP-native discriminator\n * (`type`/`url`/`command`) is passed through untouched, so callers that\n * pre-shape their entries keep working.\n */\nexport function toAcpMcpServers(servers: readonly unknown[]): unknown[] {\n return servers.map(toAcpMcpServer)\n}\n\nfunction toAcpMcpServer(server: unknown): unknown {\n if (!server || typeof server !== \"object\") return server\n const entry = server as Record<string, unknown>\n\n // Already ACP-native (has a wire discriminator) → leave as-is.\n if (\"type\" in entry || \"url\" in entry || \"command\" in entry) return entry\n\n const transport = entry.transport\n // Not our `{ name, transport, ref }` shape → nothing to map.\n if (typeof transport !== \"string\") return entry\n\n const name = entry.name\n const ref = entry.ref\n const headers = entry.headers\n\n switch (transport) {\n case \"http\":\n return { type: \"http\", name, url: ref ?? \"\", headers: toAcpHeaders(headers) }\n case \"sse\":\n return { type: \"sse\", name, url: ref ?? \"\", headers: toAcpHeaders(headers) }\n case \"stdio\":\n return { name, command: ref ?? \"\", args: [], env: [] }\n default:\n return entry\n }\n}\n\nfunction toAcpHeaders(headers: unknown): Array<{ name: string; value: string }> {\n if (!headers || typeof headers !== \"object\") return []\n return Object.entries(headers).map(([name, value]) => ({\n name,\n value: typeof value === \"string\" ? value : String(value),\n }))\n}\n\nexport interface AcpClientOptions {\n /** Output bytes go here (subprocess stdin). */\n output: WritableStream<Uint8Array>\n /** Input bytes come from here (subprocess stdout). */\n input: ReadableStream<Uint8Array>\n /** Client identity advertised during `initialize`. */\n clientInfo?: {\n name: string\n title?: string\n version?: string\n }\n /** Client capabilities advertised during `initialize`. */\n capabilities?: {\n fs?: { readTextFile?: boolean; writeTextFile?: boolean }\n terminal?: boolean\n }\n /** Optional handlers for agent-initiated requests beyond fs / terminal. */\n handlers?: Partial<AcpClientHandlers>\n /** Protocol version to negotiate. Defaults to the SDK's current. */\n protocolVersion?: number\n /**\n * Called on ANY JSON-RPC traffic with the child agent process — every\n * incoming `session/update` notification (even ones that\n * `translateSessionUpdate` maps to `null`, e.g. a `tool_call_update`\n * still in progress) and every outbound RPC call this client makes\n * (`newSession`, `loadSession`, `setSessionConfigOption`, `prompt`,\n * `cancel`). Distinct from the `StreamEvent` stream: fires even\n * during gaps where no event is produced, so a host can track\n * liveness independent of what the agent actually says.\n */\n onActivity?: () => void\n /**\n * Turn-idle watchdog: if a `prompt()` call goes this many ms with NO\n * activity signal (see `onActivity` above — incoming `session/update`\n * notifications, outbound RPCs) and the underlying `connection.prompt()`\n * promise still hasn't resolved, synthesize a `turn-end` event with\n * `reason: \"watchdog-timeout\"` so the caller's async iterator completes\n * instead of hanging forever. The timer resets on every activity signal\n * observed DURING that turn, so a legitimately long tool-call chain\n * (still producing activity, just no user-visible output) never\n * false-positives — this is \"N ms of true silence,\" not \"N ms since the\n * turn started.\"\n *\n * Undefined (the default) disables the watchdog entirely — existing\n * callers see no behavior change. If the real `connection.prompt()`\n * eventually settles after the synthetic turn-end was already emitted,\n * the late result is logged and discarded (no crash, no duplicate\n * turn-end for the same logical turn).\n */\n turnIdleTimeoutMs?: number\n}\n\nexport interface AcpClient {\n readonly connection: ClientSideConnection\n /** Negotiated agent capabilities returned from `initialize`. */\n readonly agentCapabilities: Record<string, unknown> | undefined\n newSession(params: {\n cwd: string\n mcpServers?: unknown[]\n /**\n * Model to select via `session/set_config_option` immediately after\n * `newSession`. The claude-agent-acp wrapper supports this as\n * `configId:\"model\"`. Omit to keep the agent's own default.\n *\n * Best-effort: a value the agent can't resolve (a stale/gated/typo'd id)\n * is warned about and dropped — the session still starts on the agent's\n * default model rather than crashing the spawn. See the reject handler\n * in `newSession` for the reasoning (agentproto#186).\n */\n model?: string\n /**\n * Effort level to select via `session/set_config_option` after\n * `newSession`. Effort is model-dependent — same label ≠ same\n * budget across models; defaults differ too (see AgentCliConnectOptions).\n * Omit to keep the model's own default.\n */\n effort?: string\n /**\n * Mode to select via `session/set_config_option` after `newSession`,\n * for ACP servers that model mode selection as a session config\n * option (`configId:\"mode\"`) instead of a CLI flag — e.g. opencode.\n * Best-effort, same as `model`/`effort`: a rejection is warned about\n * and dropped rather than failing the spawn. Omit to keep the\n * agent's own default mode.\n */\n mode?: string\n }): Promise<AcpClientSession>\n /**\n * Reattach to an existing session by id. Available only when the\n * agent advertised `loadSession: true` in its capabilities (callers\n * should check `agentCapabilities` first; we forward the SDK error\n * verbatim if they don't).\n *\n * Drives the AIP-45 native-resume continuation strategy: the host\n * persists the `sessionId` returned by `newSession`, then on a\n * cold start (process restart, fresh sandbox, different machine)\n * spawns a new subprocess and calls `loadSession` to pick up\n * the conversation from the stored history.\n */\n loadSession(params: {\n sessionId: string\n cwd: string\n mcpServers?: unknown[]\n }): Promise<AcpClientSession>\n close(): Promise<void>\n}\n\nexport interface AcpClientSession {\n readonly sessionId: string\n prompt(input: {\n messages: unknown[]\n signal?: AbortSignal\n }): AsyncIterable<StreamEvent>\n cancel(): Promise<void>\n close(): Promise<void>\n}\n\ninterface SessionState {\n events: StreamEvent[]\n resolveNext: ((evt: StreamEvent | undefined) => void) | null\n done: boolean\n active: boolean\n /**\n * Set by `buildSession.prompt()` for the duration of an in-flight turn\n * when a watchdog timer is armed; cleared once that turn settles. Lets\n * the `sessionUpdate` handler (which only has a `sessionId`, not a\n * closure over the turn's timer) bump the SAME timer an incoming\n * notification is liveness evidence for.\n */\n resetWatchdogTimer?: () => void\n}\n\nexport async function createAcpClient(\n options: AcpClientOptions,\n): Promise<AcpClient> {\n const sessions = new Map<string, SessionState>()\n\n const stream: Stream = ndJsonStream(options.output, options.input)\n\n const connection: ClientSideConnection = new ClientSideConnection(\n () => buildClientHandlers(options.handlers ?? {}, sessions, options.onActivity),\n stream,\n )\n\n const initResponse = await connection.initialize({\n protocolVersion: options.protocolVersion ?? PROTOCOL_VERSION_DEFAULT,\n clientCapabilities: clientCapabilitiesFromOptions(options),\n clientInfo: options.clientInfo\n ? {\n name: options.clientInfo.name,\n title: options.clientInfo.title ?? options.clientInfo.name,\n version: options.clientInfo.version ?? \"0.0.0\",\n }\n : undefined,\n } as never)\n\n return {\n connection,\n agentCapabilities: (initResponse as { agentCapabilities?: Record<string, unknown> })\n .agentCapabilities,\n async newSession(params) {\n const response = await connection.newSession({\n cwd: params.cwd,\n mcpServers: toAcpMcpServers(params.mcpServers ?? []) as never,\n } as never)\n options.onActivity?.()\n const sessionId = (response as { sessionId: string }).sessionId\n const state: SessionState = {\n events: [],\n resolveNext: null,\n done: false,\n active: false,\n }\n sessions.set(sessionId, state)\n // Apply model + effort via session/set_config_option immediately\n // after newSession. The claude-agent-acp wrapper handles these as\n // configId:\"model\" and configId:\"effort\". Both are optional — when\n // omitted the agent keeps its own defaults (which vary by model).\n // We call these sequentially so a model switch (which rebuilds the\n // effort options) always precedes the effort set.\n if (params.model) {\n try {\n await connection.setSessionConfigOption({\n configId: \"model\",\n value: params.model,\n sessionId,\n } as never)\n options.onActivity?.()\n } catch (err) {\n // Non-fatal by design. claude-agent-acp validates the requested\n // model against the concrete option ids it advertises for THIS\n // session (e.g. \"default\", \"opus[1m]\", \"sonnet\", \"claude-sonnet-5\",\n // \"haiku\") and rejects anything it can't resolve — including stale\n // or gated ids like \"claude-sonnet-4-6\" — with a JSON-RPC -32603\n // whose only useful text is buried in `error.data.details`\n // (\"Invalid value for config option model: <id>\"). Letting that\n // reject `newSession` turned a bad model id into an opaque\n // \"spawn failed — Internal error\" that killed the whole session\n // (agentproto#186). The accepted vocabulary is dynamic (it varies\n // by wrapper version and account entitlements), so there is no\n // version-stable way to know it before the call — a rejected model\n // must therefore never be fatal. This is NOT a silent drop: the\n // requested id AND the server's own reason are logged, and the\n // session continues on the agent's default model.\n console.warn(\n `[acp] set_config_option model=\"${params.model}\" rejected by server ` +\n `— keeping the agent's default model. Reason: ${configOptionErrorDetail(err)}`,\n )\n }\n }\n if (params.effort) {\n try {\n await connection.setSessionConfigOption({\n configId: \"effort\",\n value: params.effort,\n sessionId,\n } as never)\n options.onActivity?.()\n } catch (err) {\n // Best-effort: this ACP server does not support the \"effort\" config\n // option (e.g. claude-agent-acp ignores unknown config keys and\n // some versions reject them outright). A rejected set_config_option\n // must never kill the spawn — effort is silently ignored instead.\n console.warn(\n `[acp] set_config_option effort=\"${params.effort}\" rejected by server ` +\n `(best-effort): ${configOptionErrorDetail(err)}`,\n )\n }\n }\n if (params.mode) {\n try {\n await connection.setSessionConfigOption({\n configId: \"mode\",\n value: params.mode,\n sessionId,\n } as never)\n options.onActivity?.()\n } catch (err) {\n // Best-effort: this ACP server does not support the \"mode\" config\n // option, or doesn't recognize the requested mode id. A rejected\n // set_config_option must never kill the spawn — mode is silently\n // ignored (the session keeps whatever mode the agent defaults to).\n console.warn(\n `[acp] set_config_option mode=\"${params.mode}\" rejected by server ` +\n `(best-effort): ${configOptionErrorDetail(err)}`,\n )\n }\n }\n return buildSession(\n connection,\n sessionId,\n state,\n sessions,\n options.onActivity,\n options.turnIdleTimeoutMs,\n )\n },\n async loadSession(params) {\n // SDK returns `LoadSessionResponse` (no body fields we need); the\n // sessionId is what the caller already provided. We register a\n // fresh `SessionState` so subsequent `prompt` calls have a slot\n // to flush events into — same lifecycle shape as a brand-new\n // session.\n await connection.loadSession({\n sessionId: params.sessionId,\n cwd: params.cwd,\n mcpServers: toAcpMcpServers(params.mcpServers ?? []) as never,\n } as never)\n options.onActivity?.()\n const state: SessionState = {\n events: [],\n resolveNext: null,\n done: false,\n active: false,\n }\n sessions.set(params.sessionId, state)\n return buildSession(\n connection,\n params.sessionId,\n state,\n sessions,\n options.onActivity,\n options.turnIdleTimeoutMs,\n )\n },\n async close() {\n sessions.clear()\n },\n }\n}\n\nfunction clientCapabilitiesFromOptions(\n options: AcpClientOptions,\n): Record<string, unknown> {\n const caps = options.capabilities ?? {}\n return {\n fs: {\n readTextFile: caps.fs?.readTextFile ?? false,\n writeTextFile: caps.fs?.writeTextFile ?? false,\n },\n terminal: caps.terminal ?? false,\n }\n}\n\nfunction buildSession(\n connection: ClientSideConnection,\n sessionId: string,\n state: SessionState,\n sessions: Map<string, SessionState>,\n onActivity?: () => void,\n turnIdleTimeoutMs?: number,\n): AcpClientSession {\n return {\n sessionId,\n prompt(input) {\n if (state.active) {\n throw new Error(\n `AcpClientSession.prompt: session ${sessionId} already has an in-flight prompt`,\n )\n }\n state.active = true\n state.done = false\n state.events.length = 0\n\n const iter = makeIterator(state)\n\n // Turn-idle watchdog — undefined turnIdleTimeoutMs disables it\n // entirely (existing callers see no behavior change). Armed below\n // and re-armed on every activity signal observed during this turn\n // (outbound send, resolve, and incoming session/update via\n // `state.resetWatchdogTimer`); cleared once the turn settles.\n let watchdogTimer: ReturnType<typeof setTimeout> | undefined\n let watchdogFired = false\n const clearWatchdogTimer = () => {\n if (watchdogTimer) clearTimeout(watchdogTimer)\n watchdogTimer = undefined\n }\n const armWatchdogTimer = () => {\n if (turnIdleTimeoutMs === undefined) return\n clearWatchdogTimer()\n watchdogTimer = setTimeout(() => {\n watchdogFired = true\n state.done = true\n enqueue(state, { kind: \"turn-end\", sessionId, reason: \"watchdog-timeout\" })\n }, turnIdleTimeoutMs)\n }\n state.resetWatchdogTimer = armWatchdogTimer\n\n // Outbound send — proves the daemon is still driving this turn,\n // independent of whatever StreamEvents come back. Also the initial\n // arm of the watchdog timer, so the timeout is measured from \"we\n // just sent this\" rather than some earlier idle point.\n armWatchdogTimer()\n onActivity?.()\n const promise = connection\n .prompt({\n sessionId,\n prompt: input.messages as never,\n } as never)\n .then((response) => {\n clearWatchdogTimer()\n onActivity?.()\n if (watchdogFired) {\n console.warn(\n `[acp] session ${sessionId}: connection.prompt() resolved after the ` +\n `turn-idle watchdog already synthesized a turn-end — ignoring late response.`,\n )\n return\n }\n enqueue(state, {\n kind: \"turn-end\",\n sessionId,\n reason:\n ((response as { stopReason?: string }).stopReason ===\n \"cancelled\" && \"cancelled\") ||\n ((response as { stopReason?: string }).stopReason ===\n \"max_turns\" && \"max_turns\") ||\n \"completed\",\n })\n })\n .catch((err: unknown) => {\n clearWatchdogTimer()\n if (watchdogFired) {\n console.warn(\n `[acp] session ${sessionId}: connection.prompt() rejected after the ` +\n `turn-idle watchdog already synthesized a turn-end — ignoring late error:`,\n err instanceof Error ? err.message : err,\n )\n return\n }\n const message =\n err instanceof Error ? err.message : String(err)\n enqueue(state, {\n kind: \"error\",\n sessionId,\n error: { message },\n })\n })\n .finally(() => {\n state.active = false\n state.done = true\n state.resetWatchdogTimer = undefined\n clearWatchdogTimer()\n flush(state)\n })\n\n if (input.signal) {\n input.signal.addEventListener(\n \"abort\",\n () => {\n void connection.cancel({ sessionId } as never)\n },\n { once: true },\n )\n }\n\n void promise\n return iter\n },\n async cancel() {\n if (!state.active) return\n await connection.cancel({ sessionId } as never)\n onActivity?.()\n },\n async close() {\n sessions.delete(sessionId)\n },\n }\n}\n\nfunction makeIterator(state: SessionState): AsyncIterable<StreamEvent> {\n return {\n [Symbol.asyncIterator]() {\n return {\n async next() {\n if (state.events.length > 0) {\n const value = state.events.shift() as StreamEvent\n return { value, done: false }\n }\n if (state.done) return { value: undefined, done: true }\n const value = await new Promise<StreamEvent | undefined>(\n (resolve) => {\n state.resolveNext = resolve\n },\n )\n if (value === undefined) return { value: undefined, done: true }\n return { value, done: false }\n },\n }\n },\n }\n}\n\nfunction enqueue(state: SessionState, event: StreamEvent) {\n if (state.resolveNext) {\n const r = state.resolveNext\n state.resolveNext = null\n r(event)\n return\n }\n state.events.push(event)\n}\n\nfunction flush(state: SessionState) {\n if (state.resolveNext) {\n const r = state.resolveNext\n state.resolveNext = null\n r(undefined)\n }\n}\n\nfunction buildClientHandlers(\n partial: Partial<AcpClientHandlers>,\n sessions: Map<string, SessionState>,\n onActivity?: () => void,\n): AcpClientHandlers {\n return {\n async sessionUpdate(params) {\n // Every notification is a liveness signal, even ones that don't\n // translate into a StreamEvent below (e.g. an in-progress\n // tool_call_update) — this is the gap that leaves lastOutputAt\n // stale during a long internal tool-call chain.\n onActivity?.()\n\n const sid = (params as { sessionId?: string }).sessionId\n if (!sid) return\n const state = sessions.get(sid)\n if (!state) return\n\n // Reset this session's in-flight turn watchdog (if any) — an\n // incoming notification is exactly the \"not silent\" signal the\n // watchdog exists to detect the absence of.\n state.resetWatchdogTimer?.()\n\n const update = (params as { update?: Record<string, unknown> }).update\n if (!update) return\n const event = translateSessionUpdate(sid, update)\n if (event) enqueue(state, event)\n\n if (partial.sessionUpdate) await partial.sessionUpdate(params)\n },\n async requestPermission(params) {\n if (partial.requestPermission) return partial.requestPermission(params)\n throw new Error(\"AcpClient.requestPermission: no handler configured\")\n },\n async readTextFile(params) {\n if (partial.readTextFile) return partial.readTextFile(params)\n throw new Error(\"AcpClient.readTextFile: capability not advertised\")\n },\n async writeTextFile(params) {\n if (partial.writeTextFile) return partial.writeTextFile(params)\n throw new Error(\"AcpClient.writeTextFile: capability not advertised\")\n },\n ...(partial as object),\n } as AcpClientHandlers\n}\n\nfunction translateSessionUpdate(\n sessionId: string,\n update: Record<string, unknown>,\n): StreamEvent | null {\n switch (update.sessionUpdate) {\n case \"agent_message_chunk\":\n return {\n kind: \"text-delta\",\n sessionId,\n text: extractText(update.content),\n }\n case \"agent_thought_chunk\":\n return {\n kind: \"thought\",\n sessionId,\n text: extractText(update.content),\n }\n case \"tool_call\": {\n const toolCallId = (update.toolCallId as string) ?? \"\"\n // `title` is the descriptive label an agent gives a tool call\n // (\"Reading src/foo.ts\") — prefer it over the coarse `kind`\n // bucket (read/edit/search/execute/…) so rendered lines are\n // informative instead of generic.\n const toolName =\n (update.title as string) ?? (update.kind as string) ?? \"tool\"\n const locations = update.locations as\n | Array<{ path: string; line?: number | null }>\n | undefined\n let args: unknown = update.rawInput ?? update.content ?? {}\n // Some tool calls (e.g. a bare \"read\" with no structured input)\n // carry only `locations` — fold the file paths into `arguments`\n // so they survive downstream instead of being dropped.\n if (update.rawInput == null && locations && locations.length > 0) {\n args =\n locations.length === 1\n ? {\n path: locations[0]!.path,\n ...(locations[0]!.line != null\n ? { line: locations[0]!.line }\n : {}),\n }\n : { paths: locations.map((location) => location.path) }\n }\n return {\n kind: \"tool-call\",\n sessionId,\n toolCallId,\n toolName,\n arguments: args,\n }\n }\n case \"tool_call_update\": {\n const status = update.status as string | undefined\n if (status === \"completed\" || status === \"failed\") {\n return {\n kind: \"tool-result\",\n sessionId,\n toolCallId: (update.toolCallId as string) ?? \"\",\n result: update.rawOutput ?? update.content ?? null,\n isError: status === \"failed\",\n }\n }\n return null\n }\n case \"plan\": {\n const entries = (update.entries as Array<Record<string, unknown>>) ?? []\n return {\n kind: \"plan\",\n sessionId,\n entries: entries.map((entry) => ({\n content: (entry.content as string) ?? \"\",\n priority: (entry.priority as \"high\" | \"medium\" | \"low\") ?? \"medium\",\n status:\n (entry.status as \"pending\" | \"in_progress\" | \"completed\") ??\n \"pending\",\n })),\n }\n }\n case \"usage_update\": {\n const cost = update.cost as { amount: number; currency: string } | null | undefined\n // Accept both camelCase and snake_case token fields — agents differ on\n // the convention and this update kind is non-standard ACP.\n const numeric = (...keys: string[]): number | undefined => {\n for (const key of keys) {\n const v = update[key]\n if (typeof v === \"number\") return v\n }\n return undefined\n }\n const tokensIn = numeric(\"tokensIn\", \"input_tokens\", \"inputTokens\")\n const tokensOut = numeric(\"tokensOut\", \"output_tokens\", \"outputTokens\")\n return {\n kind: \"usage_update\",\n sessionId,\n size: (update.size as number) ?? 0,\n used: (update.used as number) ?? 0,\n ...(cost ? { cost } : {}),\n ...(tokensIn !== undefined ? { tokensIn } : {}),\n ...(tokensOut !== undefined ? { tokensOut } : {}),\n }\n }\n case \"user_message_chunk\":\n return null\n default:\n return null\n }\n}\n\n/**\n * Best human-readable reason from a failed `session/set_config_option`.\n *\n * The ACP SDK surfaces a server-side rejection as a JSON-RPC `RequestError`\n * whose generic `message` is just \"Internal error\" (code -32603) — the useful\n * text lives in `error.data.details` (e.g. claude-agent-acp's\n * \"Invalid value for config option model: claude-sonnet-4-6\"). Prefer that\n * detail, falling back to the error's own message, then a stringified form.\n * Narrows `unknown` with `in`/`typeof` guards — no casts.\n */\nfunction configOptionErrorDetail(err: unknown): string {\n if (typeof err === \"object\" && err !== null) {\n if (\"data\" in err && typeof err.data === \"object\" && err.data !== null) {\n const data = err.data\n if (\n \"details\" in data &&\n typeof data.details === \"string\" &&\n data.details.length > 0\n ) {\n return data.details\n }\n }\n if (\n \"message\" in err &&\n typeof err.message === \"string\" &&\n err.message.length > 0\n ) {\n return err.message\n }\n }\n return String(err)\n}\n\nfunction extractText(content: unknown): string {\n if (!content) return \"\"\n if (typeof content === \"string\") return content\n if (typeof content === \"object\" && content !== null) {\n const c = content as { type?: string; text?: string }\n if (c.type === \"text\" && typeof c.text === \"string\") return c.text\n }\n return \"\"\n}\n"]}
1
+ {"version":3,"sources":["../../src/client/index.ts"],"names":["value"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * AIP-44 ACP client — drives a subprocess agent via stdio JSON-RPC.\n *\n * Wraps `@agentclientprotocol/sdk`'s `ClientSideConnection`. Owns the\n * connection lifecycle, runs `initialize` on construction, and exposes\n * a session API where each `prompt()` call returns an\n * `AsyncIterable<StreamEvent>` fed by the SDK's `session/update`\n * notification handler.\n */\n\nimport {\n ClientSideConnection,\n ndJsonStream,\n type Client as AcpClientHandlers,\n type Stream,\n} from \"@agentclientprotocol/sdk\"\nimport type {\n AcpMcpServer,\n AcpPermissionResolution,\n StreamEvent,\n} from \"../types.js\"\n\nexport type { AcpPermissionResolution }\n\nconst PROTOCOL_VERSION_DEFAULT = 1\n\n/**\n * The doubly-nested `RequestPermissionResponse` shape the ACP SDK expects\n * back from a `session/request_permission` handler — see\n * `@agentproto/driver-agent-cli`'s `AcpPermissionOutcome` for the full\n * rationale on why the `outcome` field is nested.\n */\ntype RequestPermissionResponse =\n | { outcome: { outcome: \"selected\"; optionId: string } }\n | { outcome: { outcome: \"cancelled\" } }\n\n/** A `session/request_permission` RPC parked by permission-hold mode. */\ninterface HeldPermission {\n sessionId: string\n resolve: (response: RequestPermissionResponse) => void\n}\n\n/**\n * Map our internal `AcpMcpServer` (`{ name, transport, ref, headers, credentialRef }`)\n * onto the `@agentclientprotocol/sdk` wire shape expected by\n * `session/new.mcpServers` (and `session/load.mcpServers`).\n *\n * The ACP `McpServer` union is transport-tagged and field-named\n * differently from our compact internal form:\n * - http → `{ type: \"http\", name, url, headers: [{ name, value }, …] }`\n * - sse → `{ type: \"sse\", name, url, headers: [{ name, value }, …] }`\n * - stdio → `{ name, command, args: [], env: [] }` (untagged variant)\n *\n * Without this mapping the raw `{ transport, ref }` entry reaches the\n * agent verbatim and `session/new` rejects with `Invalid params` — the\n * symptom that broke orchestrator spawns (the auto-injected scoped\n * gateway is an `http` entry).\n *\n * Retro-compat: an entry that already carries an ACP-native discriminator\n * (`type`/`url`/`command`) is passed through untouched, so callers that\n * pre-shape their entries keep working.\n */\nexport function toAcpMcpServers(servers: readonly unknown[]): unknown[] {\n return servers.map(toAcpMcpServer)\n}\n\nfunction toAcpMcpServer(server: unknown): unknown {\n if (!server || typeof server !== \"object\") return server\n const entry = server as Record<string, unknown>\n\n // Already ACP-native (has a wire discriminator) → leave as-is.\n if (\"type\" in entry || \"url\" in entry || \"command\" in entry) return entry\n\n const transport = entry.transport\n // Not our `{ name, transport, ref }` shape → nothing to map.\n if (typeof transport !== \"string\") return entry\n\n const name = entry.name\n const ref = entry.ref\n const headers = entry.headers\n\n switch (transport) {\n case \"http\":\n return { type: \"http\", name, url: ref ?? \"\", headers: toAcpHeaders(headers) }\n case \"sse\":\n return { type: \"sse\", name, url: ref ?? \"\", headers: toAcpHeaders(headers) }\n case \"stdio\":\n return { name, command: ref ?? \"\", args: [], env: [] }\n default:\n return entry\n }\n}\n\nfunction toAcpHeaders(headers: unknown): Array<{ name: string; value: string }> {\n if (!headers || typeof headers !== \"object\") return []\n return Object.entries(headers).map(([name, value]) => ({\n name,\n value: typeof value === \"string\" ? value : String(value),\n }))\n}\n\nexport interface AcpClientOptions {\n /** Output bytes go here (subprocess stdin). */\n output: WritableStream<Uint8Array>\n /** Input bytes come from here (subprocess stdout). */\n input: ReadableStream<Uint8Array>\n /** Client identity advertised during `initialize`. */\n clientInfo?: {\n name: string\n title?: string\n version?: string\n }\n /** Client capabilities advertised during `initialize`. */\n capabilities?: {\n fs?: { readTextFile?: boolean; writeTextFile?: boolean }\n terminal?: boolean\n }\n /** Optional handlers for agent-initiated requests beyond fs / terminal. */\n handlers?: Partial<AcpClientHandlers>\n /**\n * When true, every `session/request_permission` the agent raises is\n * SURFACED (emitted as an `agent-prompt` StreamEvent on the session's\n * stream) and HELD — the RPC does not resolve until the host calls\n * `respondPermission(requestId, ...)`. `handlers.requestPermission` is\n * bypassed while this is set. Undefined/false (the default) is unchanged:\n * requests go straight to `handlers.requestPermission`. Used by the\n * daemon's cross-session permission inbox.\n */\n permissionHold?: boolean\n /** Protocol version to negotiate. Defaults to the SDK's current. */\n protocolVersion?: number\n /**\n * Called on ANY JSON-RPC traffic with the child agent process — every\n * incoming `session/update` notification (even ones that\n * `translateSessionUpdate` maps to `null`, e.g. a `tool_call_update`\n * still in progress) and every outbound RPC call this client makes\n * (`newSession`, `loadSession`, `setSessionConfigOption`, `prompt`,\n * `cancel`). Distinct from the `StreamEvent` stream: fires even\n * during gaps where no event is produced, so a host can track\n * liveness independent of what the agent actually says.\n */\n onActivity?: () => void\n /**\n * Turn-idle watchdog: if a `prompt()` call goes this many ms with NO\n * activity signal (see `onActivity` above — incoming `session/update`\n * notifications, outbound RPCs) and the underlying `connection.prompt()`\n * promise still hasn't resolved, synthesize a `turn-end` event with\n * `reason: \"watchdog-timeout\"` so the caller's async iterator completes\n * instead of hanging forever. The timer resets on every activity signal\n * observed DURING that turn, so a legitimately long tool-call chain\n * (still producing activity, just no user-visible output) never\n * false-positives — this is \"N ms of true silence,\" not \"N ms since the\n * turn started.\"\n *\n * Undefined (the default) disables the watchdog entirely — existing\n * callers see no behavior change. If the real `connection.prompt()`\n * eventually settles after the synthetic turn-end was already emitted,\n * the late result is logged and discarded (no crash, no duplicate\n * turn-end for the same logical turn).\n */\n turnIdleTimeoutMs?: number\n}\n\nexport interface AcpClient {\n readonly connection: ClientSideConnection\n /** Negotiated agent capabilities returned from `initialize`. */\n readonly agentCapabilities: Record<string, unknown> | undefined\n newSession(params: {\n cwd: string\n mcpServers?: unknown[]\n /**\n * Model to select via `session/set_config_option` immediately after\n * `newSession`. The claude-agent-acp wrapper supports this as\n * `configId:\"model\"`. Omit to keep the agent's own default.\n *\n * Best-effort: a value the agent can't resolve (a stale/gated/typo'd id)\n * is warned about and dropped — the session still starts on the agent's\n * default model rather than crashing the spawn. See the reject handler\n * in `newSession` for the reasoning (agentproto#186).\n */\n model?: string\n /**\n * Effort level to select via `session/set_config_option` after\n * `newSession`. Effort is model-dependent — same label ≠ same\n * budget across models; defaults differ too (see AgentCliConnectOptions).\n * Omit to keep the model's own default.\n */\n effort?: string\n /**\n * Mode to select via `session/set_config_option` after `newSession`,\n * for ACP servers that model mode selection as a session config\n * option (`configId:\"mode\"`) instead of a CLI flag — e.g. opencode.\n * Best-effort, same as `model`/`effort`: a rejection is warned about\n * and dropped rather than failing the spawn. Omit to keep the\n * agent's own default mode.\n */\n mode?: string\n }): Promise<AcpClientSession>\n /**\n * Reattach to an existing session by id. Available only when the\n * agent advertised `loadSession: true` in its capabilities (callers\n * should check `agentCapabilities` first; we forward the SDK error\n * verbatim if they don't).\n *\n * Drives the AIP-45 native-resume continuation strategy: the host\n * persists the `sessionId` returned by `newSession`, then on a\n * cold start (process restart, fresh sandbox, different machine)\n * spawns a new subprocess and calls `loadSession` to pick up\n * the conversation from the stored history.\n */\n loadSession(params: {\n sessionId: string\n cwd: string\n mcpServers?: unknown[]\n }): Promise<AcpClientSession>\n /**\n * Resolve a permission request parked by permission-hold mode. `requestId`\n * is the `toolCallId` carried on the `agent-prompt` StreamEvent that\n * surfaced the request. Returns true when a matching parked request was\n * found and resolved, false when the id is unknown or already resolved\n * (idempotent — a duplicate response is a no-op). No-op returning false\n * when `permissionHold` was never enabled.\n */\n respondPermission(\n requestId: string,\n resolution: AcpPermissionResolution,\n ): boolean\n close(): Promise<void>\n}\n\nexport interface AcpClientSession {\n readonly sessionId: string\n prompt(input: {\n messages: unknown[]\n signal?: AbortSignal\n }): AsyncIterable<StreamEvent>\n cancel(): Promise<void>\n close(): Promise<void>\n}\n\ninterface SessionState {\n events: StreamEvent[]\n resolveNext: ((evt: StreamEvent | undefined) => void) | null\n done: boolean\n active: boolean\n /**\n * Set by `buildSession.prompt()` for the duration of an in-flight turn\n * when a watchdog timer is armed; cleared once that turn settles. Lets\n * the `sessionUpdate` handler (which only has a `sessionId`, not a\n * closure over the turn's timer) bump the SAME timer an incoming\n * notification is liveness evidence for.\n */\n resetWatchdogTimer?: () => void\n}\n\nexport async function createAcpClient(\n options: AcpClientOptions,\n): Promise<AcpClient> {\n const sessions = new Map<string, SessionState>()\n\n // Permission-hold state: parked `session/request_permission` RPCs keyed by\n // the stable request id we mint per request (and emit on the `agent-prompt`\n // StreamEvent). Empty and untouched unless `permissionHold` is set.\n const pendingPermissions = new Map<string, HeldPermission>()\n let permissionSeq = 0\n\n const respondPermission = (\n requestId: string,\n resolution: AcpPermissionResolution,\n ): boolean => {\n const held = pendingPermissions.get(requestId)\n if (!held) return false\n pendingPermissions.delete(requestId)\n held.resolve(\n \"cancelled\" in resolution\n ? { outcome: { outcome: \"cancelled\" } }\n : { outcome: { outcome: \"selected\", optionId: resolution.optionId } },\n )\n return true\n }\n\n // Resolve every request parked for a session as `cancelled` — used when a\n // session (or the whole client) tears down so no held RPC dangles.\n const cancelPermissionsForSession = (sessionId: string): void => {\n // Collect the matching keys first, THEN delete + resolve — never mutate the\n // map while iterating its entries.\n const ids: string[] = []\n for (const [id, held] of pendingPermissions) {\n if (held.sessionId === sessionId) ids.push(id)\n }\n for (const id of ids) {\n const held = pendingPermissions.get(id)\n if (!held) continue\n pendingPermissions.delete(id)\n held.resolve({ outcome: { outcome: \"cancelled\" } })\n }\n }\n\n const stream: Stream = ndJsonStream(options.output, options.input)\n\n const connection: ClientSideConnection = new ClientSideConnection(\n () =>\n buildClientHandlers(options.handlers ?? {}, sessions, options.onActivity, {\n permissionHold: options.permissionHold ?? false,\n pendingPermissions,\n nextRequestId: () => `perm_${++permissionSeq}`,\n }),\n stream,\n )\n\n const initResponse = await connection.initialize({\n protocolVersion: options.protocolVersion ?? PROTOCOL_VERSION_DEFAULT,\n clientCapabilities: clientCapabilitiesFromOptions(options),\n clientInfo: options.clientInfo\n ? {\n name: options.clientInfo.name,\n title: options.clientInfo.title ?? options.clientInfo.name,\n version: options.clientInfo.version ?? \"0.0.0\",\n }\n : undefined,\n } as never)\n\n return {\n connection,\n agentCapabilities: (initResponse as { agentCapabilities?: Record<string, unknown> })\n .agentCapabilities,\n async newSession(params) {\n const response = await connection.newSession({\n cwd: params.cwd,\n mcpServers: toAcpMcpServers(params.mcpServers ?? []) as never,\n } as never)\n options.onActivity?.()\n const sessionId = (response as { sessionId: string }).sessionId\n const state: SessionState = {\n events: [],\n resolveNext: null,\n done: false,\n active: false,\n }\n sessions.set(sessionId, state)\n // Apply model + effort via session/set_config_option immediately\n // after newSession. The claude-agent-acp wrapper handles these as\n // configId:\"model\" and configId:\"effort\". Both are optional — when\n // omitted the agent keeps its own defaults (which vary by model).\n // We call these sequentially so a model switch (which rebuilds the\n // effort options) always precedes the effort set.\n if (params.model) {\n try {\n await connection.setSessionConfigOption({\n configId: \"model\",\n value: params.model,\n sessionId,\n } as never)\n options.onActivity?.()\n } catch (err) {\n // Non-fatal by design. claude-agent-acp validates the requested\n // model against the concrete option ids it advertises for THIS\n // session (e.g. \"default\", \"opus[1m]\", \"sonnet\", \"claude-sonnet-5\",\n // \"haiku\") and rejects anything it can't resolve — including stale\n // or gated ids like \"claude-sonnet-4-6\" — with a JSON-RPC -32603\n // whose only useful text is buried in `error.data.details`\n // (\"Invalid value for config option model: <id>\"). Letting that\n // reject `newSession` turned a bad model id into an opaque\n // \"spawn failed — Internal error\" that killed the whole session\n // (agentproto#186). The accepted vocabulary is dynamic (it varies\n // by wrapper version and account entitlements), so there is no\n // version-stable way to know it before the call — a rejected model\n // must therefore never be fatal. This is NOT a silent drop: the\n // requested id AND the server's own reason are logged, and the\n // session continues on the agent's default model.\n console.warn(\n `[acp] set_config_option model=\"${params.model}\" rejected by server ` +\n `— keeping the agent's default model. Reason: ${configOptionErrorDetail(err)}`,\n )\n }\n }\n if (params.effort) {\n try {\n await connection.setSessionConfigOption({\n configId: \"effort\",\n value: params.effort,\n sessionId,\n } as never)\n options.onActivity?.()\n } catch (err) {\n // Best-effort: this ACP server does not support the \"effort\" config\n // option (e.g. claude-agent-acp ignores unknown config keys and\n // some versions reject them outright). A rejected set_config_option\n // must never kill the spawn — effort is silently ignored instead.\n console.warn(\n `[acp] set_config_option effort=\"${params.effort}\" rejected by server ` +\n `(best-effort): ${configOptionErrorDetail(err)}`,\n )\n }\n }\n if (params.mode) {\n try {\n await connection.setSessionConfigOption({\n configId: \"mode\",\n value: params.mode,\n sessionId,\n } as never)\n options.onActivity?.()\n } catch (err) {\n // Best-effort: this ACP server does not support the \"mode\" config\n // option, or doesn't recognize the requested mode id. A rejected\n // set_config_option must never kill the spawn — mode is silently\n // ignored (the session keeps whatever mode the agent defaults to).\n console.warn(\n `[acp] set_config_option mode=\"${params.mode}\" rejected by server ` +\n `(best-effort): ${configOptionErrorDetail(err)}`,\n )\n }\n }\n return buildSession(\n connection,\n sessionId,\n state,\n sessions,\n options.onActivity,\n options.turnIdleTimeoutMs,\n cancelPermissionsForSession,\n )\n },\n async loadSession(params) {\n // SDK returns `LoadSessionResponse` (no body fields we need); the\n // sessionId is what the caller already provided. We register a\n // fresh `SessionState` so subsequent `prompt` calls have a slot\n // to flush events into — same lifecycle shape as a brand-new\n // session.\n await connection.loadSession({\n sessionId: params.sessionId,\n cwd: params.cwd,\n mcpServers: toAcpMcpServers(params.mcpServers ?? []) as never,\n } as never)\n options.onActivity?.()\n const state: SessionState = {\n events: [],\n resolveNext: null,\n done: false,\n active: false,\n }\n sessions.set(params.sessionId, state)\n return buildSession(\n connection,\n params.sessionId,\n state,\n sessions,\n options.onActivity,\n options.turnIdleTimeoutMs,\n cancelPermissionsForSession,\n )\n },\n respondPermission,\n async close() {\n // Never leave a held permission RPC dangling — resolve all as cancelled.\n for (const [, held] of pendingPermissions) {\n held.resolve({ outcome: { outcome: \"cancelled\" } })\n }\n pendingPermissions.clear()\n sessions.clear()\n },\n }\n}\n\nfunction clientCapabilitiesFromOptions(\n options: AcpClientOptions,\n): Record<string, unknown> {\n const caps = options.capabilities ?? {}\n return {\n fs: {\n readTextFile: caps.fs?.readTextFile ?? false,\n writeTextFile: caps.fs?.writeTextFile ?? false,\n },\n terminal: caps.terminal ?? false,\n }\n}\n\nfunction buildSession(\n connection: ClientSideConnection,\n sessionId: string,\n state: SessionState,\n sessions: Map<string, SessionState>,\n onActivity: (() => void) | undefined,\n turnIdleTimeoutMs: number | undefined,\n cancelPermissionsForSession: (sessionId: string) => void,\n): AcpClientSession {\n return {\n sessionId,\n prompt(input) {\n if (state.active) {\n throw new Error(\n `AcpClientSession.prompt: session ${sessionId} already has an in-flight prompt`,\n )\n }\n state.active = true\n state.done = false\n state.events.length = 0\n\n const iter = makeIterator(state)\n\n // Turn-idle watchdog — undefined turnIdleTimeoutMs disables it\n // entirely (existing callers see no behavior change). Armed below\n // and re-armed on every activity signal observed during this turn\n // (outbound send, resolve, and incoming session/update via\n // `state.resetWatchdogTimer`); cleared once the turn settles.\n let watchdogTimer: ReturnType<typeof setTimeout> | undefined\n let watchdogFired = false\n const clearWatchdogTimer = () => {\n if (watchdogTimer) clearTimeout(watchdogTimer)\n watchdogTimer = undefined\n }\n const armWatchdogTimer = () => {\n if (turnIdleTimeoutMs === undefined) return\n clearWatchdogTimer()\n watchdogTimer = setTimeout(() => {\n watchdogFired = true\n state.done = true\n enqueue(state, { kind: \"turn-end\", sessionId, reason: \"watchdog-timeout\" })\n }, turnIdleTimeoutMs)\n }\n state.resetWatchdogTimer = armWatchdogTimer\n\n // Outbound send — proves the daemon is still driving this turn,\n // independent of whatever StreamEvents come back. Also the initial\n // arm of the watchdog timer, so the timeout is measured from \"we\n // just sent this\" rather than some earlier idle point.\n armWatchdogTimer()\n onActivity?.()\n const promise = connection\n .prompt({\n sessionId,\n prompt: input.messages as never,\n } as never)\n .then((response) => {\n clearWatchdogTimer()\n onActivity?.()\n if (watchdogFired) {\n console.warn(\n `[acp] session ${sessionId}: connection.prompt() resolved after the ` +\n `turn-idle watchdog already synthesized a turn-end — ignoring late response.`,\n )\n return\n }\n enqueue(state, {\n kind: \"turn-end\",\n sessionId,\n reason:\n ((response as { stopReason?: string }).stopReason ===\n \"cancelled\" && \"cancelled\") ||\n ((response as { stopReason?: string }).stopReason ===\n \"max_turns\" && \"max_turns\") ||\n \"completed\",\n })\n })\n .catch((err: unknown) => {\n clearWatchdogTimer()\n if (watchdogFired) {\n console.warn(\n `[acp] session ${sessionId}: connection.prompt() rejected after the ` +\n `turn-idle watchdog already synthesized a turn-end — ignoring late error:`,\n err instanceof Error ? err.message : err,\n )\n return\n }\n const message =\n err instanceof Error ? err.message : String(err)\n enqueue(state, {\n kind: \"error\",\n sessionId,\n error: { message },\n })\n })\n .finally(() => {\n state.active = false\n state.done = true\n state.resetWatchdogTimer = undefined\n clearWatchdogTimer()\n flush(state)\n })\n\n if (input.signal) {\n input.signal.addEventListener(\n \"abort\",\n () => {\n void connection.cancel({ sessionId } as never)\n },\n { once: true },\n )\n }\n\n void promise\n return iter\n },\n async cancel() {\n if (!state.active) return\n await connection.cancel({ sessionId } as never)\n onActivity?.()\n },\n async close() {\n // Cancel any permission requests this session parked before dropping it,\n // so the agent's held RPCs settle rather than hang.\n cancelPermissionsForSession(sessionId)\n sessions.delete(sessionId)\n },\n }\n}\n\nfunction makeIterator(state: SessionState): AsyncIterable<StreamEvent> {\n return {\n [Symbol.asyncIterator]() {\n return {\n async next() {\n if (state.events.length > 0) {\n const value = state.events.shift() as StreamEvent\n return { value, done: false }\n }\n if (state.done) return { value: undefined, done: true }\n const value = await new Promise<StreamEvent | undefined>(\n (resolve) => {\n state.resolveNext = resolve\n },\n )\n if (value === undefined) return { value: undefined, done: true }\n return { value, done: false }\n },\n }\n },\n }\n}\n\nfunction enqueue(state: SessionState, event: StreamEvent) {\n if (state.resolveNext) {\n const r = state.resolveNext\n state.resolveNext = null\n r(event)\n return\n }\n state.events.push(event)\n}\n\nfunction flush(state: SessionState) {\n if (state.resolveNext) {\n const r = state.resolveNext\n state.resolveNext = null\n r(undefined)\n }\n}\n\ninterface PermissionHoldContext {\n permissionHold: boolean\n pendingPermissions: Map<string, HeldPermission>\n nextRequestId: () => string\n}\n\n/**\n * Shape of an ACP `session/request_permission` params object — only the\n * fields we read. Narrowed defensively (all optional) since it arrives as\n * `unknown` from the SDK handler boundary.\n */\ninterface RequestPermissionParams {\n sessionId?: string\n toolCall?: {\n toolCallId?: string\n title?: string\n kind?: string\n }\n options?: Array<{ optionId?: string; name?: string; kind?: string }>\n}\n\n/**\n * Permission-hold path: surface the request as an `agent-prompt` StreamEvent\n * on the owning session's stream and park the RPC in `pendingPermissions`,\n * returning a promise that resolves only when the host calls\n * `respondPermission` (or the session/client tears down). The `agent-prompt`\n * event carries the minted request id as `toolCallId` so the host has the\n * exact key to respond with.\n */\nfunction holdPermissionRequest(\n rawParams: unknown,\n sessions: Map<string, SessionState>,\n ctx: PermissionHoldContext,\n): Promise<RequestPermissionResponse> {\n const params = (rawParams ?? {}) as RequestPermissionParams\n const sessionId = params.sessionId ?? \"\"\n const requestId = ctx.nextRequestId()\n const options = Array.isArray(params.options)\n ? params.options\n .filter((o): o is { optionId: string; name?: string; kind?: string } =>\n typeof o?.optionId === \"string\",\n )\n .map(o => ({\n optionId: o.optionId,\n ...(typeof o.name === \"string\" ? { name: o.name } : {}),\n ...(typeof o.kind === \"string\" ? { kind: o.kind } : {}),\n }))\n : []\n const toolName = params.toolCall?.title ?? params.toolCall?.kind\n const text = toolName\n ? `Allow \"${toolName}\"?`\n : \"The agent is requesting permission to run a tool.\"\n\n const state = sessions.get(sessionId)\n if (state) {\n enqueue(state, {\n kind: \"agent-prompt\",\n sessionId,\n toolCallId: requestId,\n options,\n text,\n ...(toolName ? { toolName } : {}),\n })\n } else {\n // No session slot for this id — the agent-prompt event can't be surfaced,\n // so the host won't see the request in its stream. We still park the RPC\n // below (so the agent doesn't hang), but log it: a missing state here means\n // the request arrived for an unknown/torn-down session and would otherwise\n // vanish silently.\n console.warn(\n `[acp] permission-hold: no session state for sessionId=\"${sessionId}\" ` +\n `(request ${requestId}) — parking the RPC but not surfacing an agent-prompt event.`,\n )\n }\n\n return new Promise<RequestPermissionResponse>(resolve => {\n ctx.pendingPermissions.set(requestId, { sessionId, resolve })\n })\n}\n\nfunction buildClientHandlers(\n partial: Partial<AcpClientHandlers>,\n sessions: Map<string, SessionState>,\n onActivity: (() => void) | undefined,\n hold: PermissionHoldContext,\n): AcpClientHandlers {\n return {\n // Spread the caller-supplied handlers FIRST so the named methods defined\n // below always win. This matters for `requestPermission`: the arm always\n // passes a `requestPermission` handler, and if that spread came last it\n // would clobber the permission-hold guard below — silently bypassing hold\n // mode. With the spread first, our guard stays in control and still falls\n // through to `partial.requestPermission` when hold mode is OFF.\n ...(partial as object),\n async sessionUpdate(params) {\n // Every notification is a liveness signal, even ones that don't\n // translate into a StreamEvent below (e.g. an in-progress\n // tool_call_update) — this is the gap that leaves lastOutputAt\n // stale during a long internal tool-call chain.\n onActivity?.()\n\n const sid = (params as { sessionId?: string }).sessionId\n if (!sid) return\n const state = sessions.get(sid)\n if (!state) return\n\n // Reset this session's in-flight turn watchdog (if any) — an\n // incoming notification is exactly the \"not silent\" signal the\n // watchdog exists to detect the absence of.\n state.resetWatchdogTimer?.()\n\n const update = (params as { update?: Record<string, unknown> }).update\n if (!update) return\n const event = translateSessionUpdate(sid, update)\n if (event) enqueue(state, event)\n\n if (partial.sessionUpdate) await partial.sessionUpdate(params)\n },\n async requestPermission(params) {\n // Every incoming request is a liveness signal.\n onActivity?.()\n // Permission-hold mode intercepts BEFORE any auto-answer handler: the\n // request is surfaced + parked instead of resolved in-arm.\n if (hold.permissionHold) {\n return holdPermissionRequest(params, sessions, hold) as never\n }\n if (partial.requestPermission) return partial.requestPermission(params)\n throw new Error(\"AcpClient.requestPermission: no handler configured\")\n },\n async readTextFile(params) {\n if (partial.readTextFile) return partial.readTextFile(params)\n throw new Error(\"AcpClient.readTextFile: capability not advertised\")\n },\n async writeTextFile(params) {\n if (partial.writeTextFile) return partial.writeTextFile(params)\n throw new Error(\"AcpClient.writeTextFile: capability not advertised\")\n },\n } as AcpClientHandlers\n}\n\n/**\n * Nothing worth calling a tool's input: absent, `{}`, or `[]`. All three are\n * what an agent sends when it hasn't decided the input yet — the claude-code\n * bridge announces a read with `rawInput: {}` and `content: []` before it\n * knows the path.\n */\nfunction isEmptyish(value: unknown): boolean {\n if (value == null) return true\n if (Array.isArray(value)) return value.length === 0\n if (typeof value === \"object\") return Object.keys(value as object).length === 0\n return false\n}\n\n/**\n * Derive a tool call's `arguments` from an ACP tool_call / tool_call_update\n * payload, or undefined when the payload says nothing about the input.\n *\n * Preference order: `rawInput` (the real thing) → the file paths in\n * `locations` (some agents describe a read only that way) → `content`. An\n * EMPTY `rawInput` counts as \"nothing\", not as \"the input is {}\" — otherwise\n * the placeholder the agent sends before it knows the input would outrank the\n * locations it did send, and would later look indistinguishable from a real\n * no-argument call.\n */\nfunction toolCallArguments(update: Record<string, unknown>): unknown | undefined {\n const rawInput = update.rawInput\n if (!isEmptyish(rawInput)) return rawInput\n\n const locations = update.locations as\n | Array<{ path: string; line?: number | null }>\n | undefined\n // Some tool calls (e.g. a bare \"read\" with no structured input) carry only\n // `locations` — fold the file paths into `arguments` so they survive\n // downstream instead of being dropped. Reached whenever rawInput said\n // nothing, including the `{}` placeholder: a real path beats a placeholder.\n if (locations && locations.length > 0) {\n const first = locations[0]!\n return locations.length === 1\n ? { path: first.path, ...(first.line != null ? { line: first.line } : {}) }\n : { paths: locations.map(location => location.path) }\n }\n\n if (!isEmptyish(update.content)) return update.content\n // Distinguish \"the agent sent an empty input\" from \"the agent said nothing\n // about the input\". The announcement keeps rendering the former as-is; an\n // enrichment carrying the latter isn't worth emitting at all.\n return rawInput != null ? rawInput : undefined\n}\n\n/**\n * Build the tool-call ENRICHMENT for a non-terminal `tool_call_update` — the\n * frame where the claude-code bridge delivers the input and the real title\n * for a call it already announced as a bare \"Read File\" with `rawInput: {}`.\n *\n * Returns null when the update adds nothing (e.g. a bare `{_meta, toolCallId}`\n * keep-alive), so a no-op frame stays a no-op event.\n */\nfunction toolCallEnrichment(\n sessionId: string,\n update: Record<string, unknown>,\n): StreamEvent | null {\n const toolCallId = update.toolCallId as string | undefined\n if (!toolCallId) return null\n const args = toolCallArguments(update)\n const title = typeof update.title === \"string\" ? update.title : undefined\n if (args === undefined && title === undefined) return null\n return {\n kind: \"tool-call\",\n sessionId,\n toolCallId,\n // \"\" when the update carried no title — consumers merge only non-empty\n // names, so an untitled enrichment can't erase the announced one.\n toolName: title ?? \"\",\n arguments: args,\n isUpdate: true,\n }\n}\n\nfunction translateSessionUpdate(\n sessionId: string,\n update: Record<string, unknown>,\n): StreamEvent | null {\n switch (update.sessionUpdate) {\n case \"agent_message_chunk\":\n return {\n kind: \"text-delta\",\n sessionId,\n text: extractText(update.content),\n }\n case \"agent_thought_chunk\":\n return {\n kind: \"thought\",\n sessionId,\n text: extractText(update.content),\n }\n case \"tool_call\": {\n const toolCallId = (update.toolCallId as string) ?? \"\"\n // `title` is the descriptive label an agent gives a tool call\n // (\"Reading src/foo.ts\") — prefer it over the coarse `kind`\n // bucket (read/edit/search/execute/…) so rendered lines are\n // informative instead of generic.\n const toolName =\n (update.title as string) ?? (update.kind as string) ?? \"tool\"\n return {\n kind: \"tool-call\",\n sessionId,\n toolCallId,\n toolName,\n arguments: toolCallArguments(update) ?? {},\n }\n }\n case \"tool_call_update\": {\n const status = update.status as string | undefined\n if (status === \"completed\" || status === \"failed\") {\n return {\n kind: \"tool-result\",\n sessionId,\n toolCallId: (update.toolCallId as string) ?? \"\",\n result: update.rawOutput ?? update.content ?? null,\n isError: status === \"failed\",\n }\n }\n // A non-terminal update is where several agents actually DELIVER the\n // call's input. Captured off the wire from the claude-code bridge:\n //\n // tool_call title=\"Read File\" rawInput={} locations=[]\n // tool_call_update title=\"Read /tmp/probe.txt\"\n // rawInput={\"file_path\":\"/tmp/probe.txt\"}\n // locations=[{path:\"/tmp/probe.txt\",line:1}]\n // tool_call_update status=completed rawOutput=…\n //\n // The middle frame has no `status`, so returning null here discarded the\n // arguments AND the real title of ~95% of that adapter's tool calls —\n // which is why they rendered as \"Read File\" with an empty input while\n // adapters that inline their input on the initial call looked fine.\n return toolCallEnrichment(sessionId, update)\n }\n case \"plan\": {\n const entries = (update.entries as Array<Record<string, unknown>>) ?? []\n return {\n kind: \"plan\",\n sessionId,\n entries: entries.map((entry) => ({\n content: (entry.content as string) ?? \"\",\n priority: (entry.priority as \"high\" | \"medium\" | \"low\") ?? \"medium\",\n status:\n (entry.status as \"pending\" | \"in_progress\" | \"completed\") ??\n \"pending\",\n })),\n }\n }\n case \"usage_update\": {\n const cost = update.cost as { amount: number; currency: string } | null | undefined\n // Accept both camelCase and snake_case token fields — agents differ on\n // the convention and this update kind is non-standard ACP.\n const numeric = (...keys: string[]): number | undefined => {\n for (const key of keys) {\n const v = update[key]\n if (typeof v === \"number\") return v\n }\n return undefined\n }\n const tokensIn = numeric(\"tokensIn\", \"input_tokens\", \"inputTokens\")\n const tokensOut = numeric(\"tokensOut\", \"output_tokens\", \"outputTokens\")\n return {\n kind: \"usage_update\",\n sessionId,\n size: (update.size as number) ?? 0,\n used: (update.used as number) ?? 0,\n ...(cost ? { cost } : {}),\n ...(tokensIn !== undefined ? { tokensIn } : {}),\n ...(tokensOut !== undefined ? { tokensOut } : {}),\n }\n }\n case \"user_message_chunk\":\n return null\n default:\n return null\n }\n}\n\n/**\n * Best human-readable reason from a failed `session/set_config_option`.\n *\n * The ACP SDK surfaces a server-side rejection as a JSON-RPC `RequestError`\n * whose generic `message` is just \"Internal error\" (code -32603) — the useful\n * text lives in `error.data.details` (e.g. claude-agent-acp's\n * \"Invalid value for config option model: claude-sonnet-4-6\"). Prefer that\n * detail, falling back to the error's own message, then a stringified form.\n * Narrows `unknown` with `in`/`typeof` guards — no casts.\n */\nfunction configOptionErrorDetail(err: unknown): string {\n if (typeof err === \"object\" && err !== null) {\n if (\"data\" in err && typeof err.data === \"object\" && err.data !== null) {\n const data = err.data\n if (\n \"details\" in data &&\n typeof data.details === \"string\" &&\n data.details.length > 0\n ) {\n return data.details\n }\n }\n if (\n \"message\" in err &&\n typeof err.message === \"string\" &&\n err.message.length > 0\n ) {\n return err.message\n }\n }\n return String(err)\n}\n\nfunction extractText(content: unknown): string {\n if (!content) return \"\"\n if (typeof content === \"string\") return content\n if (typeof content === \"object\" && content !== null) {\n const c = content as { type?: string; text?: string }\n if (c.type === \"text\" && typeof c.text === \"string\") return c.text\n }\n return \"\"\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AcpDefinition } from './types-Sxd1kKk2.js';
2
- export { a as AcpAuditConfig, b as AcpCapabilities, c as AcpHandle, d as AcpMcpServer, e as AcpRole, f as AcpTier, g as AcpTransport, h as Aip44Extensions, S as StreamEvent } from './types-Sxd1kKk2.js';
1
+ import { A as AcpDefinition } from './types-DQCJO97x.js';
2
+ export { a as AcpAuditConfig, b as AcpCapabilities, c as AcpHandle, d as AcpMcpServer, e as AcpPermissionResolution, f as AcpRole, g as AcpTier, h as AcpTransport, i as Aip44Extensions, S as StreamEvent } from './types-DQCJO97x.js';
3
3
  export { A as AcpFrontmatter, a as Aip44ExtensionsSchema, b as acpFrontmatterSchema } from './schema-CxWMGVy0.js';
4
4
  import 'zod';
5
5
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;AAgBO,IAAM,SAAA,GAAY;AAClB,IAAM,YAAA,GAAe","file":"index.mjs","sourcesContent":["/**\n * @agentproto/acp — AIP-44 ACP.md `defineAcp` reference impl + thin\n * wrappers around the upstream Agent Client Protocol SDK.\n *\n * Spec: https://agentproto.sh/docs/aip-44\n * Upstream protocol: https://agentclientprotocol.com/\n *\n * Authoring paths:\n * - TS: `defineAcp({...})` → `AcpHandle`\n * - MD: `parseAcpManifest(src) → acpFromManifest({...})` → `AcpHandle`\n *\n * Runtime paths:\n * - Drive a subprocess agent: `createAcpClient({ output, input, ... })`\n * - Expose an operator as a server: `createAcpServer({ handle, ... })`\n */\n\nexport const SPEC_NAME = \"agentacp/v1\" as const\nexport const SPEC_VERSION = \"0.1.0-alpha\" as const\n\nexport { defineAcp } from \"./define-acp.js\"\nexport {\n acpFrontmatterSchema,\n type AcpFrontmatter,\n type Aip44Extensions as Aip44ExtensionsSchema,\n} from \"./schema.js\"\nexport type {\n AcpDefinition,\n AcpHandle,\n AcpRole,\n AcpTransport,\n AcpTier,\n AcpCapabilities,\n AcpAuditConfig,\n AcpMcpServer,\n Aip44Extensions,\n StreamEvent,\n} from \"./types.js\"\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;AAgBO,IAAM,SAAA,GAAY;AAClB,IAAM,YAAA,GAAe","file":"index.mjs","sourcesContent":["/**\n * @agentproto/acp — AIP-44 ACP.md `defineAcp` reference impl + thin\n * wrappers around the upstream Agent Client Protocol SDK.\n *\n * Spec: https://agentproto.sh/docs/aip-44\n * Upstream protocol: https://agentclientprotocol.com/\n *\n * Authoring paths:\n * - TS: `defineAcp({...})` → `AcpHandle`\n * - MD: `parseAcpManifest(src) → acpFromManifest({...})` → `AcpHandle`\n *\n * Runtime paths:\n * - Drive a subprocess agent: `createAcpClient({ output, input, ... })`\n * - Expose an operator as a server: `createAcpServer({ handle, ... })`\n */\n\nexport const SPEC_NAME = \"agentacp/v1\" as const\nexport const SPEC_VERSION = \"0.1.0-alpha\" as const\n\nexport { defineAcp } from \"./define-acp.js\"\nexport {\n acpFrontmatterSchema,\n type AcpFrontmatter,\n type Aip44Extensions as Aip44ExtensionsSchema,\n} from \"./schema.js\"\nexport type {\n AcpDefinition,\n AcpHandle,\n AcpRole,\n AcpTransport,\n AcpTier,\n AcpCapabilities,\n AcpAuditConfig,\n AcpMcpServer,\n AcpPermissionResolution,\n Aip44Extensions,\n StreamEvent,\n} from \"./types.js\"\n"]}
@@ -1,6 +1,6 @@
1
1
  import { A as AcpFrontmatter } from '../schema-CxWMGVy0.js';
2
2
  export { b as acpFrontmatterSchema } from '../schema-CxWMGVy0.js';
3
- import { c as AcpHandle } from '../types-Sxd1kKk2.js';
3
+ import { c as AcpHandle } from '../types-DQCJO97x.js';
4
4
  import 'zod';
5
5
 
6
6
  /**
@@ -1,5 +1,5 @@
1
1
  import { AgentSideConnection } from '@agentclientprotocol/sdk';
2
- import { c as AcpHandle } from '../types-Sxd1kKk2.js';
2
+ import { c as AcpHandle } from '../types-DQCJO97x.js';
3
3
 
4
4
  /**
5
5
  * AIP-44 ACP server — exposes an AIP-9 operator to ACP-speaking clients.
@@ -223,6 +223,18 @@ interface HelloFrame {
223
223
  * daemon's live `tools/list` over the HTTP relay.
224
224
  */
225
225
  tools?: ReadonlyArray<string>;
226
+ /**
227
+ * Set when this daemon's tunnel channel is E2E-encrypted (design:
228
+ * tunnel-e2e/v1). Informational confirmation only — this `hello` is
229
+ * itself carried INSIDE the already-established encrypted channel (the
230
+ * `wrapE2E` box), so a host that decrypts it has, by definition, already
231
+ * completed the token-authenticated handshake. The negotiation that turns
232
+ * a channel encrypted happens BEFORE this frame, over `e2e_handshake`
233
+ * frames on the raw sink — not by advertising a capability here. A host
234
+ * may surface this flag in its UI ("this daemon is end-to-end encrypted").
235
+ * Absent ⇒ plaintext tunnel (today's behaviour). See ./tunnel-e2e.ts.
236
+ */
237
+ e2e?: boolean;
226
238
  }>;
227
239
  /** User-friendly daemon label, surfaced in host UIs. */
228
240
  label?: string;
@@ -374,8 +386,32 @@ interface WsCloseFrame {
374
386
  code: number;
375
387
  reason?: string;
376
388
  }
377
- type HostToDaemonFrame = SpawnFrame | StdinFrame | KillFrame | ResizeFrame | HttpRequestFrame | WsOpenFrame | WsMessageFrame | WsCloseFrame | PingFrame | PongFrame | ErrorFrame | ReconnectSoonFrame;
378
- type DaemonToHostFrame = HelloFrame | SpawnedFrame | StdoutFrame | StderrFrame | ExitFrame | HttpResponseFrame | HttpResponseHeadFrame | HttpResponseChunkFrame | WsOpenAckFrame | WsMessageFrame | WsCloseFrame | PingFrame | PongFrame | ErrorFrame;
389
+ /**
390
+ * Either direction. One AEAD-encrypted inner tunnel frame. `n` is the
391
+ * sender's per-direction, strictly-monotonic frame counter — it is the basis
392
+ * of the GCM nonce and lets the receiver detect replay (a repeated/older `n`),
393
+ * reorder, or drops (a gap in `n`). `d` is base64(ciphertext ‖ 16-byte GCM
394
+ * tag). The plaintext, once decrypted, is itself an `encodeFrame`'d
395
+ * `TunnelFrame`.
396
+ */
397
+ interface E2eFrame {
398
+ t: "e2e";
399
+ n: number;
400
+ d: string;
401
+ }
402
+ /**
403
+ * Either direction. An opaque handshake message exchanged BEFORE the channel
404
+ * is wrapped. `d` is base64 of a transport-agnostic handshake message (the
405
+ * `pair/v1` hello or reply, produced by `@agentproto/secrets/pairing`). The
406
+ * E2E layer here treats it as opaque bytes handed to an injected driver, so
407
+ * this package never depends on the crypto package.
408
+ */
409
+ interface E2eHandshakeFrame {
410
+ t: "e2e_handshake";
411
+ d: string;
412
+ }
413
+ type HostToDaemonFrame = SpawnFrame | StdinFrame | KillFrame | ResizeFrame | HttpRequestFrame | WsOpenFrame | WsMessageFrame | WsCloseFrame | PingFrame | PongFrame | ErrorFrame | ReconnectSoonFrame | E2eFrame | E2eHandshakeFrame;
414
+ type DaemonToHostFrame = HelloFrame | SpawnedFrame | StdoutFrame | StderrFrame | ExitFrame | HttpResponseFrame | HttpResponseHeadFrame | HttpResponseChunkFrame | WsOpenAckFrame | WsMessageFrame | WsCloseFrame | PingFrame | PongFrame | ErrorFrame | E2eFrame | E2eHandshakeFrame;
379
415
  type TunnelFrame = HostToDaemonFrame | DaemonToHostFrame;
380
416
  /**
381
417
  * Parse a raw WS message into a `TunnelFrame`. Returns null when the
@@ -417,6 +453,217 @@ interface FrameSink {
417
453
  readonly isOpen: boolean;
418
454
  }
419
455
 
456
+ /**
457
+ * End-to-end AEAD wrapper for a `FrameSink` (design: DESIGN §5).
458
+ *
459
+ * `wrapE2E(inner, keys)` returns a `FrameSink` that is byte-for-byte
460
+ * transparent to the tunnel client/server above it — they send and receive
461
+ * ordinary `TunnelFrame`s — while everything crossing `inner` (the real
462
+ * transport, typically a WebSocket spliced through an untrusted rendezvous) is
463
+ * AES-256-GCM ciphertext wrapped in `e2e` envelope frames. The rendezvous sees
464
+ * only opaque bytes, their sizes, and their timing.
465
+ *
466
+ * There is deliberately NO new application protocol: the existing
467
+ * `agentproto/tunnel/v1` frames are reused verbatim, serialized with
468
+ * `encodeFrame`, encrypted, and carried inside `e2e` envelopes. So
469
+ * `createTunnelClient` / `createTunnelServer` work unchanged over a wrapped
470
+ * sink.
471
+ *
472
+ * Nonce discipline (the security-critical part):
473
+ * - Two independent AES-256-GCM keys, one per direction (`sendKey` /
474
+ * `recvKey`), already assigned by the handshake — so a frame can never be
475
+ * reflected back and decrypt.
476
+ * - A per-direction, strictly-monotonic 64-bit counter `n` is the GCM nonce
477
+ * (never randomly chosen — GCM nonce reuse is catastrophic, and a counter
478
+ * under one key can never repeat). `n` is also bound as AEAD associated
479
+ * data, so it can't be edited without failing the tag.
480
+ * - The receiver requires `n` to be exactly the next expected value.
481
+ * Anything else — an older/repeated `n` (replay), a higher `n` (a dropped
482
+ * or reordered frame) — is a typed `E2eError` that closes the channel.
483
+ * The GCM tag itself catches any bit-flip inside the ciphertext.
484
+ *
485
+ * This module depends only on `node:crypto` and the frame codec — never on the
486
+ * crypto/handshake package. It consumes opaque key material and, for the
487
+ * handshake-over-sink helpers, an opaque driver callback.
488
+ */
489
+
490
+ /** The two symmetric session keys, already role-adjusted by the handshake
491
+ * (this side's `sendKey` is the peer's `recvKey`). Each is 32 bytes for
492
+ * AES-256-GCM. */
493
+ interface E2eKeys {
494
+ sendKey: Uint8Array;
495
+ recvKey: Uint8Array;
496
+ }
497
+ /** Stable failure codes for the AEAD channel. */
498
+ type E2eErrorCode = "replay" | "reorder" | "auth" | "decode" | "not_e2e" | "overflow";
499
+ /** Raised for every E2E channel failure. Surfaced via `onSecurityError` and as
500
+ * the `onClose` reason — the channel always closes on any of these, so no
501
+ * tampered or out-of-order frame is ever delivered to the tunnel above. */
502
+ declare class E2eError extends Error {
503
+ readonly code: E2eErrorCode;
504
+ constructor(code: E2eErrorCode, message: string);
505
+ }
506
+ interface WrapE2EOptions {
507
+ /**
508
+ * Called once, with the typed error, the first time an inbound frame fails
509
+ * an AEAD or counter check (or a send overflows). The channel is closed
510
+ * immediately afterwards. Optional — the same reason is also delivered to
511
+ * `onClose` listeners, so consumers that only care about closure can ignore
512
+ * this.
513
+ */
514
+ onSecurityError?: (err: E2eError) => void;
515
+ /**
516
+ * Frame count at which sends must stop and rekey. Defaults to 2^32 — the
517
+ * point at which a 64-bit-counter/one-key regime should rotate keys. v1 has
518
+ * no rekey, so it errors here instead of ever reusing a nonce. Unreachable
519
+ * in practice; the guard exists so it can never be violated.
520
+ */
521
+ maxFrames?: number;
522
+ }
523
+ /** A wrapped sink also exposes its direction counters, so a later phase can
524
+ * trigger a rekey as they approach the limit. */
525
+ interface E2eFrameSink extends FrameSink {
526
+ /** Number of frames this side has encrypted and sent. */
527
+ readonly sentCount: number;
528
+ /** Number of frames this side has decrypted and delivered. */
529
+ readonly recvCount: number;
530
+ }
531
+ /** Default rekey ceiling: 2^32 frames per direction. */
532
+ declare const DEFAULT_E2E_MAX_FRAMES = 4294967296;
533
+ /**
534
+ * Wrap `inner` so the tunnel above it exchanges plaintext `TunnelFrame`s while
535
+ * the wire carries AEAD ciphertext. Transparent: pass the result anywhere a
536
+ * `FrameSink` is expected.
537
+ */
538
+ declare function wrapE2E(inner: FrameSink, keys: E2eKeys, opts?: WrapE2EOptions): E2eFrameSink;
539
+ interface HandshakeOverSinkOptions {
540
+ /** How long to wait for the peer's handshake message before failing.
541
+ * Default 10s. */
542
+ timeoutMs?: number;
543
+ /** Forwarded to `wrapE2E`. */
544
+ wrap?: WrapE2EOptions;
545
+ }
546
+ /**
547
+ * Client side of the handshake-over-sink. Sends the `hello` bytes, awaits the
548
+ * daemon's single reply, runs `deriveKeys` (which verifies the reply and yields
549
+ * the session keys), and returns a wrapped sink. Rejects — closing the sink —
550
+ * if `deriveKeys` throws (e.g. a bad daemon signature).
551
+ */
552
+ declare function clientHandshakeOverSink(sink: FrameSink, hello: Uint8Array, deriveKeys: (reply: Uint8Array) => E2eKeys, opts?: HandshakeOverSinkOptions): Promise<E2eFrameSink>;
553
+ /**
554
+ * Daemon side of the handshake-over-sink. Awaits the client `hello`, runs
555
+ * `respond` (which validates it and yields the reply bytes + session keys),
556
+ * sends the reply, and returns a wrapped sink. Rejects — closing the sink — if
557
+ * `respond` throws (e.g. a rejected offer token or tampered hello).
558
+ */
559
+ declare function daemonHandshakeOverSink(sink: FrameSink, respond: (hello: Uint8Array) => {
560
+ reply: Uint8Array;
561
+ keys: E2eKeys;
562
+ }, opts?: HandshakeOverSinkOptions): Promise<E2eFrameSink>;
563
+
564
+ /**
565
+ * Opt-in E2E negotiation for the reverse `serve --connect` tunnel
566
+ * (design: tunnel-e2e/v1).
567
+ *
568
+ * The plaintext reverse tunnel lets a host terminate the WS and read every
569
+ * `agentproto/tunnel/v1` frame. These helpers upgrade that channel to the
570
+ * `wrapE2E` AEAD box using a short, token-authenticated ephemeral handshake, so
571
+ * even a trusted host loses plaintext visibility — while staying fully
572
+ * backward-compatible: with an old peer (or the feature off) the channel stays
573
+ * exactly as it is today.
574
+ *
575
+ * ## Negotiation, and why it lives before the tunnel `hello`
576
+ *
577
+ * `createTunnelServer` emits the tunnel `hello` as its very first frame, and we
578
+ * want that `hello` — and everything after it — INSIDE the encrypted box. So the
579
+ * negotiation cannot ride on the `hello`; it runs first, on the raw sink, over
580
+ * `e2e_handshake` frames:
581
+ *
582
+ * ```
583
+ * daemon → host: e2e_handshake(offer) // daemon advertises e2e + its ephemeral
584
+ * host → daemon: e2e_handshake(accept) // host advertises e2e + its ephemeral
585
+ * … both wrapE2E(sink) and speak tunnel/v1 encrypted from here …
586
+ * ```
587
+ *
588
+ * The daemon (initiator) offers e2e ONLY when configured to; the host accepts
589
+ * ONLY when it too supports it. If either doesn't, we fall back to plaintext:
590
+ * - **Old host**: never sends an `accept`. `e2e_handshake` is a known frame
591
+ * type with no handler on an unwrapped peer, so the daemon's offer is
592
+ * ignored, and after `timeoutMs` the daemon falls back to plaintext on the
593
+ * same still-open sink. One stray, ignored frame — behaviour is otherwise
594
+ * identical to today.
595
+ * - **Daemon with e2e off**: never sends an `offer`; it emits the plaintext
596
+ * `hello` first. An e2e-capable host detects a non-handshake first frame and
597
+ * falls back, re-injecting that frame into its plaintext client.
598
+ *
599
+ * ## Fail-closed vs. fall-back
600
+ *
601
+ * A silent peer (timeout) is a COMPATIBILITY signal → fall back to plaintext. A
602
+ * peer that DID answer but whose handshake is invalid (a wrong `tunnel.token`, a
603
+ * tampered message) is a SECURITY signal → the injected crypto throws and we
604
+ * close the sink and propagate, never downgrading to plaintext. Distinguishing
605
+ * these two is the whole point of returning `null` on timeout but throwing on a
606
+ * bad handshake.
607
+ *
608
+ * This module is crypto-agnostic: all key material comes from injected callbacks
609
+ * (backed by `@agentproto/secrets/pairing`'s `tunnel-handshake`). It depends only
610
+ * on `wrapE2E` + the frame codec, so `@agentproto/acp` never imports the crypto
611
+ * package — exactly as the pairing handshake-over-sink helpers do.
612
+ */
613
+
614
+ /** Default negotiation timeout: how long the daemon waits for the host's
615
+ * `accept` (or the host waits for the daemon's first frame) before concluding
616
+ * the peer does not support e2e and falling back to plaintext. */
617
+ declare const DEFAULT_TUNNEL_E2E_TIMEOUT_MS = 8000;
618
+ interface TunnelE2EOptions {
619
+ /** Negotiation timeout in ms. Default `DEFAULT_TUNNEL_E2E_TIMEOUT_MS`. */
620
+ timeoutMs?: number;
621
+ /** Forwarded to `wrapE2E`. */
622
+ wrap?: WrapE2EOptions;
623
+ }
624
+ /**
625
+ * Daemon side of the tunnel-e2e negotiation. Sends the `offer` bytes as an
626
+ * `e2e_handshake` frame, awaits the host's single reply, and:
627
+ *
628
+ * - **reply arrives** → runs `deriveKeys` (which verifies the reply and yields
629
+ * the session keys) and returns a `wrapE2E`-wrapped sink. If `deriveKeys`
630
+ * throws — a wrong `tunnel.token`, a tampered accept — this CLOSES the sink
631
+ * and rethrows: a security failure must NOT silently downgrade to plaintext.
632
+ * - **timeout (peer silent)** → returns `null`. The peer is an old/plaintext
633
+ * host; the sink is left open so the caller falls back to plaintext on it.
634
+ * - **a non-handshake frame arrives first** → protocol violation (a compliant
635
+ * old host stays silent until it receives the tunnel `hello`); closes the
636
+ * sink and throws.
637
+ */
638
+ declare function connectSinkE2E(sink: FrameSink, offer: Uint8Array, deriveKeys: (reply: Uint8Array) => E2eKeys, opts?: TunnelE2EOptions): Promise<E2eFrameSink | null>;
639
+ /** Outcome of the host-side negotiation. On the plaintext branch, `sink` is a
640
+ * view of the original transport that will replay any already-read frame to the
641
+ * first subscriber — so the host can hand it straight to a plaintext tunnel
642
+ * client without losing the daemon's `hello`. */
643
+ type AcceptSinkE2EResult = {
644
+ e2e: true;
645
+ sink: E2eFrameSink;
646
+ } | {
647
+ e2e: false;
648
+ sink: FrameSink;
649
+ };
650
+ /**
651
+ * Host side of the tunnel-e2e negotiation. Awaits the daemon's first frame:
652
+ *
653
+ * - **`e2e_handshake` offer** → runs `respond` (which verifies the offer and
654
+ * yields the reply bytes + session keys), sends the reply, and returns a
655
+ * `wrapE2E`-wrapped sink. A `respond` throw — wrong token, tampered offer —
656
+ * closes the sink and rethrows (fail closed).
657
+ * - **any other frame** (the daemon emitted a plaintext `hello` — it isn't
658
+ * doing e2e) → returns `{ e2e: false }` with a sink that replays that frame,
659
+ * so the caller's plaintext client still sees it.
660
+ * - **timeout** → `{ e2e: false }` on the raw sink (nothing to replay).
661
+ */
662
+ declare function acceptSinkE2E(sink: FrameSink, respond: (offer: Uint8Array) => {
663
+ reply: Uint8Array;
664
+ keys: E2eKeys;
665
+ }, opts?: TunnelE2EOptions): Promise<AcceptSinkE2EResult>;
666
+
420
667
  /**
421
668
  * Tunnel server (daemon side).
422
669
  *
@@ -469,6 +716,16 @@ interface TunnelServerOptions {
469
716
  * `error: { code: "http_upstream_not_configured" }`.
470
717
  */
471
718
  httpUpstream?: string;
719
+ /**
720
+ * Headers merged into EVERY forwarded `http_request` before it hits the
721
+ * upstream, overriding any same-named header from the frame. Used by the E2E
722
+ * pairing serve path to inject the daemon's own bearer token so a
723
+ * peer-authenticated pairing passes the gateway's mutating-route token gate
724
+ * (`checkSessionsToken` has no loopback bypass) — see DESIGN §5. Omitted for
725
+ * the trusted `serve --connect` host, which needs no injection. The pairing
726
+ * layer never learns or transports the user's daemon bearer beyond this hop.
727
+ */
728
+ httpInjectHeaders?: Readonly<Record<string, string>>;
472
729
  /**
473
730
  * Default ceiling for an `http_request` forward (connect + buffered body, or
474
731
  * connect + headers for a stream) when the frame carries no `timeoutMs`.
@@ -494,6 +751,14 @@ interface TunnelServerOptions {
494
751
  * route by capability without a round-trip. Omit to advertise none.
495
752
  */
496
753
  tools?: ReadonlyArray<string>;
754
+ /**
755
+ * Set when the `sink` is an E2E-encrypted channel (a `wrapE2E` box). Purely
756
+ * informational — surfaced to the host as `hello.capabilities.e2e` so it can
757
+ * confirm/display that the tunnel is end-to-end encrypted. The encryption
758
+ * itself is established by the handshake BEFORE this server is constructed
759
+ * (see ./tunnel-e2e.ts); this flag does not turn anything on. Default false.
760
+ */
761
+ e2e?: boolean;
497
762
  /**
498
763
  * Whether this daemon advertises PTY support. Set to true only when
499
764
  * `spawnPty` is also provided.
@@ -816,4 +1081,4 @@ interface WebSocketLike {
816
1081
  }
817
1082
  declare function wrapWebSocket(ws: WebSocketLike): FrameSink;
818
1083
 
819
- export { DEFAULT_HTTP_FORWARD_TIMEOUT_MS, DEFAULT_WS_DIAL_TIMEOUT_MS, type DaemonToHostFrame, type ErrorFrame, type ExitFrame, type FrameSink, type HelloFrame, type HostToDaemonFrame, type KillFrame, type PingFrame, type PongFrame, type PtyProcess, type ReconnectSoonFrame, type ResizeFrame, type SpawnFrame, type SpawnedFrame, type StderrFrame, type StdinFrame, type StdoutFrame, TUNNEL_VERSION, type TunnelChildProcess, type TunnelClient, type TunnelClientOptions, type TunnelFrame, type TunnelHttpRequest, type TunnelHttpResponse, type TunnelHttpStreamResponse, type TunnelServer, type TunnelServerOptions, type TunnelSpawnOptions, type TunnelWebSocket, type TunnelWebSocketOpenRequest, type UpstreamWebSocket, type WebSocketLike, type WsCloseFrame, type WsMessageFrame, type WsOpenAckFrame, type WsOpenFrame, createTunnelClient, createTunnelServer, decodeData, encodeData, encodeFrame, parseFrame, wrapWebSocket };
1084
+ export { type AcceptSinkE2EResult, DEFAULT_E2E_MAX_FRAMES, DEFAULT_HTTP_FORWARD_TIMEOUT_MS, DEFAULT_TUNNEL_E2E_TIMEOUT_MS, DEFAULT_WS_DIAL_TIMEOUT_MS, type DaemonToHostFrame, E2eError, type E2eErrorCode, type E2eFrame, type E2eFrameSink, type E2eHandshakeFrame, type E2eKeys, type ErrorFrame, type ExitFrame, type FrameSink, type HandshakeOverSinkOptions, type HelloFrame, type HostToDaemonFrame, type KillFrame, type PingFrame, type PongFrame, type PtyProcess, type ReconnectSoonFrame, type ResizeFrame, type SpawnFrame, type SpawnedFrame, type StderrFrame, type StdinFrame, type StdoutFrame, TUNNEL_VERSION, type TunnelChildProcess, type TunnelClient, type TunnelClientOptions, type TunnelE2EOptions, type TunnelFrame, type TunnelHttpRequest, type TunnelHttpResponse, type TunnelHttpStreamResponse, type TunnelServer, type TunnelServerOptions, type TunnelSpawnOptions, type TunnelWebSocket, type TunnelWebSocketOpenRequest, type UpstreamWebSocket, type WebSocketLike, type WrapE2EOptions, type WsCloseFrame, type WsMessageFrame, type WsOpenAckFrame, type WsOpenFrame, acceptSinkE2E, clientHandshakeOverSink, connectSinkE2E, createTunnelClient, createTunnelServer, daemonHandshakeOverSink, decodeData, encodeData, encodeFrame, parseFrame, wrapE2E, wrapWebSocket };