@wrongstack/acp 0.295.1 → 0.296.2

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,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/agent/stdio-transport.ts", "../src/win32-cmd.ts", "../src/types/acp-v1.ts", "../src/client/file-server.ts", "../src/client/permission.ts", "../src/client/terminal-server.ts", "../src/client/trust-boundary-permission.ts", "../src/client/websocket-transport.ts", "../src/client/acp-session.ts", "../src/integration/acp-subagent-runner.ts", "../src/client/tool-translator.ts"],
4
- "sourcesContent": ["/**\n * StdioTransport \u2014 bidirectional stdin/stdout communication for ACP.\n *\n * ACP uses newline-delimited JSON-RPC 2.0 messages over stdio:\n * client \u2192 agent: JSON-RPC request/notification on stdin\n * agent \u2192 client: JSON-RPC response/notification on stdout\n *\n * Legacy startup marker support remains for older internal harnesses, but\n * standard ACP agents must not write non-JSON data to stdout.\n */\nimport { expectDefined, writeErr } from '@wrongstack/core/utils';\nimport { treeKill } from '@wrongstack/core/utils/tree-kill';\nimport type { ACPMessage } from '../types/acp-messages.js';\nimport { buildWin32CmdShimInvocation } from '../win32-cmd.js';\n\nconst DEFAULT_MAX_FRAME_CHARS = 20 * 1024 * 1024;\nconst DEFAULT_MAX_QUEUED_MESSAGES = 1_000;\n\nfunction positiveLimit(value: number | undefined, fallback: number): number {\n return value !== undefined && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;\n}\n\nexport interface AgentServerTransport {\n send(msg: ACPMessage): Promise<void>;\n sendRaw(chunk: string): void;\n read(): Promise<ACPMessage | null>;\n close(): void;\n onMessage(handler: (msg: ACPMessage) => void): () => void;\n}\n\n/**\n * Minimal client-side transport contract `ACPSession` drives. `ClientTransport`\n * (stdio subprocess) and `WebSocketClientTransport` (remote) both implement it,\n * so the session is agnostic to how bytes reach the agent.\n */\nexport interface ACPClientTransport {\n start(): Promise<void>;\n send(msg: ACPMessage): Promise<void>;\n onMessage(handler: (msg: ACPMessage) => void): () => void;\n stop(): void;\n}\n\nexport class StdioTransport implements AgentServerTransport {\n private readonly stdin = process.stdin;\n private readonly stdout = process.stdout;\n private readonly stderr = process.stderr;\n\n private buffer = '';\n private readonly handlers = new Set<(msg: ACPMessage) => void>();\n private closed = false;\n private resolveRead: ((msg: ACPMessage | null) => void) | null = null;\n private messageQueue: ACPMessage[] = [];\n private readonly maxFrameChars: number;\n private readonly maxQueuedMessages: number;\n\n constructor(opts: { maxFrameChars?: number; maxQueuedMessages?: number } = {}) {\n this.maxFrameChars = positiveLimit(opts.maxFrameChars, DEFAULT_MAX_FRAME_CHARS);\n this.maxQueuedMessages = positiveLimit(opts.maxQueuedMessages, DEFAULT_MAX_QUEUED_MESSAGES);\n this.stdin.resume();\n this.stdin.setEncoding('utf8');\n this.stdin.on('data', (chunk: string) => this.onData(chunk));\n this.stdin.on('end', () => this.handleClose());\n this.stdin.on('error', (err: Error) => this.failAll(err));\n }\n\n sendStartupMarker(): void {\n this.stdout.write('[wstack-acp]\\n', 'utf8');\n }\n\n send(msg: ACPMessage): Promise<void> {\n if (this.closed) return Promise.resolve();\n return new Promise((resolve) => {\n const line = JSON.stringify(msg) + '\\n';\n this.stdout.write(line, 'utf8', () => resolve());\n });\n }\n\n sendRaw(chunk: string): void {\n this.stdout.write(chunk, 'utf8');\n }\n\n read(): Promise<ACPMessage | null> {\n if (this.messageQueue.length > 0)\n return Promise.resolve(expectDefined(this.messageQueue.shift()));\n if (this.closed) return Promise.resolve(null);\n return new Promise((resolve) => {\n this.resolveRead = resolve;\n });\n }\n\n onMessage(handler: (msg: ACPMessage) => void): () => void {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n close(): void {\n this.closed = true;\n this.stdin.pause();\n this.resolveRead?.(null);\n this.resolveRead = null;\n this.buffer = '';\n this.messageQueue.length = 0;\n this.handlers.clear();\n }\n\n private onData(chunk: string): void {\n this.buffer += chunk;\n const lines = this.buffer.split('\\n');\n /* v8 ignore next -- split() always yields \u22651 element, so pop() is never undefined; the ?? '' is defensive. */\n this.buffer = lines.pop() ?? '';\n if (this.buffer.length > this.maxFrameChars) {\n this.stderr.write(\n `[wstack-acp frame error] pending frame exceeds ${this.maxFrameChars} characters\\n`,\n 'utf8',\n );\n this.close();\n return;\n }\n\n for (const raw of lines) {\n if (!raw.trim()) continue;\n if (raw.length > this.maxFrameChars) {\n this.stderr.write(\n `[wstack-acp frame error] frame exceeds ${this.maxFrameChars} characters\\n`,\n 'utf8',\n );\n this.close();\n return;\n }\n try {\n this.dispatch(JSON.parse(raw) as ACPMessage);\n } catch (err) {\n this.stderr.write(`[wstack-acp parse error] ${err}\\n`, 'utf8');\n }\n }\n }\n\n private dispatch(msg: ACPMessage): void {\n if (this.resolveRead) {\n const resolve = this.resolveRead;\n this.resolveRead = null;\n resolve(msg);\n } else {\n if (this.messageQueue.length >= this.maxQueuedMessages) {\n this.stderr.write(\n `[wstack-acp queue error] pending message queue exceeds ${this.maxQueuedMessages} entries\\n`,\n 'utf8',\n );\n this.close();\n return;\n }\n this.messageQueue.push(msg);\n }\n for (const handler of this.handlers) {\n try {\n handler(msg);\n } catch (err) {\n this.stderr.write(`[wstack-acp handler error] ${err}\\n`, 'utf8');\n }\n }\n }\n\n private handleClose(): void {\n this.close();\n }\n\n private failAll(err: Error): void {\n this.stderr.write(`[wstack-acp stdin error] ${err.message}\\n`, 'utf8');\n this.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// ClientTransport \u2014 spawns a child ACP agent process (DIR-1)\n// ---------------------------------------------------------------------------\n\nimport type { EventEmitter } from 'node:events';\n\nexport interface ClientTransportOptions {\n command: string;\n args?: string[] | undefined;\n env?: Record<string, string>;\n cwd?: string | undefined;\n handshakeTimeoutMs?: number | undefined;\n /**\n * Set to true when the child is an external ACP agent (Claude Code,\n * Gemini CLI, Codex CLI, \u2026) that does NOT emit a `[wstack-acp]\\n`\n * marker on startup. The v1 client (`ACPSession`) sets this; the\n * server-side transport (the default) keeps the marker check.\n */\n skipHandshakeMarker?: boolean | undefined;\n /** Maximum pending newline-delimited JSON frame size. Default 20 MiB. */\n maxFrameChars?: number | undefined;\n /** Maximum messages retained for the optional read() API. Default 1000. */\n maxQueuedMessages?: number | undefined;\n}\n\nexport interface ACPChildProcess extends EventEmitter {\n stdout: NodeJS.ReadableStream;\n stdin: NodeJS.WritableStream;\n stderr: NodeJS.ReadableStream;\n pid: number | undefined;\n kill(): void;\n}\n\nexport class ClientTransport implements ACPClientTransport {\n private child: ACPChildProcess | null = null;\n private buffer = '';\n private readonly handlers = new Set<(msg: ACPMessage) => void>();\n private closed = false;\n private resolveRead: ((msg: ACPMessage | null) => void) | null = null;\n private messageQueue: ACPMessage[] = [];\n private readonly opts: Required<Pick<ClientTransportOptions, 'handshakeTimeoutMs'>> &\n ClientTransportOptions;\n private readonly maxFrameChars: number;\n private readonly maxQueuedMessages: number;\n\n constructor(options: ClientTransportOptions) {\n this.opts = {\n handshakeTimeoutMs: 30_000,\n ...options,\n };\n this.maxFrameChars = positiveLimit(options.maxFrameChars, DEFAULT_MAX_FRAME_CHARS);\n this.maxQueuedMessages = positiveLimit(options.maxQueuedMessages, DEFAULT_MAX_QUEUED_MESSAGES);\n }\n\n async start(): Promise<void> {\n if (this.child) return;\n const [{ spawn }, { buildChildEnv }, os] = await Promise.all([\n import('node:child_process'),\n import('@wrongstack/core/utils'),\n import('node:os'),\n ]);\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(\n new Error(`ACP child process failed to start within ${this.opts.handshakeTimeoutMs}ms`),\n );\n }, this.opts.handshakeTimeoutMs);\n\n // `npx`/`uvx` resolve+install the package using the npm/pip config of\n // the spawn cwd. Inside a repo with dependency `overrides` (WrongStack\n // pins undici/jsdom), that install fails with EOVERRIDE and the adapter\n // never starts \u2192 handshake timeout. Spawn package launchers from a\n // NEUTRAL dir (home) so they install cleanly; the agent still learns the\n // project directory via the ACP `session/new` `cwd` param, not this cwd.\n const isPkgLauncher = this.opts.command === 'npx' || this.opts.command === 'uvx';\n const spawnCwd = isPkgLauncher ? os.homedir() : this.opts.cwd;\n\n try {\n const childArgs = this.opts.args ?? [];\n const invocation = spawnInvocation(this.opts.command, childArgs, process.platform);\n this.child = spawn(invocation.command, invocation.args, {\n env: { ...buildChildEnv(), ...this.opts.env },\n cwd: spawnCwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n ...verbatimOptions(invocation),\n }) as never as ACPChildProcess;\n /* v8 ignore start -- spawn() throwing synchronously is a defensive guard (e.g. argv0 type errors); the realistic async failure path is the child 'error' event, covered by tests. */\n } catch (err) {\n clearTimeout(timeout);\n reject(err);\n return;\n }\n /* v8 ignore stop */\n\n const child = this.child;\n\n child.stdout.setEncoding('utf8');\n\n let settled = false;\n // Register failure handlers IMMEDIATELY, before either readiness path,\n // so a spawn failure (ENOENT / EACCES) rejects start() instead of\n // emitting an unhandled 'error' event that crashes the host process.\n // This is critical for the skip-marker path (external ACP agents),\n // which previously returned before any 'error' listener was attached.\n const onSpawnFailure = (err: Error): void => {\n if (settled) {\n // Post-ready error: just tear the connection down.\n this.closed = true;\n return;\n }\n settled = true;\n clearTimeout(timeout);\n reject(err);\n };\n child.on('error', onSpawnFailure);\n child.stdout.on('error', onSpawnFailure);\n\n if (this.opts.skipHandshakeMarker) {\n // External ACP agents don't emit a startup marker. Attach the data\n // pump right away so no early output is dropped, then resolve once\n // the OS confirms the process actually spawned (the 'spawn' event).\n // If the binary is missing, 'error' fires instead and rejects above.\n child.stdout.on('data', (c: string) => this.onChildData(c));\n child.stderr.on('data', (c: string) => this.onChildError(c));\n child.on('close', (code: number | null) => this.onChildClose(code));\n child.once('spawn', () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolve();\n });\n return;\n }\n\n const onReady = (): void => {\n if (settled) return;\n settled = true;\n child.stdout.on('data', (c: string) => this.onChildData(c));\n child.stderr.on('data', (c: string) => this.onChildError(c));\n child.on('close', (code: number | null) => this.onChildClose(code));\n clearTimeout(timeout);\n resolve();\n };\n\n const waitForMarker = (chunk: string) => {\n this.buffer += chunk;\n const idx = this.buffer.indexOf('[wstack-acp]\\n');\n if (idx !== -1) {\n this.buffer = this.buffer.slice(idx + '[wstack-acp]\\n'.length);\n child.stdout.removeListener('data', waitForMarker);\n onReady();\n }\n };\n\n child.stdout.on('data', waitForMarker);\n });\n }\n\n send(msg: ACPMessage): Promise<void> {\n if (!this.child) return Promise.reject(new Error('ClientTransport not started'));\n return new Promise((resolve, reject) => {\n const line = JSON.stringify(msg) + '\\n';\n this.child?.stdin.write(line, 'utf8', (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n read(): Promise<ACPMessage | null> {\n if (this.messageQueue.length > 0)\n return Promise.resolve(expectDefined(this.messageQueue.shift()));\n if (this.closed) return Promise.resolve(null);\n return new Promise((resolve) => {\n this.resolveRead = resolve;\n });\n }\n\n onMessage(handler: (msg: ACPMessage) => void): () => void {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n stop(): void {\n this.closed = true;\n this.resolveRead?.(null);\n this.resolveRead = null;\n this.buffer = '';\n this.messageQueue.length = 0;\n this.handlers.clear();\n const child = this.child;\n if (!child) return;\n // On Windows `child` is the cmd.exe shim wrapper; a bare kill() orphans the\n // real agent grandchild. treeKill tears down the whole process tree.\n treeKill(child);\n this.child = null;\n }\n\n private onChildData(chunk: string): void {\n this.buffer += chunk;\n const lines = this.buffer.split('\\n');\n /* v8 ignore next -- split() always yields \u22651 element, so pop() is never undefined; the ?? '' is defensive. */\n this.buffer = lines.pop() ?? '';\n if (this.buffer.length > this.maxFrameChars) {\n writeErr(`[acp-child pending frame exceeds ${this.maxFrameChars} characters]\\n`);\n this.stop();\n return;\n }\n\n for (const raw of lines) {\n if (!raw.trim()) continue;\n if (raw.length > this.maxFrameChars) {\n writeErr(`[acp-child frame exceeds ${this.maxFrameChars} characters]\\n`);\n this.stop();\n return;\n }\n try {\n this.dispatch(JSON.parse(raw) as ACPMessage);\n } catch {\n // skip malformed\n }\n }\n }\n\n private onChildError(chunk: string): void {\n writeErr(`[acp-child stderr] ${chunk}`);\n }\n\n private onChildClose(code: number | null): void {\n this.closed = true;\n this.resolveRead?.(null);\n this.resolveRead = null;\n this.buffer = '';\n this.messageQueue.length = 0;\n this.handlers.clear();\n if (code !== 0 && code !== null) {\n writeErr(`[acp-child exited with code ${code}]\\n`);\n }\n }\n\n private dispatch(msg: ACPMessage): void {\n if (this.resolveRead) {\n const resolve = this.resolveRead;\n this.resolveRead = null;\n resolve(msg);\n } else if (this.handlers.size === 0) {\n if (this.messageQueue.length >= this.maxQueuedMessages) {\n writeErr(`[acp-child message queue exceeds ${this.maxQueuedMessages} entries]\\n`);\n this.stop();\n return;\n }\n this.messageQueue.push(msg);\n }\n for (const handler of this.handlers) {\n try {\n handler(msg);\n } catch {\n // non-fatal\n }\n }\n }\n}\n\nfunction spawnInvocation(\n command: string,\n args: string[],\n platform: NodeJS.Platform,\n): {\n command: string;\n args: string[];\n windowsVerbatimArguments?: true;\n} {\n if (platform !== 'win32') return { command, args };\n return buildWin32CmdShimInvocation(command, args);\n}\n\nfunction verbatimOptions(invocation: { windowsVerbatimArguments?: true }): {\n windowsVerbatimArguments?: true;\n} {\n return invocation.windowsVerbatimArguments\n ? { windowsVerbatimArguments: invocation.windowsVerbatimArguments }\n : {};\n}\n\n/** Direct-module test seam; not re-exported by the package barrel. */\nexport const stdioTransportCoverage = { positiveLimit, spawnInvocation, verbatimOptions };\n", "const WIN32_CMD_META = /[&|<>\"\\r\\n\\0]/;\n\nexport interface Win32CmdShimInvocation {\n command: string;\n args: string[];\n windowsVerbatimArguments: true;\n}\n\nexport function buildWin32CmdShimInvocation(\n command: string,\n args: readonly string[] = [],\n): Win32CmdShimInvocation {\n assertSafeWin32CmdArgs([command, ...args]);\n const line = ['call', quoteWin32CmdArg(command), ...args.map(quoteWin32CmdArg)].join(' ');\n return {\n command: process.env['COMSPEC'] ?? 'cmd.exe',\n args: ['/d', '/c', line],\n windowsVerbatimArguments: true,\n };\n}\n\nfunction assertSafeWin32CmdArgs(args: readonly unknown[]): void {\n for (const arg of args) {\n if (typeof arg === 'string' && WIN32_CMD_META.test(arg)) {\n throw new Error(\n 'win32 cmd shim spawn: argument contains a shell metacharacter ' +\n '(one of & | < > \", or a newline) that could enable command injection ' +\n 'through the .cmd/.bat wrapper - refusing to run. Offending argument: ' +\n JSON.stringify(arg),\n );\n }\n }\n}\n\nfunction quoteWin32CmdArg(arg: string): string {\n return `\"${arg}\"`;\n}\n", "/**\n * ACP v1 type definitions \u2014 Agent Client Protocol, stable v1 spec.\n *\n * Scope: discriminated union for the `session/update` notification payload\n * (the `update` field of a `session/update` JSON-RPC notification), plus the\n * subset of supporting types it depends on.\n *\n * Spec: https://agentclientprotocol.com/protocol/v1/overview\n *\n * Design notes\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * \u2022 The stable v1 spec defines 11 `sessionUpdate` discriminator values. We\n * type exactly those 11, plus an `_unstable_*` escape hatch for v2-RFD\n * kinds (e.g. `next_edit_suggestions`, `elicitation`) that real agents\n * may emit before the spec stabilises them, and an `unknown` fallback for\n * everything else. We do NOT synthesise 32 fake variants to match a\n * number cited in passing \u2014 the union is honest about the surface.\n *\n * \u2022 Per the spec's conventions, discriminator values use snake_case. The\n * property keys inside each variant are camelCase (the JSON-RPC envelope\n * is JSON-RPC 2.0, everything else is camelCase unless the spec says\n * otherwise).\n *\n * \u2022 Optional fields that the spec marks optional are marked `?:`. Required\n * fields have no `?`. We do not include spec fields the spec marks\n * \"SHOULD NOT\" or \"reserved\".\n *\n * \u2022 The existing `acp-messages.ts` types describe an older draft of the\n * protocol (string `protocolVersion: '2024-11'`, fake `tools/call`\n * method, etc.). Do NOT import from it here \u2014 `acp-v1.ts` is\n * self-contained so the new code path can be reviewed in isolation and\n * deleted wholesale if the rewrite is ever reverted.\n */\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Shared building blocks\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Stable protocol version (integer per the spec, not a date-string). */\nexport const ACP_PROTOCOL_VERSION = 1 as const;\nexport type ACPProtocolVersion = typeof ACP_PROTOCOL_VERSION;\n\n/** Per the spec: opaque, unique id. We type as branded string. */\nexport type SessionId = string & { readonly __acpSessionId: unique symbol };\nexport type ToolCallId = string & { readonly __acpToolCallId: unique symbol };\nexport type MessageId = string & { readonly __acpMessageId: unique symbol };\nexport type TerminalId = string & { readonly __acpTerminalId: unique symbol };\nexport type PlanEntryId = string & { readonly __acpPlanEntryId: unique symbol };\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Implementation metadata\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface Implementation {\n /** Programmatic/logical name \u2014 also a display-name fallback if title is absent. */\n name: string;\n /** Human-readable display name for UI contexts. */\n title?: string | undefined;\n /** Version string (display/debug/metrics). */\n version: string;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Client capabilities \u2014 sent in the initialize request\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ClientCapabilities {\n fs?: {\n readTextFile?: boolean | undefined;\n writeTextFile?: boolean | undefined;\n } | undefined;\n terminal?: boolean | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Agent capabilities \u2014 received in the initialize response\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface PromptCapabilities {\n image?: boolean | undefined;\n audio?: boolean | undefined;\n embeddedContext?: boolean | undefined;\n}\n\nexport interface McpCapabilities {\n http?: boolean | undefined;\n sse?: boolean | undefined;\n}\n\nexport interface SessionCapabilities {\n close?: Record<string, unknown> | undefined;\n list?: Record<string, unknown> | undefined;\n delete?: Record<string, unknown> | undefined;\n resume?: Record<string, unknown> | undefined;\n additionalDirectories?: Record<string, unknown> | undefined;\n}\n\nexport interface AuthCapabilities {\n logout?: Record<string, unknown> | undefined;\n}\n\nexport interface AgentCapabilities {\n loadSession?: boolean | undefined;\n promptCapabilities?: PromptCapabilities | undefined;\n mcpCapabilities?: McpCapabilities | undefined;\n sessionCapabilities?: SessionCapabilities | undefined;\n auth?: AuthCapabilities | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Authentication\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface AuthMethod {\n id: string;\n name: string;\n description?: string | undefined;\n type?: 'agent' | 'oauth' | 'http' | undefined;\n}\n\nexport interface AuthenticateRequest {\n methodId: string;\n}\n\nexport type AuthenticateResponse = {}\n\nexport type LogoutRequest = {}\n\nexport type LogoutResponse = {}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// MCP server configuration \u2014 sent in session lifecycle requests\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface StdioMcpServer {\n name: string;\n command: string;\n args?: string[] | undefined;\n env?: { name: string; value: string }[] | undefined;\n}\n\nexport interface HttpMcpServer {\n type: 'http';\n name: string;\n url: string;\n headers?: { name: string; value: string }[] | undefined;\n}\n\nexport interface SseMcpServer {\n type: 'sse';\n name: string;\n url: string;\n headers?: { name: string; value: string }[] | undefined;\n}\n\nexport type McpServer = StdioMcpServer | HttpMcpServer | SseMcpServer;\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Session lifecycle \u2014 request/response types\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface NewSessionRequest {\n cwd: string;\n mcpServers: McpServer[];\n additionalDirectories?: string[] | undefined;\n}\n\nexport interface NewSessionResponse {\n sessionId: SessionId;\n initialMode?: SessionModeState | null | undefined;\n configOptions?: SessionConfigOption[] | null | undefined;\n}\n\nexport interface LoadSessionRequest {\n sessionId: SessionId;\n cwd: string;\n mcpServers: McpServer[];\n additionalDirectories?: string[] | undefined;\n}\n\nexport interface LoadSessionResponse {\n initialMode?: SessionModeState | null | undefined;\n configOptions?: SessionConfigOption[] | null | undefined;\n}\n\nexport interface ResumeSessionRequest {\n sessionId: SessionId;\n cwd: string;\n mcpServers: McpServer[];\n additionalDirectories?: string[] | undefined;\n}\n\nexport interface ResumeSessionResponse {\n initialMode?: SessionModeState | null | undefined;\n configOptions?: SessionConfigOption[] | null | undefined;\n}\n\nexport interface CloseSessionRequest {\n sessionId: SessionId;\n}\n\nexport type CloseSessionResponse = {}\n\nexport interface ListSessionsRequest {\n cursor?: string | undefined;\n cwd?: string | undefined;\n}\n\nexport interface ListSessionsResponse {\n sessions: SessionInfo[];\n nextCursor?: string | undefined;\n}\n\nexport interface DeleteSessionRequest {\n sessionId: SessionId;\n}\n\nexport type DeleteSessionResponse = {}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Session config options\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface SessionConfigOption {\n id: string;\n name: string;\n description?: string | undefined;\n category?: ConfigOptionCategory | undefined;\n type: ConfigOptionType;\n defaultValue?: string | undefined;\n currentValue: string;\n options: ConfigOptionValue[];\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Content blocks \u2014 reused from MCP per the spec\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Annotations attached to a content block. Optional, agent-supplied hint\n * about audience/priority. Spec leaves shape open; we mirror the fields\n * the spec shows in its examples.\n */\nexport interface ContentAnnotations {\n audience?: ('user' | 'assistant')[] | undefined;\n priority?: number | undefined;\n [key: string]: unknown;\n}\n\nexport interface TextContent {\n type: 'text';\n text: string;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface ImageContent {\n type: 'image';\n mimeType: string;\n /** Base64-encoded image data. */\n data: string;\n uri?: string | undefined;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface AudioContent {\n type: 'audio';\n mimeType: string;\n /** Base64-encoded audio data. */\n data: string;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface TextResourceContents {\n uri: string;\n mimeType?: string | undefined;\n text: string;\n}\n\nexport interface BlobResourceContents {\n uri: string;\n mimeType?: string | undefined;\n /** Base64-encoded binary. */\n blob: string;\n}\n\nexport type EmbeddedResourceContents = TextResourceContents | BlobResourceContents;\n\nexport interface EmbeddedResourceContent {\n type: 'resource';\n resource: EmbeddedResourceContents;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface ResourceLinkContent {\n type: 'resource_link';\n uri: string;\n name: string;\n mimeType?: string | undefined;\n title?: string | undefined;\n description?: string | undefined;\n size?: number | undefined;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport type ContentBlock =\n | TextContent\n | ImageContent\n | AudioContent\n | EmbeddedResourceContent\n | ResourceLinkContent;\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Tool calls\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type ToolKind =\n | 'read'\n | 'edit'\n | 'delete'\n | 'move'\n | 'search'\n | 'execute'\n | 'think'\n | 'fetch'\n | 'switch_mode'\n | 'other';\n\nexport type ToolCallStatus = 'pending' | 'in_progress' | 'completed' | 'failed';\n\n/** A single concrete content payload attached to a tool call. */\nexport type ToolCallContent =\n | { type: 'content'; content: ContentBlock }\n | {\n type: 'diff';\n path: string;\n oldText: string | null;\n newText: string;\n }\n | { type: 'terminal'; terminalId: TerminalId };\n\nexport interface ToolCallLocation {\n path: string;\n /** 1-based per the spec's argument requirements. */\n line?: number | undefined;\n}\n\nexport interface ToolCall {\n toolCallId: ToolCallId;\n title: string;\n kind?: ToolKind | undefined;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n rawOutput?: Record<string, unknown> | undefined;\n}\n\n/**\n * Partial update of a previously-emitted tool call. All fields except\n * `toolCallId` are optional \u2014 only the changed fields are included.\n * Declared standalone (not `extends ToolCall`) because `title` is required\n * on `ToolCall` but optional here; the structural variance is the point.\n */\nexport interface ToolCallUpdateFields {\n toolCallId: ToolCallId;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n title?: string | undefined;\n kind?: ToolKind | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n rawOutput?: Record<string, unknown> | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Plan\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type PlanEntryPriority = 'high' | 'medium' | 'low';\nexport type PlanEntryStatus = 'pending' | 'in_progress' | 'completed';\n\nexport interface PlanEntry {\n /** Required by the spec for the array shape, but per-entry id is optional. */\n content: string;\n priority: PlanEntryPriority;\n status: PlanEntryStatus;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Slash commands\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface AvailableCommandInput {\n hint: string;\n}\n\nexport interface AvailableCommand {\n name: string;\n description: string;\n input?: AvailableCommandInput | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Session modes\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type SessionModeId = string & { readonly __acpModeId: unique symbol };\n\nexport interface SessionMode {\n id: SessionModeId;\n name: string;\n description?: string | undefined;\n}\n\nexport interface SessionModeState {\n currentModeId: SessionModeId;\n availableModes: SessionMode[];\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Config options\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Reserved spec categories. Underscore-prefixed names are free for custom use. */\nexport type ConfigOptionCategory =\n | 'mode'\n | 'model'\n | 'thought_level'\n | `_${string}`;\n\nexport type ConfigOptionType = 'select' | string;\n\nexport interface ConfigOptionValue {\n value: string;\n name: string;\n description?: string | undefined;\n}\n\nexport interface ConfigOption {\n id: string;\n name: string;\n description?: string | undefined;\n category?: ConfigOptionCategory | undefined;\n type: ConfigOptionType;\n currentValue: string;\n options: ConfigOptionValue[];\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Session info\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface SessionInfo {\n sessionId: SessionId;\n cwd: string;\n title?: string | undefined;\n updatedAt?: string | undefined;\n /** Agent-supplied extension metadata; opaque to clients. */\n _meta?: Record<string, unknown> | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Usage (token / cost) updates\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface UsageCost {\n amount: number;\n /** ISO 4217 currency code, e.g. \"USD\". */\n currency: string;\n}\n\nexport interface UsageUpdate {\n /** Tokens used in the current session context. Required, non-null. */\n used: number;\n /** Total context window size in tokens. Required, non-null. */\n size: number;\n cost?: UsageCost | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Permission requests\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type PermissionOptionKind =\n | 'allow_once'\n | 'allow_always'\n | 'reject_once'\n | 'reject_always';\n\nexport interface PermissionOption {\n optionId: string;\n name: string;\n kind: PermissionOptionKind;\n}\n\nexport type RequestPermissionOutcome =\n | { outcome: 'cancelled' }\n | { outcome: 'selected'; optionId: string };\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Stop reasons\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type StopReason =\n | 'end_turn'\n | 'max_tokens'\n | 'max_turn_requests'\n | 'refusal'\n | 'cancelled'\n | string;\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// SessionUpdate \u2014 the discriminated union\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Stable v1 variants. The spec currently defines exactly 11. */\nexport type SessionUpdate =\n | UserMessageChunkUpdate\n | AgentMessageChunkUpdate\n | ThoughtChunkUpdate\n | ToolCallUpdateUpdate\n | ToolCallUpdateNotification\n | PlanUpdate\n | AvailableCommandsUpdate\n | CurrentModeUpdate\n | ConfigOptionUpdate\n | SessionInfoUpdate\n | UsageUpdateUpdate;\n\n// --- Streaming message chunks -----------------------------------------------\n\nexport interface UserMessageChunkUpdate {\n sessionUpdate: 'user_message_chunk';\n messageId?: MessageId | undefined;\n content: ContentBlock;\n}\n\nexport interface AgentMessageChunkUpdate {\n sessionUpdate: 'agent_message_chunk';\n messageId?: MessageId | undefined;\n content: ContentBlock;\n}\n\nexport interface ThoughtChunkUpdate {\n sessionUpdate: 'thought_chunk';\n messageId?: MessageId | undefined;\n content: ContentBlock;\n}\n\n// --- Tool calls ------------------------------------------------------------\n\n/** First notification for a new tool call. */\nexport interface ToolCallUpdateUpdate {\n sessionUpdate: 'tool_call';\n toolCallId: ToolCallId;\n title: string;\n kind?: ToolKind | undefined;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n}\n\n/** Subsequent updates to a previously-emitted tool call. */\nexport interface ToolCallUpdateNotification {\n sessionUpdate: 'tool_call_update';\n toolCallId: ToolCallId;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n title?: string | undefined;\n kind?: ToolKind | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n rawOutput?: Record<string, unknown> | undefined;\n}\n\n// --- Plan ------------------------------------------------------------------\n\nexport interface PlanUpdate {\n sessionUpdate: 'plan';\n entries: PlanEntry[];\n}\n\n// --- Commands / modes / config ---------------------------------------------\n\nexport interface AvailableCommandsUpdate {\n sessionUpdate: 'available_commands_update';\n availableCommands: AvailableCommand[];\n}\n\nexport interface CurrentModeUpdate {\n sessionUpdate: 'current_mode_update';\n modeId: SessionModeId;\n}\n\nexport interface ConfigOptionUpdate {\n sessionUpdate: 'config_option_update';\n configOptions: ConfigOption[];\n}\n\n// --- Session metadata ------------------------------------------------------\n\nexport interface SessionInfoUpdate {\n sessionUpdate: 'session_info_update';\n title?: string | null | undefined;\n updatedAt?: string | null | undefined;\n _meta?: Record<string, unknown> | undefined;\n}\n\n// --- Usage -----------------------------------------------------------------\n\nexport interface UsageUpdateUpdate {\n sessionUpdate: 'usage_update';\n used: number;\n size: number;\n cost?: UsageCost | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Escape hatches: unknown / unstable\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Escape hatch for v2-RFD `sessionUpdate` kinds that have been published\n * but are not yet stabilised in the v1 spec. Examples seen in the wild:\n * `next_edit_suggestions`, `elicitation`, `proxy_extension`. We surface\n * the raw payload so forward-compat code can switch on\n * `kind === 'next_edit_suggestions'` etc. without losing the data.\n */\nexport interface UnstableSessionUpdate {\n sessionUpdate: `_unstable_${string}`;\n [key: string]: unknown;\n}\n\n/**\n * Last-resort variant: the agent sent a discriminator string we don't\n * recognise at all. The full payload is preserved as a record so consumers\n * can still log/inspect it. Prefer matching the known variants first.\n */\nexport interface UnknownSessionUpdate {\n sessionUpdate: string;\n [key: string]: unknown;\n}\n\n/** The full union, including escape hatches. */\nexport type AnySessionUpdate = SessionUpdate | UnstableSessionUpdate | UnknownSessionUpdate;\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Top-level notification envelope\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface SessionUpdateNotification {\n jsonrpc?: '2.0' | undefined;\n method: 'session/update';\n params: {\n sessionId: SessionId;\n update: AnySessionUpdate;\n };\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Type guards\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Exhaustiveness helper. Call from the `default:` branch of a switch on\n * `sessionUpdate` to get a compile-time error when a new variant is added\n * without updating the consumer.\n */\nexport function assertNeverSessionUpdate(x: never): never {\n throw new Error(\n `Unhandled sessionUpdate: ${JSON.stringify(x)}`,\n );\n}\n", "/**\n * FileServer \u2014 answers `fs/read_text_file` and `fs/write_text_file`\n * from an ACP agent, scoped to a single project root.\n *\n * Per the spec, all file paths in ACP MUST be absolute. We additionally\n * require them to resolve under `projectRoot` after normalisation AND after\n * resolving symlinks (`fs.realpath`). A path that passes a lexical prefix\n * check but points outside the root via an in-project symlink/junction is\n * rejected. This closes the CWE-22/CWE-59 symlink-escape vector that a\n * purely textual containment check leaves open.\n *\n * The server itself is transport-agnostic: the caller (ACPSession)\n * routes incoming fs/* requests to `readTextFile`/`writeTextFile` and\n * sends the result back. Keeping the routing out of this class lets the\n * file logic be unit-tested in isolation.\n */\nimport { randomBytes } from 'node:crypto';\nimport { realpathSync } from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\n\nexport interface FileServerOptions {\n /** Absolute path; only files under this root are accessible. */\n projectRoot: string;\n /** Per-call timeout, default 30s. */\n timeoutMs?: number;\n /**\n * Hard cap on the number of bytes that may be read in a single\n * `readTextFile` call. Protects against a malicious agent requesting a\n * gigantic file to exhaust host memory. Default 5 MiB.\n */\n maxReadBytes?: number;\n /**\n * Hard cap on the number of bytes that may be written in a single\n * `writeTextFile` call. Default 5 MiB.\n */\n maxWriteBytes?: number;\n /** Filesystem implementation override for deterministic tests. */\n operations?: FileServerOperations;\n}\n\nexport interface FileServerOperations {\n stat(file: string): Promise<{ size: number }>;\n readFile(\n file: string,\n options: { encoding: 'utf8'; signal: AbortSignal },\n ): Promise<string>;\n writeFile(\n file: string,\n content: string,\n options: { encoding: 'utf8'; signal: AbortSignal },\n ): Promise<void>;\n realpath(file: string): Promise<string>;\n rename(from: string, to: string): Promise<void>;\n unlink(file: string): Promise<void>;\n}\n\nconst DEFAULT_FILE_OPERATIONS: FileServerOperations = {\n stat: fsp.stat,\n readFile: fsp.readFile,\n writeFile: fsp.writeFile,\n realpath: fsp.realpath,\n rename: fsp.rename,\n unlink: fsp.unlink,\n};\n\nexport interface ReadFileParams {\n sessionId: string;\n path: string;\n}\n\nexport interface WriteFileParams {\n sessionId: string;\n path: string;\n content: string;\n}\n\nexport type FsErrorCode =\n | 'ENOENT'\n | 'EACCES'\n | 'OUTSIDE_ROOT'\n | 'TIMEOUT'\n | 'INVALID_PATH'\n | 'TOO_LARGE';\n\nconst DEFAULT_MAX_READ_BYTES = 5 * 1024 * 1024;\nconst DEFAULT_MAX_WRITE_BYTES = 5 * 1024 * 1024;\n\n/**\n * Thrown for protocol-level rejections (path outside root, etc.).\n * The session converts these into JSON-RPC error responses.\n */\nexport class FsError extends Error {\n readonly code: FsErrorCode;\n readonly path: string;\n constructor(code: FsErrorCode, path: string, message: string) {\n super(message);\n this.name = 'FsError';\n this.code = code;\n this.path = path;\n }\n}\n\nexport class FileServer {\n private readonly root: string;\n private readonly realRoot: string;\n private readonly timeoutMs: number;\n private readonly maxReadBytes: number;\n private readonly maxWriteBytes: number;\n private readonly operations: FileServerOperations;\n\n constructor(opts: FileServerOptions) {\n this.root = path.resolve(opts.projectRoot);\n // Resolve the root itself once \u2014 it may be a symlink (macOS /var \u2192\n // /private/var, Windows 8.3 short names, etc.). All realpaths are\n // compared against this canonical root for a like-for-like check.\n // Synchronous in constructor: the root must exist for an ACP session\n // and this keeps the per-call path simple.\n this.realRoot = safeRealpathSync(this.root);\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.maxReadBytes = opts.maxReadBytes ?? DEFAULT_MAX_READ_BYTES;\n this.maxWriteBytes = opts.maxWriteBytes ?? DEFAULT_MAX_WRITE_BYTES;\n this.operations = opts.operations ?? DEFAULT_FILE_OPERATIONS;\n }\n\n /** Read a text file. Returns the content as a string. */\n async readTextFile(params: ReadFileParams): Promise<{ content: string }> {\n const safe = await this.resolveInside(params.path);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n // Size check before reading to prevent loading a huge file.\n const stat = await this.operations.stat(safe).catch((err) => {\n throw mapFsError(err, safe);\n });\n if (stat.size > this.maxReadBytes) {\n throw new FsError(\n 'TOO_LARGE',\n safe,\n `file is ${stat.size} bytes, max read is ${this.maxReadBytes} bytes`,\n );\n }\n const content = await this.operations.readFile(safe, {\n encoding: 'utf8',\n signal: controller.signal,\n });\n return { content };\n } catch (err) {\n if (err instanceof FsError) throw err;\n if (controller.signal.aborted) {\n throw new FsError('TIMEOUT', safe, `readTextFile timed out after ${this.timeoutMs}ms`);\n }\n throw mapFsError(err, safe);\n } finally {\n clearTimeout(timer);\n }\n }\n\n /** Write a text file. Atomic via write-then-rename. */\n async writeTextFile(params: WriteFileParams): Promise<void> {\n const byteLength = Buffer.byteLength(params.content, 'utf8');\n if (byteLength > this.maxWriteBytes) {\n throw new FsError(\n 'TOO_LARGE',\n params.path,\n `content is ${byteLength} bytes, max write is ${this.maxWriteBytes} bytes`,\n );\n }\n\n const safe = await this.resolveInside(params.path);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n const tmp = `${safe}.${randomBytes(6).toString('hex')}.tmp`;\n try {\n await this.operations.writeFile(tmp, params.content, {\n encoding: 'utf8',\n signal: controller.signal,\n });\n // Re-verify both tmp and the rename destination's parent dir before\n // rename. This closes the TOCTOU window where an attacker could plant a\n // symlink at the destination's parent between resolveInside and rename.\n await this.assertRealInside(tmp);\n await this.assertRealInside(path.dirname(safe));\n await this.operations.rename(tmp, safe);\n } catch (err) {\n if (err instanceof FsError) {\n // Best-effort cleanup of the tmp file\n await this.operations.unlink(tmp).catch(() => undefined);\n throw err;\n }\n // Best-effort cleanup of the tmp file\n try {\n await this.operations.unlink(tmp);\n } catch {\n // tmp didn't exist; ignore\n }\n if (controller.signal.aborted) {\n throw new FsError('TIMEOUT', safe, `writeTextFile timed out after ${this.timeoutMs}ms`);\n }\n throw mapFsError(err, safe);\n } finally {\n clearTimeout(timer);\n }\n }\n\n /**\n * Resolve a path and verify it is inside the project root by realpath.\n * Rejects with `FsError` if the textual path, the resolved path, or the\n * real (symlink-resolved) path escapes the project root.\n *\n * For files that don't exist yet (e.g. a write to a new file), the\n * nearest existing ancestor directory is realpath-checked instead.\n */\n private async resolveInside(p: string): Promise<string> {\n if (typeof p !== 'string' || p.length === 0) {\n throw new FsError('INVALID_PATH', p, 'path is empty or not a string');\n }\n if (!path.isAbsolute(p)) {\n throw new FsError('INVALID_PATH', p, 'path must be absolute (ACP requirement)');\n }\n const resolved = path.resolve(p);\n // +path.sep prevents \"/project-evil\" matching \"/project\" as a prefix.\n const rootWithSep = this.root.endsWith(path.sep) ? this.root : this.root + path.sep;\n if (resolved !== this.root && !resolved.startsWith(rootWithSep)) {\n throw new FsError('OUTSIDE_ROOT', resolved, 'path is outside the project root');\n }\n\n // Now resolve symlinks and compare against the canonical root.\n await this.assertRealInside(resolved);\n return resolved;\n }\n\n /**\n * Resolve `resolvedPath` through `fs.realpath` and verify the result is\n * inside `realRoot`. For non-existent paths (new files), walk up to the\n * nearest existing ancestor and check that instead.\n */\n private async assertRealInside(resolvedPath: string): Promise<void> {\n let probe = resolvedPath;\n for (;;) {\n let real: string;\n try {\n real = await this.operations.realpath(probe);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) {\n throw new FsError('ENOENT', resolvedPath, `no existing ancestor: ${resolvedPath}`);\n }\n probe = parent;\n continue;\n }\n throw mapFsError(err, resolvedPath);\n }\n if (real === this.realRoot || real.startsWith(this.realRoot + path.sep)) return;\n throw new FsError(\n 'OUTSIDE_ROOT',\n resolvedPath,\n 'path resolves through a symlink outside the project root',\n );\n }\n }\n}\n\nfunction mapFsError(err: unknown, p: string): FsError {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'ENOENT') return new FsError('ENOENT', p, `no such file: ${p}`);\n if (code === 'EACCES' || code === 'EPERM') {\n return new FsError('EACCES', p, `permission denied: ${p}`);\n }\n const msg = err instanceof Error ? err.message : String(err);\n return new FsError('INVALID_PATH', p, msg);\n}\n\n/**\n * Synchronous realpath that falls back to the input on failure (root may be\n * a fresh directory that hasn't been statted yet). The constructor needs the\n * canonical root before any async call so per-request resolveInside has a\n * stable comparison target.\n */\nfunction safeRealpathSync(p: string): string {\n try {\n return realpathSync(p);\n } catch {\n return p;\n }\n}\n", "/**\n * Permission policy for ACP v1 client sessions.\n *\n * ACP agents can call `session/request_permission` to ask the user\n * before executing a tool call. The client is expected to surface\n * the question, get a decision, and respond. This module is the seam\n * where WrongStack-specific permission UI can plug in; for v1 we ship\n * a minimal default that auto-approves the first `allow_once` option\n * (or `allow_always` if present) and rejects on abort.\n */\nimport type {\n PermissionOption,\n RequestPermissionOutcome,\n ToolCallUpdateNotification,\n} from '../types/acp-v1.js';\n\n/** A single permission decision request. */\nexport interface PermissionRequest {\n toolCall: ToolCallUpdateNotification;\n options: readonly PermissionOption[];\n signal: AbortSignal;\n}\n\n/** A permission policy decides how to respond to a request. */\nexport type PermissionPolicy = (\n req: PermissionRequest,\n) => Promise<RequestPermissionOutcome>;\n\n/**\n * Default policy: pick the safest-looking allow option if the signal\n * is not aborted, otherwise report cancelled. Order of preference:\n *\n * 1. `allow_always`\n * 2. `allow_once`\n * 3. anything else with `optionId` (last resort)\n *\n * Real WrongStack permission UIs replace this; the contract is the\n * `PermissionPolicy` function type, not the implementation.\n */\nfunction pickAllow(\n options: readonly PermissionOption[],\n): RequestPermissionOutcome {\n const ranked = [...options].sort((a, b) => {\n const score = (k: PermissionOption['kind']): number => {\n if (k === 'allow_once') return 0; // prefer once over always \u2014 least standing grant\n if (k === 'allow_always') return 1;\n if (k === 'reject_once') return 2;\n return 3;\n };\n return score(a.kind) - score(b.kind);\n });\n const chosen = ranked[0];\n if (!chosen || chosen.kind === 'reject_once' || chosen.kind === 'reject_always') {\n return { outcome: 'cancelled' };\n }\n return { outcome: 'selected', optionId: chosen.optionId };\n}\n\nfunction pickReject(\n options: readonly PermissionOption[],\n): RequestPermissionOutcome {\n const reject = options.find(\n (o) => o.kind === 'reject_once' || o.kind === 'reject_always',\n );\n return reject ? { outcome: 'selected', optionId: reject.optionId } : { outcome: 'cancelled' };\n}\n\n/**\n * Tool kinds considered side-effect-free (safe to auto-approve even in a\n * non-interactive run). Everything else (edit/delete/move/execute) mutates\n * the workspace or runs commands and should be gated by a real policy.\n */\nconst READ_ONLY_KINDS = new Set(['read', 'search', 'fetch', 'think']);\n\n/**\n * Default policy: auto-approve the least-standing allow option.\n *\n * \u26A0\uFE0F This auto-approves EVERY tool call, including file writes and shell\n * commands. It exists so non-interactive contexts (CLI `acp spawn`,\n * the Director fan-out) work without a human in the loop. Interactive\n * surfaces (TUI/WebUI) MUST inject a policy that surfaces the request to\n * the user \u2014 pass `permissionPolicy` to `ACPSession` / the subagent runner.\n *\n * NOTE: This is NO LONGER the default for `ACPSession` \u2014 the session now\n * defaults to {@link readOnlyPermissionPolicy} (safe-by-default). Pass this\n * policy explicitly when the agent is trusted and needs write/execute access.\n */\nexport const defaultPermissionPolicy: PermissionPolicy = async (req) => {\n if (req.signal.aborted) return { outcome: 'cancelled' };\n return pickAllow(req.options);\n};\n\n/**\n * Safe-by-default policy: auto-approve only side-effect-free tool calls\n * (read/search/fetch/think); reject anything that would write files or\n * run commands. This is the DEFAULT policy for `ACPSession` when no\n * `permissionPolicy` is provided. Pass {@link defaultPermissionPolicy}\n * explicitly when the agent is trusted and needs write/execute access.\n */\nexport const readOnlyPermissionPolicy: PermissionPolicy = async (req) => {\n if (req.signal.aborted) return { outcome: 'cancelled' };\n const kind = req.toolCall.kind;\n if (kind && READ_ONLY_KINDS.has(kind)) {\n return pickAllow(req.options);\n }\n return pickReject(req.options);\n};\n\n/**\n * Build a policy from a yes/no decision function. The decider receives the\n * tool call (title + kind + rawInput) and returns whether to allow it.\n * This is the seam an interactive host (TUI/WebUI confirm prompt, trust\n * store, exec-allowlist) plugs into.\n */\nexport function makePermissionPolicy(\n decide: (req: PermissionRequest) => boolean | Promise<boolean>,\n): PermissionPolicy {\n return async (req) => {\n if (req.signal.aborted) return { outcome: 'cancelled' };\n const allow = await decide(req);\n return allow ? pickAllow(req.options) : pickReject(req.options);\n };\n}\n", "/**\n * TerminalServer \u2014 answers `terminal/*` methods from an ACP agent.\n *\n * The spec lets agents spawn shell commands inside the client's\n * environment and observe their output. We honour the protocol, but\n * every command runs under a per-process timeout and a byte limit on\n * retained output, both to keep runaway agents from filling memory\n * and to give the runner a clean signal when something is stuck.\n *\n * Scoping: commands run with `cwd` set to the agent's requested cwd\n * if its canonical path is inside `projectRoot`, else `projectRoot`.\n * Agent env entries are overlaid on a credential-scrubbed base, except\n * for variables that enable preload injection or path hijacking.\n */\nimport { spawn } from 'node:child_process';\nimport { realpathSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { buildChildEnv } from '@wrongstack/core/utils';\n\nconst EMPTY_BUFFER = Buffer.alloc(0);\n\nexport interface TerminalServerOptions {\n projectRoot: string;\n /** Hard cap on per-command wall-clock. Default 5 minutes. */\n commandTimeoutMs?: number;\n /** Bytes of output to retain per terminal. Default 1 MiB. */\n outputByteLimit?: number;\n /**\n * Hard maximum cap on the per-call `outputByteLimit`. The agent can request\n * a lower limit per terminal/create, but it can never raise it above this\n * host-configured ceiling. Protects against memory exhaustion from a\n * malicious agent requesting `outputByteLimit: Infinity`. Default 16 MiB.\n */\n maxOutputByteLimit?: number;\n /** Maximum terminal records retained concurrently. Default 32. */\n maxTerminals?: number;\n /** Optional abort signal that kills ALL active terminals. */\n signal?: AbortSignal;\n}\n\ninterface TerminalState {\n proc: ReturnType<typeof spawn>;\n cwd: string;\n command: string;\n args: string[];\n /** Byte chunks retained as a bounded queue. Joined only when output is read. */\n outputChunks: Buffer[];\n /** Index of the first live chunk; avoids O(n) Array.shift calls. */\n outputHead: number;\n /** Bytes currently retained (post-truncation). */\n retainedBytes: number;\n /** True once we've dropped output to fit under the per-call byte limit. */\n truncated: boolean;\n exitStatus?: { exitCode: number | null; signal: string | null } | undefined;\n /** Resolves when the process exits. */\n exitPromise: Promise<{ exitCode: number | null; signal: string | null }>;\n /** Per-terminal timeout handle. */\n timeoutHandle: ReturnType<typeof setTimeout> | null;\n}\n\nexport class TerminalServer {\n private readonly terminals = new Map<string, TerminalState>();\n private readonly projectRoot: string;\n private readonly commandTimeoutMs: number;\n private readonly outputByteLimit: number;\n private readonly maxOutputByteLimit: number;\n private readonly maxTerminals: number;\n private readonly abortSignal: AbortSignal | undefined;\n private readonly abortHandler = (): void => this.releaseAll();\n private nextId = 1;\n\n constructor(opts: TerminalServerOptions) {\n this.projectRoot = path.resolve(opts.projectRoot);\n this.commandTimeoutMs = opts.commandTimeoutMs ?? 5 * 60_000;\n this.outputByteLimit = opts.outputByteLimit ?? 1024 * 1024;\n this.maxOutputByteLimit = opts.maxOutputByteLimit ?? 16 * 1024 * 1024;\n this.maxTerminals = this.clampFiniteInt(opts.maxTerminals, 32);\n this.abortSignal = opts.signal;\n if (opts.signal) {\n opts.signal.addEventListener('abort', this.abortHandler, { once: true });\n }\n }\n\n /** Spawn a new terminal. Returns the agent-facing id. */\n create(params: {\n sessionId: string;\n command: string;\n args?: string[];\n env?: { name: string; value: string }[];\n cwd?: string;\n outputByteLimit?: number;\n }): { terminalId: string } {\n if (this.terminals.size >= this.maxTerminals) {\n throw new Error(\n `terminal limit reached (${this.maxTerminals}); release an existing terminal before creating another`,\n );\n }\n const id = `term_${this.nextId++}`;\n const cwd = this.resolveCwd(params.cwd);\n const perCallByteLimit = Math.min(\n Math.max(1, this.clampFiniteInt(params.outputByteLimit, this.outputByteLimit)),\n this.maxOutputByteLimit,\n );\n const proc = spawn(params.command, params.args ?? [], {\n cwd,\n env: this.buildEnv(params.env),\n stdio: ['ignore', 'pipe', 'pipe'],\n windowsHide: true,\n // shell: false on purpose. The terminal server is invoked with\n // the agent's explicit argv; turning on shell-mode would make\n // the command a single shell-parsed string, which breaks\n // Windows cmd quoting for the common case of running node with\n // `-e \"<script>\"`. If a future feature needs shell features\n // (pipes, redirects), it should be opt-in per-call, not the\n // default.\n });\n\n const state: TerminalState = {\n proc,\n cwd,\n command: params.command,\n args: params.args ?? [],\n outputChunks: [],\n outputHead: 0,\n retainedBytes: 0,\n truncated: false,\n exitStatus: undefined,\n timeoutHandle: null,\n exitPromise: new Promise((resolve) => {\n proc.on('close', (code, signalName) => {\n if (state.timeoutHandle) {\n clearTimeout(state.timeoutHandle);\n state.timeoutHandle = null;\n }\n const exitStatus = {\n exitCode: typeof code === 'number' ? code : null,\n signal: typeof signalName === 'string' ? signalName : null,\n };\n state.exitStatus = exitStatus;\n resolve(exitStatus);\n });\n proc.on('error', (err) => {\n // Spawn-time errors (ENOENT etc.) \u2014 surface as a special\n // exit status with exitCode 127 (command not found).\n if (state.timeoutHandle) {\n clearTimeout(state.timeoutHandle);\n state.timeoutHandle = null;\n }\n const exitStatus = { exitCode: 127, signal: null };\n state.exitStatus = exitStatus;\n let errorOutput = Buffer.from(`[spawn error] ${err.message}\\n`, 'utf8');\n if (errorOutput.length > perCallByteLimit) {\n let start = errorOutput.length - perCallByteLimit;\n while (start < errorOutput.length && (errorOutput[start]! & 0xc0) === 0x80) start++;\n errorOutput = errorOutput.subarray(start);\n state.truncated = true;\n }\n state.outputChunks.push(errorOutput);\n state.retainedBytes = errorOutput.length;\n resolve(exitStatus);\n });\n }),\n };\n\n proc.stdout?.setEncoding('utf8');\n proc.stderr?.setEncoding('utf8');\n const onData = (chunk: string): void => {\n const outputChunk = Buffer.from(chunk, 'utf8');\n state.outputChunks.push(outputChunk);\n state.retainedBytes += outputChunk.length;\n if (state.retainedBytes > perCallByteLimit) state.truncated = true;\n\n // Evict from the head. Each chunk is visited at most twice, so frequent\n // tiny writes do not repeatedly copy the full retained output window.\n while (\n state.retainedBytes > perCallByteLimit &&\n state.outputHead < state.outputChunks.length\n ) {\n const first = state.outputChunks[state.outputHead]!;\n const overflow = state.retainedBytes - perCallByteLimit;\n if (first.length <= overflow) {\n state.outputChunks[state.outputHead] = EMPTY_BUFFER;\n state.outputHead++;\n state.retainedBytes -= first.length;\n continue;\n }\n\n let start = overflow;\n while (start < first.length && (first[start]! & 0xc0) === 0x80) start++;\n state.outputChunks[state.outputHead] = first.subarray(start);\n state.retainedBytes -= start;\n }\n\n if (state.outputHead >= 256 && state.outputHead * 2 >= state.outputChunks.length) {\n state.outputChunks = state.outputChunks.slice(state.outputHead);\n state.outputHead = 0;\n }\n };\n proc.stdout?.on('data', onData);\n proc.stderr?.on('data', onData);\n\n state.timeoutHandle = setTimeout(() => {\n // Best-effort kill; we don't have an exact \"TIMEOUT\" stop reason\n // so we just exit with -1.\n try {\n proc.kill('SIGTERM');\n } catch {\n // already dead\n }\n }, this.commandTimeoutMs);\n\n this.terminals.set(id, state);\n return { terminalId: id };\n }\n\n /** Return captured output and (if available) the exit status. */\n output(terminalId: string): {\n output: string;\n truncated: boolean;\n exitStatus?: { exitCode: number | null; signal: string | null };\n } {\n const state = this.terminals.get(terminalId);\n if (!state) throw new Error(`unknown terminal: ${terminalId}`);\n return {\n output: Buffer.concat(\n state.outputChunks.slice(state.outputHead),\n state.retainedBytes,\n ).toString('utf8'),\n truncated: state.truncated,\n ...(state.exitStatus ? { exitStatus: state.exitStatus } : {}),\n };\n }\n\n /** Block until the process exits. Resolves with the exit status. */\n async waitForExit(\n terminalId: string,\n ): Promise<{ exitCode: number | null; signal: string | null }> {\n const state = this.terminals.get(terminalId);\n if (!state) throw new Error(`unknown terminal: ${terminalId}`);\n return state.exitPromise;\n }\n\n /** Kill the process but keep the terminal record (agent can still read output). */\n kill(terminalId: string): void {\n const state = this.terminals.get(terminalId);\n if (!state) throw new Error(`unknown terminal: ${terminalId}`);\n try {\n state.proc.kill('SIGTERM');\n } catch {\n // already dead\n }\n }\n\n /** Kill the process if alive and remove the record. */\n release(terminalId: string): void {\n const state = this.terminals.get(terminalId);\n if (!state) return;\n if (state.timeoutHandle) {\n clearTimeout(state.timeoutHandle);\n state.timeoutHandle = null;\n }\n try {\n state.proc.kill('SIGKILL');\n } catch {\n // already dead\n }\n this.terminals.delete(terminalId);\n }\n\n /** Kill all active terminals. Used on session close. */\n releaseAll(): void {\n this.abortSignal?.removeEventListener('abort', this.abortHandler);\n for (const id of [...this.terminals.keys()]) {\n this.release(id);\n }\n }\n\n private resolveCwd(cwd: string | undefined): string {\n if (!cwd) return this.projectRoot;\n const resolved = path.resolve(cwd);\n const rootWithSep = this.projectRoot.endsWith(path.sep)\n ? this.projectRoot\n : this.projectRoot + path.sep;\n if (resolved !== this.projectRoot && !resolved.startsWith(rootWithSep)) {\n return this.projectRoot;\n }\n try {\n const realRoot = realpathSync(this.projectRoot);\n const realCwd = realpathSync(resolved);\n const realRootWithSep = realRoot.endsWith(path.sep) ? realRoot : realRoot + path.sep;\n if (realCwd !== realRoot && !realCwd.startsWith(realRootWithSep)) {\n return realRoot;\n }\n return realCwd;\n } catch {\n // A process cannot start in a missing/unresolvable cwd. Fall back to the\n // configured root rather than allowing spawn to fail or follow a bad link.\n return this.projectRoot;\n }\n }\n\n private buildEnv(agentEnv?: { name: string; value: string }[]): NodeJS.ProcessEnv {\n // Use the sanitized child env from @wrongstack/core instead of raw\n // process.env. This strips API keys, tokens, and other credentials from\n // the host environment so a compromised ACP agent cannot exfiltrate them\n // via `terminal/create`. buildChildEnv preserves system/tooling variables\n // (PATH, HOME, LANG, ...) and handles the Windows Path/PATH aliasing.\n const env: NodeJS.ProcessEnv = buildChildEnv();\n if (agentEnv) {\n for (const { name, value } of agentEnv) {\n // Deny agent overrides of environment variables that could re-introduce\n // code injection (NODE_OPTIONS --require/--import/--loader), shared-library\n // preloading (LD_PRELOAD, DYLD_*), or path hijacking (PATH). buildChildEnv\n // already stripped these from the host env; the agent must not be able to\n // add them back.\n const upper = name.toUpperCase();\n if (DENIED_AGENT_ENV_KEYS.has(upper)) continue;\n env[name] = value;\n }\n }\n return env;\n }\n\n /**\n * Clamp an agent-supplied numeric to a finite positive safe integer, falling\n * back to `defaultValue` for undefined/NaN/non-finite values. Prevents\n * negative, NaN, or Infinity values from disabling output caps or causing\n * unbounded memory growth.\n */\n private clampFiniteInt(value: number | undefined, defaultValue: number): number {\n if (value === undefined || !Number.isFinite(value) || value < 1) {\n return defaultValue;\n }\n return Math.trunc(value);\n }\n}\n\n/**\n * Environment variables an ACP agent must NOT be allowed to set, because they\n * can re-introduce code injection or path hijacking after `buildChildEnv`\n * already stripped them from the host env. Checked case-insensitively.\n */\nconst DENIED_AGENT_ENV_KEYS: ReadonlySet<string> = new Set([\n 'NODE_OPTIONS',\n 'LD_PRELOAD',\n 'LD_LIBRARY_PATH',\n 'DYLD_INSERT_LIBRARIES',\n 'DYLD_LIBRARY_PATH',\n 'DYLD_FALLBACK_LIBRARY_PATH',\n 'PATH',\n 'PYTHONPATH',\n 'PYTHONSTARTUP',\n 'PERL5OPT',\n 'PERLLIB',\n 'RUBYOPT',\n 'RUBYLIB',\n]);\n", "import type {\n TrustActor,\n TrustAuthContext,\n TrustBoundary,\n TrustBoundaryDecision,\n TrustBoundaryRequest,\n TrustRisk,\n TrustScope,\n TrustSubject,\n} from '@wrongstack/core/security';\nimport type { PermissionOption, RequestPermissionOutcome, ToolKind } from '../types/acp-v1.js';\nimport type { PermissionPolicy, PermissionRequest } from './permission.js';\n\nexport interface ACPTrustBoundaryAdapterOptions {\n boundary: TrustBoundary;\n actor?: TrustActor | undefined;\n scope?: TrustScope | undefined;\n authContext?: TrustAuthContext | undefined;\n}\n\nfunction pickOption(\n options: readonly PermissionOption[],\n allowed: boolean,\n): RequestPermissionOutcome {\n const kinds = allowed ? ['allow_once', 'allow_always'] : ['reject_once', 'reject_always'];\n for (const kind of kinds) {\n const option = options.find((candidate) => candidate.kind === kind);\n if (option) return { outcome: 'selected', optionId: option.optionId };\n }\n return { outcome: 'cancelled' };\n}\n\nfunction riskFor(kind: ToolKind | undefined): TrustRisk {\n if (kind === 'read' || kind === 'search' || kind === 'fetch' || kind === 'think') return 'low';\n if (kind === 'edit' || kind === 'move') return 'elevated';\n if (kind === 'delete' || kind === 'execute') return 'high';\n return 'elevated';\n}\n\nfunction capabilityFor(request: PermissionRequest): string {\n const raw = request.toolCall.rawInput;\n if (typeof raw?.path === 'string') {\n return request.toolCall.kind === 'read' || request.toolCall.kind === 'search'\n ? 'filesystem.read'\n : 'filesystem.write';\n }\n if (typeof raw?.command === 'string' || request.toolCall.kind === 'execute')\n return 'process.spawn';\n if (request.toolCall.kind === 'fetch') return 'network.fetch';\n return `tool.${request.toolCall.kind ?? 'unknown'}`;\n}\n\nfunction subjectFor(request: PermissionRequest): TrustSubject {\n const raw = request.toolCall.rawInput;\n const title = request.toolCall.title ?? `ACP tool call ${String(request.toolCall.toolCallId)}`;\n if (typeof raw?.path === 'string') {\n return { kind: 'path', id: raw.path, attributes: { toolKind: request.toolCall.kind ?? null } };\n }\n if (typeof raw?.command === 'string') {\n return {\n kind: 'command',\n id: raw.command,\n attributes: { toolKind: request.toolCall.kind ?? null },\n };\n }\n return {\n kind: 'resource',\n id: title,\n attributes: { toolKind: request.toolCall.kind ?? null },\n };\n}\n\nfunction isAllowed(decision: TrustBoundaryDecision): boolean {\n return decision.kind === 'allow' || decision.kind === 'scoped-token';\n}\n\nexport function toTrustBoundaryRequest(\n request: PermissionRequest,\n options: Omit<ACPTrustBoundaryAdapterOptions, 'boundary'>,\n): TrustBoundaryRequest {\n const rawSessionId = request.toolCall.rawInput?.sessionId;\n const sessionId =\n typeof rawSessionId === 'string' && rawSessionId.length > 0\n ? rawSessionId\n : options.actor?.sessionId;\n return {\n version: 1,\n requestId: String(request.toolCall.toolCallId),\n actor: {\n ...(options.actor ?? { kind: 'agent' as const }),\n ...(sessionId ? { sessionId } : {}),\n },\n surface: 'acp',\n capability: capabilityFor(request),\n subject: subjectFor(request),\n risk: riskFor(request.toolCall.kind),\n scope: {\n ...(options.scope ?? {}),\n ...(sessionId ? { sessionId } : {}),\n },\n ...(options.authContext ? { authContext: options.authContext } : {}),\n metadata: {\n ...(request.toolCall.title ? { title: request.toolCall.title } : {}),\n toolKind: request.toolCall.kind ?? null,\n },\n };\n}\n\n/**\n * Adapts ACP permission callbacks to the shared TrustBoundary authority.\n * `confirm` remains denied here: interactive confirmation belongs in the\n * boundary's host adapter, which must return a final `allow` after consent.\n */\nexport function makeTrustBoundaryPermissionPolicy(\n options: ACPTrustBoundaryAdapterOptions,\n): PermissionPolicy {\n return async (request) => {\n if (request.signal.aborted) return { outcome: 'cancelled' };\n const decision = await options.boundary.evaluate(toTrustBoundaryRequest(request, options));\n if (request.signal.aborted) return { outcome: 'cancelled' };\n return pickOption(request.options, isAllowed(decision));\n };\n}\n\n/** Direct-module test seam; not re-exported by the package barrel. */\nexport const trustBoundaryPermissionCoverage = {\n pickOption,\n riskFor,\n capabilityFor,\n subjectFor,\n isAllowed,\n};\n", "/**\n * WebSocketClientTransport \u2014 remote ACP transport for `ACPSession`.\n *\n * Connects to a remote ACP agent over a WebSocket (cloud-hosted agents,\n * separate-process agents reachable over the network). Each WebSocket\n * message carries exactly one JSON-RPC 2.0 object \u2014 message boundaries\n * are preserved by the WS framing, so (unlike stdio) no newline delimiter\n * is needed.\n *\n * Uses the Node \u2265 22 built-in global `WebSocket` (undici), so there is no\n * runtime dependency. Per-connection auth headers are not supported by the\n * WHATWG WebSocket client; authenticate over the protocol instead\n * (`ACPSession.authenticate`) or embed a token in the URL query string.\n *\n * Spec: https://agentclientprotocol.com/protocol/v1/overview (remote transport)\n */\n\nimport type { ACPClientTransport } from '../agent/stdio-transport.js';\nimport type { ACPMessage } from '../types/acp-messages.js';\n\nexport interface WebSocketClientTransportOptions {\n /** ws:// or wss:// URL of the remote ACP agent. */\n url: string;\n /** Optional WebSocket subprotocols. */\n protocols?: string | string[] | undefined;\n /** How long to wait for the socket to open. Default 30s. */\n handshakeTimeoutMs?: number | undefined;\n /** Maximum unsent bytes retained by the WebSocket implementation. Default 32 MiB. */\n maxBufferedBytes?: number | undefined;\n /** Maximum inbound message size in characters. Default 20 MiB. */\n maxMessageChars?: number | undefined;\n}\n\n/** Narrow view of the global WebSocket we rely on (avoids lib.dom typings). */\ninterface WSLike {\n readonly bufferedAmount?: number;\n send(data: string): void;\n close(): void;\n addEventListener(type: 'open', cb: () => void): void;\n addEventListener(type: 'error', cb: (ev: unknown) => void): void;\n addEventListener(type: 'close', cb: () => void): void;\n addEventListener(type: 'message', cb: (ev: { data: unknown }) => void): void;\n}\n\ntype WSConstructor = new (url: string, protocols?: string | string[]) => WSLike;\n\nexport class WebSocketClientTransport implements ACPClientTransport {\n private ws: WSLike | null = null;\n private readonly handlers = new Set<(msg: ACPMessage) => void>();\n private closed = false;\n private readonly opts: WebSocketClientTransportOptions;\n private readonly maxBufferedBytes: number;\n private readonly maxMessageChars: number;\n\n constructor(opts: WebSocketClientTransportOptions) {\n this.opts = opts;\n this.maxBufferedBytes = finitePositiveLimit(opts.maxBufferedBytes, 32 * 1024 * 1024);\n this.maxMessageChars = finitePositiveLimit(opts.maxMessageChars, 20 * 1024 * 1024);\n }\n\n start(): Promise<void> {\n const WS = (globalThis as { WebSocket?: WSConstructor }).WebSocket;\n if (!WS) {\n return Promise.reject(\n new Error(\n 'global WebSocket is not available \u2014 Node \u2265 22 is required for the remote ACP transport',\n ),\n );\n }\n const timeoutMs = this.opts.handshakeTimeoutMs ?? 30_000;\n return new Promise<void>((resolve, reject) => {\n let settled = false;\n const ws = new WS(this.opts.url, this.opts.protocols);\n this.ws = ws;\n const timer = setTimeout(() => {\n settled = true;\n try {\n ws.close();\n } catch {\n // ignore\n }\n reject(new Error(`WebSocket failed to open within ${timeoutMs}ms`));\n }, timeoutMs);\n\n ws.addEventListener('open', () => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve();\n });\n ws.addEventListener('error', (ev: unknown) => {\n if (settled) {\n // Post-open errors just tear the connection down.\n this.closed = true;\n return;\n }\n settled = true;\n clearTimeout(timer);\n const message =\n ev && typeof ev === 'object' && 'message' in ev\n ? String((ev as { message: unknown }).message)\n : 'WebSocket error';\n reject(new Error(message));\n });\n ws.addEventListener('close', () => {\n this.closed = true;\n });\n ws.addEventListener('message', (ev: { data: unknown }) => {\n this.onData(ev.data);\n });\n });\n }\n\n send(msg: ACPMessage): Promise<void> {\n if (this.closed || !this.ws) {\n return Promise.reject(new Error('WebSocket transport is not open'));\n }\n try {\n const serialized = JSON.stringify(msg);\n const buffered = Number.isFinite(this.ws.bufferedAmount)\n ? (this.ws.bufferedAmount as number)\n : 0;\n if (buffered + Buffer.byteLength(serialized, 'utf8') > this.maxBufferedBytes) {\n this.stop();\n return Promise.reject(new Error('WebSocket transport send buffer limit exceeded'));\n }\n this.ws.send(serialized);\n return Promise.resolve();\n } catch (err) {\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n onMessage(handler: (msg: ACPMessage) => void): () => void {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n stop(): void {\n this.closed = true;\n if (this.ws) {\n try {\n this.ws.close();\n } catch {\n // already closed\n }\n this.ws = null;\n }\n }\n\n private onData(data: unknown): void {\n const text =\n typeof data === 'string'\n ? data\n : data instanceof ArrayBuffer\n ? Buffer.from(data).toString('utf8')\n : Buffer.isBuffer(data)\n ? data.toString('utf8')\n : String(data);\n if (text.length > this.maxMessageChars) {\n this.stop();\n return;\n }\n if (!text.trim()) return;\n let msg: ACPMessage;\n try {\n msg = JSON.parse(text) as ACPMessage;\n } catch {\n // A remote agent that frames multiple JSON objects per message is\n // non-conformant; try newline-splitting as a fallback before dropping.\n for (const line of text.split('\\n')) {\n if (!line.trim()) continue;\n try {\n this.dispatch(JSON.parse(line) as ACPMessage);\n } catch {\n // skip malformed fragment\n }\n }\n return;\n }\n this.dispatch(msg);\n }\n\n private dispatch(msg: ACPMessage): void {\n for (const handler of [...this.handlers]) {\n try {\n handler(msg);\n } catch {\n // a faulty consumer must not break the socket pump\n }\n }\n }\n}\n\nfunction finitePositiveLimit(value: number | undefined, fallback: number): number {\n return value !== undefined && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;\n}\n", "/**\n * ACPSession \u2014 v1-correct ACP client.\n *\n * Owns one child process running an ACP-supporting agent (Claude Code,\n * Gemini CLI, Codex CLI, etc.) and translates the wire protocol into\n * a `SubagentRunner`-shaped surface for the rest of WrongStack.\n *\n * Spec: https://agentclientprotocol.com/protocol/v1/overview\n * Design: see ./acp-session.design.md in this directory.\n */\nimport type {\n TrustActor,\n TrustAuthContext,\n TrustBoundary,\n TrustScope,\n} from '@wrongstack/core/security';\nimport { type ACPClientTransport, ClientTransport } from '../agent/stdio-transport.js';\nimport type { ACPMessage } from '../types/acp-messages.js';\nimport {\n ACP_PROTOCOL_VERSION,\n type AgentCapabilities,\n type AnySessionUpdate,\n type AuthMethod,\n type ContentBlock,\n type McpServer,\n type PlanEntry,\n type SessionId,\n type SessionInfo,\n type StopReason,\n type ToolCallContent,\n type ToolCallStatus,\n type ToolCallUpdateNotification,\n type ToolKind,\n type UsageCost,\n} from '../types/acp-v1.js';\nimport { FileServer, FsError } from './file-server.js';\nimport { type PermissionPolicy, readOnlyPermissionPolicy } from './permission.js';\nimport { TerminalServer } from './terminal-server.js';\nimport { makeTrustBoundaryPermissionPolicy } from './trust-boundary-permission.js';\nimport {\n WebSocketClientTransport,\n type WebSocketClientTransportOptions,\n} from './websocket-transport.js';\n\nexport interface ACPSessionOptions {\n command: string;\n args?: readonly string[] | undefined;\n env?: Record<string, string> | undefined;\n cwd?: string | undefined;\n role?: string | undefined;\n /** Sandbox root for fs/* and terminal/* methods. */\n projectRoot: string;\n /** Hard timeout for one prompt turn. Default 5 minutes. */\n timeoutMs?: number | undefined;\n /** Override the permission policy. */\n permissionPolicy?: PermissionPolicy | undefined;\n /** Shared authorization authority. Mutually exclusive with `permissionPolicy`. */\n trustBoundary?: TrustBoundary | undefined;\n /** Actor identity supplied to `trustBoundary`. Defaults to an ACP agent. */\n trustActor?: TrustActor | undefined;\n /** Base scope supplied to `trustBoundary`; callback session IDs are merged in. */\n trustScope?: TrustScope | undefined;\n /** Authentication evidence supplied to `trustBoundary`. */\n trustAuthContext?: TrustAuthContext | undefined;\n /** Per-fs-call timeout, default 30s. */\n fsTimeoutMs?: number | undefined;\n /** Per-terminal command timeout, default 5 minutes. */\n terminalTimeoutMs?: number | undefined;\n /** Per-terminal output byte cap, default 1 MiB. */\n terminalOutputByteLimit?: number | undefined;\n /** Maximum terminal records retained concurrently, default 32. */\n terminalMaxCount?: number | undefined;\n /**\n * MCP server configs to include in session/new, session/load, and\n * session/resume. The agent will connect to these servers to provide\n * additional tools.\n *\n * Stdio servers are always sent. HTTP/SSE servers are only sent if\n * the agent advertises the corresponding mcpCapabilities.\n */\n mcpServers?: McpServer[] | undefined;\n}\n\n/**\n * A captured file diff emitted by the agent during a turn (via a tool\n * call's `diff` content). `oldText: null` means the file was created.\n */\nexport interface ACPCapturedDiff {\n path: string;\n oldText: string | null;\n newText: string;\n}\n\n/**\n * A captured tool call the agent ran during a turn. We collapse the\n * `tool_call` + subsequent `tool_call_update` notifications for the same\n * `toolCallId` into one record carrying its latest status.\n */\nexport interface ACPCapturedToolCall {\n toolCallId: string;\n title: string;\n kind?: ToolKind | undefined;\n status: ToolCallStatus;\n /** Terminal/command output or text content surfaced by the tool, if any. */\n rawOutput?: Record<string, unknown> | undefined;\n rawInput?: Record<string, unknown> | undefined;\n}\n\nexport interface ACPSessionRunResult {\n text: string;\n stopReason: StopReason;\n hasText: boolean;\n usage?: { used: number; size: number; cost?: UsageCost | undefined } | undefined;\n plan?: PlanEntry[] | undefined;\n /** Tool calls the agent ran this turn (deduped by toolCallId). */\n toolCalls: ACPCapturedToolCall[];\n /** File diffs the agent produced this turn. */\n diffs: ACPCapturedDiff[];\n /** Agent \"thinking\" text emitted via thought_chunk, concatenated. */\n thoughts: string;\n}\n\n/**\n * Live progress callback. Invoked for every `session/update` notification\n * the agent streams during a `prompt()` turn, in arrival order, BEFORE the\n * turn resolves. Lets the host render tool activity / text deltas / diffs\n * as they happen instead of waiting for the buffered final result.\n *\n * The raw `update` (the discriminated `session/update` payload) is passed\n * through verbatim so callers can switch on `update.sessionUpdate`.\n */\nexport type ACPProgressHandler = (event: ACPProgressEvent) => void;\n\nexport type ACPProgressEvent =\n | { type: 'message'; text: string }\n | { type: 'thought'; text: string }\n | { type: 'tool_call'; toolCall: ACPCapturedToolCall }\n | { type: 'tool_call_update'; toolCall: ACPCapturedToolCall }\n | { type: 'diff'; diff: ACPCapturedDiff }\n | { type: 'plan'; entries: PlanEntry[] }\n | { type: 'usage'; usage: { used: number; size: number; cost?: UsageCost | undefined } }\n | { type: 'raw'; update: AnySessionUpdate };\n\nexport type ACPSessionErrorKind =\n | 'spawn_failed'\n | 'init_failed'\n | 'protocol_error'\n | 'session_create_failed'\n | 'prompt_failed'\n | 'auth_failed'\n | 'logout_failed'\n | 'aborted'\n | 'closed'\n | 'agent_died'\n | 'unsupported_capability';\n\nexport class ACPSessionError extends Error {\n readonly kind: ACPSessionErrorKind;\n override readonly cause: unknown;\n constructor(kind: ACPSessionErrorKind, message: string, cause?: unknown) {\n super(message);\n this.name = 'ACPSessionError';\n this.kind = kind;\n this.cause = cause;\n }\n}\n\ninterface PendingRequest {\n method: string;\n resolve: (v: unknown) => void;\n reject: (e: Error) => void;\n timeoutMs: number;\n timeoutHandle: ReturnType<typeof setTimeout>;\n}\n\ntype State = 'init' | 'ready' | 'authenticated' | 'sessioning' | 'prompting' | 'done' | 'closed';\n\ninterface JsonRpcError {\n code: number;\n message: string;\n data?: unknown;\n}\n\nfunction isJsonRpcError(v: unknown): v is JsonRpcError {\n return (\n typeof v === 'object' &&\n v !== null &&\n typeof (v as { code?: unknown }).code === 'number' &&\n typeof (v as { message?: unknown }).message === 'string'\n );\n}\n\nexport class ACPSession {\n private readonly transport: ACPClientTransport;\n private readonly fileServer: FileServer;\n private readonly terminalServer: TerminalServer;\n private readonly permissionPolicy: PermissionPolicy;\n private readonly timeoutMs: number;\n private readonly opts: ACPSessionOptions;\n private transportOff: (() => void) | null = null;\n\n private state: State = 'init';\n private sessionId: SessionId | null = null;\n /** Pending outbound requests (initialize, session/new, session/prompt, etc). */\n private readonly pending = new Map<string | number, PendingRequest>();\n private nextId = 1;\n /** True after close() has been called. */\n private closed = false;\n\n // Agent-provided info from the initialize handshake\n private agentCapabilities: AgentCapabilities = {};\n private agentInfo: { name: string; title?: string | undefined; version: string } | null = null;\n private authMethods: AuthMethod[] = [];\n /** Protocol version negotiated with the agent during initialize. */\n private negotiatedVersion: number = ACP_PROTOCOL_VERSION;\n\n private constructor(opts: ACPSessionOptions, transport: ACPClientTransport) {\n this.opts = opts;\n this.transport = transport;\n this.timeoutMs = opts.timeoutMs ?? 5 * 60_000;\n const fsOpts: ConstructorParameters<typeof FileServer>[0] = {\n projectRoot: opts.projectRoot,\n };\n if (opts.fsTimeoutMs !== undefined) fsOpts.timeoutMs = opts.fsTimeoutMs;\n this.fileServer = new FileServer(fsOpts);\n const termOpts: ConstructorParameters<typeof TerminalServer>[0] = {\n projectRoot: opts.projectRoot,\n };\n if (opts.terminalTimeoutMs !== undefined) {\n termOpts.commandTimeoutMs = opts.terminalTimeoutMs;\n }\n if (opts.terminalOutputByteLimit !== undefined) {\n termOpts.outputByteLimit = opts.terminalOutputByteLimit;\n }\n if (opts.terminalMaxCount !== undefined) {\n termOpts.maxTerminals = opts.terminalMaxCount;\n }\n this.terminalServer = new TerminalServer(termOpts);\n if (opts.permissionPolicy && opts.trustBoundary) {\n throw new TypeError('permissionPolicy and trustBoundary are mutually exclusive');\n }\n this.permissionPolicy = opts.trustBoundary\n ? makeTrustBoundaryPermissionPolicy({\n boundary: opts.trustBoundary,\n ...(opts.trustActor ? { actor: opts.trustActor } : {}),\n scope: opts.trustScope ?? { cwd: opts.projectRoot },\n ...(opts.trustAuthContext ? { authContext: opts.trustAuthContext } : {}),\n })\n : (opts.permissionPolicy ?? readOnlyPermissionPolicy);\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Public accessors\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Agent capabilities advertised during initialize. */\n getCapabilities(): AgentCapabilities {\n return { ...this.agentCapabilities };\n }\n\n /** Authentication methods advertised by the agent. */\n getAuthMethods(): AuthMethod[] {\n return [...this.authMethods];\n }\n\n /** Agent info (name, title, version) from initialize. */\n getAgentInfo(): { name: string; title?: string | undefined; version: string } | null {\n return this.agentInfo;\n }\n\n /** Whether the agent requires authentication (has auth methods). */\n requiresAuth(): boolean {\n return this.authMethods.length > 0;\n }\n\n /** Current session id, if one exists. */\n getSessionId(): SessionId | null {\n return this.sessionId;\n }\n\n /** Protocol version negotiated during initialize. */\n getNegotiatedVersion(): number {\n return this.negotiatedVersion;\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Lifecycle \u2014 start\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Spawn the child, run the initialize handshake, install the\n * message dispatch, and return a ready session.\n */\n static async start(opts: ACPSessionOptions): Promise<ACPSession> {\n const transportOpts: ConstructorParameters<typeof ClientTransport>[0] = {\n command: opts.command,\n args: opts.args ? [...opts.args] : [],\n handshakeTimeoutMs: 30_000,\n skipHandshakeMarker: true,\n };\n if (opts.env !== undefined) transportOpts.env = opts.env;\n if (opts.cwd !== undefined) transportOpts.cwd = opts.cwd;\n const transport = new ClientTransport(transportOpts);\n return ACPSession.attach(opts, transport, `failed to spawn ${opts.command}`);\n }\n\n /**\n * Connect to a REMOTE ACP agent over a WebSocket instead of spawning a\n * local subprocess. `opts.command` is ignored for the wire (a label is\n * still useful for `role`); everything else (projectRoot sandbox for\n * fs/terminal, timeouts, permission policy, MCP servers) applies the same.\n */\n static async connectWebSocket(\n wsOpts: WebSocketClientTransportOptions,\n opts: ACPSessionOptions,\n ): Promise<ACPSession> {\n const transport = new WebSocketClientTransport(wsOpts);\n return ACPSession.attach(opts, transport, `failed to connect to ${wsOpts.url}`);\n }\n\n /**\n * Connect using a caller-supplied transport. Lets advanced callers plug\n * in their own wire (SDK streams, in-process pipes, test doubles).\n */\n static async connect(\n transport: ACPClientTransport,\n opts: ACPSessionOptions,\n ): Promise<ACPSession> {\n return ACPSession.attach(opts, transport, 'failed to connect transport');\n }\n\n /** Shared connect path: start the transport, install dispatch, handshake. */\n private static async attach(\n opts: ACPSessionOptions,\n transport: ACPClientTransport,\n spawnErrLabel: string,\n ): Promise<ACPSession> {\n try {\n await transport.start();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new ACPSessionError('spawn_failed', `${spawnErrLabel}: ${msg}`, err);\n }\n\n const session = new ACPSession(opts, transport);\n session.transportOff = transport.onMessage((msg) => session.handleMessage(msg));\n\n try {\n await session.initialize();\n } catch (err) {\n session.transportOff?.();\n session.transportOff = null;\n try {\n transport.stop();\n } catch {\n // best effort\n }\n throw err;\n }\n return session;\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Initialization\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async initialize(): Promise<void> {\n const id = this.allocId();\n const result = await this.sendRequest(id, 'initialize', {\n protocolVersion: ACP_PROTOCOL_VERSION,\n clientCapabilities: {\n fs: { readTextFile: true, writeTextFile: true },\n terminal: true,\n },\n clientInfo: { name: 'wrongstack', title: 'WrongStack', version: '0.287.0' },\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('init_failed', `initialize failed: ${result.message}`, result);\n }\n if (\n typeof result !== 'object' ||\n result === null ||\n typeof (result as { protocolVersion?: unknown }).protocolVersion !== 'number'\n ) {\n throw new ACPSessionError('protocol_error', 'initialize returned no protocolVersion');\n }\n const r = result as {\n protocolVersion: number;\n agentCapabilities?: AgentCapabilities;\n agentInfo?: { name: string; title?: string | undefined; version: string };\n authMethods?: AuthMethod[];\n };\n // Negotiation per spec: the client advertises its latest supported\n // version; the agent replies with the version both will use \u2014 the\n // client's if the agent supports it, otherwise the agent's own latest.\n // We therefore accept any version <= ours (we can speak it) and only\n // reject a version HIGHER than we support (the agent demands a protocol\n // we don't implement). Equal is the common path.\n if (r.protocolVersion > ACP_PROTOCOL_VERSION) {\n throw new ACPSessionError(\n 'unsupported_capability',\n `agent requires protocolVersion=${r.protocolVersion}, client supports up to ${ACP_PROTOCOL_VERSION}`,\n );\n }\n this.negotiatedVersion = r.protocolVersion;\n // Store agent metadata\n this.agentCapabilities = r.agentCapabilities ?? {};\n this.agentInfo = r.agentInfo ?? null;\n this.authMethods = r.authMethods ?? [];\n this.state = 'ready';\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Authentication\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Authenticate with the agent using one of the advertised auth methods.\n * Call this AFTER start() and BEFORE any session/new call.\n *\n * Throws ACPSessionError('auth_failed') if the agent rejects the\n * authentication or if the methodId is not in the advertised list.\n */\n async authenticate(methodId: string): Promise<void> {\n if (this.state === 'closed') {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (this.state !== 'ready') {\n throw new ACPSessionError(\n 'protocol_error',\n `authenticate called in state=${this.state} (expected 'ready')`,\n );\n }\n if (!this.authMethods.some((m) => m.id === methodId)) {\n throw new ACPSessionError(\n 'auth_failed',\n `auth method \"${methodId}\" not in advertised methods: ${this.authMethods.map((m) => m.id).join(', ')}`,\n );\n }\n\n const id = this.allocId();\n const result = await this.sendRequest(id, 'authenticate', { methodId });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('auth_failed', `authenticate failed: ${result.message}`, result);\n }\n this.state = 'authenticated';\n }\n\n /**\n * Log out from the current authenticated session.\n * Only callable if the agent advertises `auth.logout` capability.\n */\n async logout(): Promise<void> {\n if (this.state === 'closed') {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (!this.agentCapabilities.auth?.logout) {\n throw new ACPSessionError(\n 'unsupported_capability',\n 'agent does not support logout (auth.logout capability not advertised)',\n );\n }\n\n const id = this.allocId();\n const result = await this.sendRequest(id, 'logout', {});\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('logout_failed', `logout failed: ${result.message}`, result);\n }\n this.state = 'ready';\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Session management\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Load an existing session. The agent replays the conversation history\n * via session/update notifications before responding.\n *\n * Only works if the agent advertises `loadSession` capability.\n *\n * @param sessionId - The session to load\n * @param mcpServers - Optional MCP servers (defaults to options.mcpServers)\n * @param cwd - Optional working directory (defaults to options.cwd or projectRoot)\n */\n async loadSession(sessionId: SessionId, mcpServers?: McpServer[], cwd?: string): Promise<void> {\n if (this.closed) {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (!this.agentCapabilities.loadSession) {\n throw new ACPSessionError(\n 'unsupported_capability',\n 'agent does not support session/load (loadSession capability not advertised)',\n );\n }\n if (this.sessionId) {\n // Close current session first\n await this.closeSession();\n }\n\n this.resetScratch();\n const servers = this.filterMcpServers(mcpServers ?? this.opts.mcpServers);\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/load', {\n sessionId,\n cwd: cwd ?? this.opts.cwd ?? this.opts.projectRoot,\n mcpServers: servers,\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('prompt_failed', `session/load failed: ${result.message}`, result);\n }\n this.sessionId = sessionId;\n }\n\n /**\n * Resume an existing session without replaying history.\n *\n * Only works if the agent advertises `sessionCapabilities.resume`.\n *\n * @param sessionId - The session to resume\n * @param mcpServers - Optional MCP servers (defaults to options.mcpServers)\n * @param cwd - Optional working directory (defaults to options.cwd or projectRoot)\n */\n async resumeSession(sessionId: SessionId, mcpServers?: McpServer[], cwd?: string): Promise<void> {\n if (this.closed) {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (!this.agentCapabilities.sessionCapabilities?.resume) {\n throw new ACPSessionError(\n 'unsupported_capability',\n 'agent does not support session/resume (sessionCapabilities.resume not advertised)',\n );\n }\n if (this.sessionId) {\n await this.closeSession();\n }\n\n const servers = this.filterMcpServers(mcpServers ?? this.opts.mcpServers);\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/resume', {\n sessionId,\n cwd: cwd ?? this.opts.cwd ?? this.opts.projectRoot,\n mcpServers: servers,\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `session/resume failed: ${result.message}`,\n result,\n );\n }\n this.sessionId = sessionId;\n }\n\n /**\n * List existing sessions known to the agent.\n *\n * Only works if the agent advertises `sessionCapabilities.list`.\n */\n async listSessions(\n cursor?: string,\n cwd?: string,\n ): Promise<{ sessions: SessionInfo[]; nextCursor?: string | undefined }> {\n if (this.closed) {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (!this.agentCapabilities.sessionCapabilities?.list) {\n throw new ACPSessionError(\n 'unsupported_capability',\n 'agent does not support session/list (sessionCapabilities.list not advertised)',\n );\n }\n\n const id = this.allocId();\n const params: Record<string, unknown> = {};\n if (cursor !== undefined) params.cursor = cursor;\n if (cwd !== undefined) params.cwd = cwd;\n const result = await this.sendRequest(id, 'session/list', params);\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('prompt_failed', `session/list failed: ${result.message}`, result);\n }\n const r = result as { sessions?: SessionInfo[]; nextCursor?: string };\n return {\n sessions: r.sessions ?? [],\n nextCursor: r.nextCursor,\n };\n }\n\n /**\n * Delete a session from the agent's session list.\n *\n * Only works if the agent advertises `sessionCapabilities.delete`.\n */\n async deleteSession(sessionId: SessionId): Promise<void> {\n if (this.closed) {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (!this.agentCapabilities.sessionCapabilities?.delete) {\n throw new ACPSessionError(\n 'unsupported_capability',\n 'agent does not support session/delete (sessionCapabilities.delete not advertised)',\n );\n }\n\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/delete', { sessionId });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `session/delete failed: ${result.message}`,\n result,\n );\n }\n\n if (this.sessionId === sessionId) {\n this.sessionId = null;\n }\n }\n\n /**\n * Fork a session \u2014 create a new session from an existing one.\n */\n async forkSession(\n sourceSessionId: SessionId,\n cwd?: string,\n mcpServers?: McpServer[],\n ): Promise<SessionId> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n\n const servers = this.filterMcpServers(mcpServers ?? this.opts.mcpServers);\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/fork', {\n sessionId: sourceSessionId,\n cwd: cwd ?? this.opts.cwd ?? this.opts.projectRoot,\n ...(servers.length > 0 ? { mcpServers: servers } : {}),\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('prompt_failed', `session/fork failed: ${result.message}`, result);\n }\n const newId = (result as { sessionId?: unknown }).sessionId;\n if (typeof newId !== 'string' || !newId) {\n throw new ACPSessionError('protocol_error', 'session/fork returned no sessionId', result);\n }\n return newId as SessionId;\n }\n\n /**\n * Set the active mode for a session.\n */\n async setMode(sessionId: SessionId, modeId: string): Promise<void> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/set_mode', { sessionId, modeId });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `session/set_mode failed: ${result.message}`,\n result,\n );\n }\n }\n\n /**\n * Set a configuration option for a session.\n */\n async setConfigOption(sessionId: SessionId, configId: string, value: string): Promise<void> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/set_config_option', {\n sessionId,\n configId,\n value,\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `session/set_config_option failed: ${result.message}`,\n result,\n );\n }\n }\n\n /**\n * List available providers and the current provider.\n */\n async listProviders(): Promise<{ providers: unknown[]; currentProviderId: string | null }> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'providers/list', {});\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `providers/list failed: ${result.message}`,\n result,\n );\n }\n const r = result as { providers?: unknown[]; currentProviderId?: string | null };\n return { providers: r.providers ?? [], currentProviderId: r.currentProviderId ?? null };\n }\n\n /**\n * Send an MCP message to the agent for routing.\n */\n async mcpMessage(connectionId: string, message: Record<string, unknown>): Promise<unknown> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'mcp/message', { connectionId, message });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('prompt_failed', `mcp/message failed: ${result.message}`, result);\n }\n return result;\n }\n\n /**\n * Set the active provider for the agent.\n */\n async setProvider(providerId: string, config?: Record<string, unknown>): Promise<void> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'providers/set', { providerId, ...(config ?? {}) });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('prompt_failed', `providers/set failed: ${result.message}`, result);\n }\n }\n\n /**\n * Disable the current provider.\n */\n async disableProvider(): Promise<void> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'providers/disable', {});\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `providers/disable failed: ${result.message}`,\n result,\n );\n }\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Prompt\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Run one prompt turn. Creates a session if needed, sends the\n * prompt, streams session/update notifications, and resolves with\n * the agent's response.\n *\n * @param blocks - Content blocks to send. Use `textContent()` for plain\n * text, or include ImageContent/AudioContent if the agent's\n * `promptCapabilities` allow it.\n * @param signal - AbortSignal for cancellation.\n *\n * Cancellation: if `signal` aborts mid-prompt, we send\n * `session/cancel` (a notification per spec) and keep accepting\n * updates until the agent returns with `stopReason: 'cancelled'`.\n * The result is the same shape as a normal turn, with\n * `stopReason === 'cancelled'`.\n */\n async prompt(\n blocks: ContentBlock[],\n signal: AbortSignal,\n onProgress?: ACPProgressHandler,\n ): Promise<ACPSessionRunResult> {\n if (this.closed) {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (this.state !== 'ready' && this.state !== 'authenticated' && this.state !== 'done') {\n throw new ACPSessionError('protocol_error', `prompt called in state=${this.state}`);\n }\n\n // Pre-aborted signals short-circuit BEFORE we create a session\n // and before any wire activity.\n if (signal.aborted) {\n return emptyRunResult('cancelled');\n }\n\n if (!this.sessionId) {\n await this.createSession();\n }\n\n this.resetScratch();\n this.progressHandler = onProgress ?? null;\n\n const promptId = this.allocId();\n const turnPromise = this.sendRequest(\n promptId,\n 'session/prompt',\n {\n sessionId: this.sessionId,\n prompt: blocks,\n },\n this.timeoutMs,\n );\n\n let cancelled = false;\n const onAbort = (): void => {\n cancelled = true;\n this.transport\n .send({\n jsonrpc: '2.0',\n method: 'session/cancel',\n params: { sessionId: this.sessionId },\n } as never as ACPMessage)\n .catch(() => {\n // transport may already be torn down \u2014 ignore\n });\n };\n signal.addEventListener('abort', onAbort, { once: true });\n\n this.state = 'prompting';\n let response: unknown;\n try {\n response = await turnPromise;\n } catch (err) {\n this.state = 'done';\n signal.removeEventListener('abort', onAbort);\n if (cancelled || signal.aborted) {\n throw new ACPSessionError('aborted', 'prompt was aborted by the parent');\n }\n const msg = err instanceof Error ? err.message : String(err);\n throw new ACPSessionError('prompt_failed', `session/prompt failed: ${msg}`, err);\n } finally {\n signal.removeEventListener('abort', onAbort);\n this.progressHandler = null;\n }\n\n this.state = 'done';\n if (isJsonRpcError(response)) {\n throw new ACPSessionError('prompt_failed', `agent error: ${response.message}`, response);\n }\n const stopReason = (response as { stopReason?: StopReason }).stopReason ?? 'end_turn';\n const finalText = this.scratch.text;\n return {\n text: finalText,\n stopReason,\n hasText: finalText.length > 0,\n usage: this.scratch.usage,\n plan: this.scratch.plan,\n toolCalls: [...this.scratch.toolCalls.values()],\n diffs: this.scratch.diffs,\n thoughts: this.scratch.thoughts,\n };\n }\n\n private async createSession(): Promise<void> {\n const servers = this.filterMcpServers(this.opts.mcpServers);\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/new', {\n cwd: this.opts.cwd ?? this.opts.projectRoot,\n mcpServers: servers,\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'session_create_failed',\n `session/new failed: ${result.message}`,\n result,\n );\n }\n const sessionId = (result as { sessionId?: unknown }).sessionId;\n if (typeof sessionId !== 'string' || sessionId.length === 0) {\n throw new ACPSessionError('protocol_error', 'session/new returned no sessionId', result);\n }\n this.sessionId = sessionId as SessionId;\n }\n\n /**\n * Close the current session gracefully (if the agent supports it).\n *\n * Sends `session/close` JSON-RPC request, then clears the local\n * session id. Best-effort \u2014 errors are swallowed so the caller can\n * always proceed to transport teardown.\n */\n private async closeSession(): Promise<void> {\n if (!this.sessionId) return;\n const sid = this.sessionId;\n this.sessionId = null;\n\n if (this.agentCapabilities.sessionCapabilities?.close) {\n const id = this.allocId();\n try {\n await this.sendRequest(id, 'session/close', { sessionId: sid }, 10_000);\n } catch {\n // Best-effort: if close fails, we still proceed with transport stop.\n }\n }\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Lifecycle \u2014 close\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Tear down the session and kill the child process. */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.state = 'closed';\n this.terminalServer.releaseAll();\n\n // Graceful session close (if session is active and agent supports it)\n if (this.sessionId && this.agentCapabilities.sessionCapabilities?.close) {\n try {\n await this.closeSession();\n } catch {\n // best-effort\n }\n }\n\n // Reject any pending outbound requests so their awaits return.\n for (const [, p] of this.pending) {\n clearTimeout(p.timeoutHandle);\n p.reject(new ACPSessionError('closed', 'session was closed'));\n }\n this.pending.clear();\n this.transportOff?.();\n this.transportOff = null;\n try {\n this.transport.stop();\n } catch {\n // best effort\n }\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Helpers\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Filter MCP servers according to agent capabilities.\n * - Stdio servers are always included.\n * - HTTP servers are only included if agent supports mcpCapabilities.http.\n * - SSE servers are only included if agent supports mcpCapabilities.sse.\n */\n private filterMcpServers(servers?: McpServer[]): McpServer[] {\n if (!servers || servers.length === 0) return [];\n const mcpCaps = this.agentCapabilities.mcpCapabilities ?? {};\n return servers.filter((s) => {\n if ('type' in s && s.type === 'http') return mcpCaps.http === true;\n if ('type' in s && s.type === 'sse') return mcpCaps.sse === true;\n return true; // stdio \u2014 always supported per spec\n });\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Wire layer\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private allocId(): number {\n return this.nextId++;\n }\n\n private async sendRequest(\n id: number,\n method: string,\n params: unknown,\n timeoutMs?: number,\n ): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n const effectiveTimeout = timeoutMs ?? this.timeoutMs;\n const handle = setTimeout(() => {\n this.pending.delete(id);\n reject(\n new ACPSessionError('protocol_error', `${method} timed out after ${effectiveTimeout}ms`),\n );\n }, effectiveTimeout);\n this.pending.set(id, {\n method,\n resolve: resolve as (v: unknown) => void,\n reject,\n timeoutMs: effectiveTimeout,\n timeoutHandle: handle,\n });\n this.transport\n .send({ jsonrpc: '2.0', id, method, params } as never as ACPMessage)\n .catch((err) => {\n clearTimeout(handle);\n this.pending.delete(id);\n const msg = err instanceof Error ? err.message : String(err);\n reject(new ACPSessionError('protocol_error', `send ${method} failed: ${msg}`, err));\n });\n });\n }\n\n /**\n * Send a JSON-RPC 2.0 success response to an agent-initiated request.\n *\n * Per JSON-RPC 2.0 (and the official ACP SDK's message router) a Response\n * object MUST carry `jsonrpc: \"2.0\"` and MUST NOT carry a `method` field \u2014\n * the SDK classifies any object with a `method` key as a Request and drops\n * it as a response, so an agent's `fs/*`, `terminal/*`, or\n * `session/request_permission` callback would hang forever. The legacy\n * `ACPMessage` type predates v1 (requires `method`, lacks `jsonrpc`), so we\n * build the correct wire object and cast at the boundary.\n */\n private sendResult(id: string | number, result: unknown): Promise<void> {\n return this.transport.send({ jsonrpc: '2.0', id, result } as never as ACPMessage);\n }\n\n /** Send a JSON-RPC 2.0 error response (no `method` field, per spec). */\n private sendErrorResponse(id: string | number, code: number, message: string): Promise<void> {\n return this.transport.send({\n jsonrpc: '2.0',\n id,\n error: { code, message },\n } as never as ACPMessage);\n }\n\n private handleMessage(msg: ACPMessage): void {\n // Response to an outbound request (has id and either result or error)\n if (msg.id !== undefined && (msg.result !== undefined || msg.error !== undefined)) {\n const pending = this.pending.get(msg.id);\n if (!pending) return;\n clearTimeout(pending.timeoutHandle);\n this.pending.delete(msg.id);\n if (msg.error !== undefined) {\n pending.reject(new Error(msg.error.message ?? 'unknown JSON-RPC error'));\n } else {\n pending.resolve(msg.result);\n }\n return;\n }\n\n // session/update notification (no id)\n if (msg.method === 'session/update') {\n this.handleUpdate(msg);\n return;\n }\n\n // session/request_permission (has id, expected response: outcome)\n if (msg.method === 'session/request_permission') {\n void this.handlePermissionRequest(msg);\n return;\n }\n\n // fs/* requests\n if (msg.method === 'fs/read_text_file' || msg.method === 'fs/write_text_file') {\n void this.handleFsRequest(msg);\n return;\n }\n\n // terminal/* requests\n if (msg.method?.startsWith('terminal/')) {\n void this.handleTerminalRequest(msg);\n return;\n }\n\n // mcp/* requests from the agent\n if (\n msg.method === 'mcp/connect' ||\n msg.method === 'mcp/message' ||\n msg.method === 'mcp/disconnect'\n ) {\n // MCP channel management \u2014 best-effort acknowledge.\n if (msg.id !== undefined) {\n this.sendResult(msg.id, {}).catch(() => {});\n }\n return;\n }\n\n // elicitation/* requests from the agent\n if (msg.method === 'elicitation/create' || msg.method === 'elicitation/complete') {\n // Elicitation is a UI feedback mechanism \u2014 acknowledge and ignore.\n if (msg.id !== undefined) {\n this.sendResult(msg.id, {}).catch(() => {});\n }\n return;\n }\n\n // $/cancel_request protocol notification \u2014 no response expected.\n if (msg.method === '$/cancel_request') {\n return;\n }\n\n // Anything else: log to stderr and ignore. Don't crash.\n if (msg.method) {\n // eslint-disable-next-line no-console\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'acp_session.unhandled_method',\n method: msg.method,\n timestamp: new Date().toISOString(),\n }),\n );\n }\n }\n\n private handleUpdate(msg: ACPMessage): void {\n const update = (msg as { params?: { update?: unknown } }).params?.update;\n if (typeof update !== 'object' || update === null) return;\n const u = update as { sessionUpdate?: string; [k: string]: unknown };\n // Always surface the raw update so callers that want full fidelity\n // (forwarding to an event bus, etc.) never lose a notification.\n this.emitProgress({ type: 'raw', update: u as AnySessionUpdate });\n switch (u.sessionUpdate) {\n case 'agent_message_chunk': {\n const text = extractText(u.content);\n if (text) {\n this.scratch.text += text;\n this.emitProgress({ type: 'message', text });\n }\n return;\n }\n case 'thought_chunk': {\n const text = extractText(u.content);\n if (text) {\n this.scratch.thoughts += text;\n this.emitProgress({ type: 'thought', text });\n }\n return;\n }\n case 'tool_call':\n case 'tool_call_update': {\n this.captureToolCall(u, u.sessionUpdate === 'tool_call');\n return;\n }\n case 'plan':\n if (Array.isArray(u.entries)) {\n this.scratch.plan = u.entries as PlanEntry[];\n this.emitProgress({ type: 'plan', entries: u.entries as PlanEntry[] });\n }\n return;\n case 'usage_update':\n if (typeof u.used === 'number' && typeof u.size === 'number') {\n const usage = {\n used: u.used,\n size: u.size,\n ...(typeof u.cost === 'object' && u.cost !== null ? { cost: u.cost as UsageCost } : {}),\n };\n this.scratch.usage = usage;\n this.emitProgress({ type: 'usage', usage });\n }\n return;\n case 'available_commands_update':\n case 'current_mode_update':\n case 'config_option_update':\n case 'session_info_update':\n case 'user_message_chunk':\n case 'next_edit_suggestions':\n case 'elicitation':\n return;\n default:\n return;\n }\n }\n\n /**\n * Fold a `tool_call` / `tool_call_update` notification into the scratch\n * tool-call map (deduped by toolCallId), extract any `diff` content into\n * the diffs list, and emit live progress.\n */\n private captureToolCall(u: { [k: string]: unknown }, isNew: boolean): void {\n const toolCallId = typeof u.toolCallId === 'string' ? u.toolCallId : '';\n if (!toolCallId) return;\n const prev = this.scratch.toolCalls.get(toolCallId);\n const record: ACPCapturedToolCall = {\n toolCallId,\n title: typeof u.title === 'string' ? u.title : (prev?.title ?? toolCallId),\n kind: typeof u.kind === 'string' ? (u.kind as ToolKind) : prev?.kind,\n status:\n typeof u.status === 'string'\n ? (u.status as ToolCallStatus)\n : (prev?.status ?? (isNew ? 'pending' : 'in_progress')),\n rawInput: isRecord(u.rawInput) ? u.rawInput : prev?.rawInput,\n rawOutput: isRecord(u.rawOutput) ? u.rawOutput : prev?.rawOutput,\n };\n this.scratch.toolCalls.set(toolCallId, record);\n\n // Pull any diff content out of the tool call so the host can show\n // what changed. The agent sends diffs as ToolCallContent of type 'diff'.\n if (Array.isArray(u.content)) {\n for (const c of u.content as ToolCallContent[]) {\n if (c && typeof c === 'object' && c.type === 'diff') {\n const diff: ACPCapturedDiff = {\n path: c.path,\n oldText: c.oldText,\n newText: c.newText,\n };\n this.scratch.diffs.push(diff);\n this.emitProgress({ type: 'diff', diff });\n }\n }\n }\n\n this.emitProgress({\n type: isNew ? 'tool_call' : 'tool_call_update',\n toolCall: record,\n });\n }\n\n private emitProgress(event: ACPProgressEvent): void {\n if (!this.progressHandler) return;\n try {\n this.progressHandler(event);\n } catch {\n // A faulty host handler must never break the wire pump.\n }\n }\n\n /** Live progress handler installed for the duration of a `prompt()` turn. */\n private progressHandler: ACPProgressHandler | null = null;\n\n // Per-prompt scratch state\n private scratch: {\n text: string;\n thoughts: string;\n plan?: PlanEntry[];\n usage?: { used: number; size: number; cost?: UsageCost | undefined };\n toolCalls: Map<string, ACPCapturedToolCall>;\n diffs: ACPCapturedDiff[];\n } = { text: '', thoughts: '', toolCalls: new Map(), diffs: [] };\n\n private resetScratch(): void {\n this.scratch = { text: '', thoughts: '', toolCalls: new Map(), diffs: [] };\n }\n\n private async handlePermissionRequest(msg: ACPMessage): Promise<void> {\n const id = msg.id;\n if (id === undefined) return;\n const params = (msg as { params?: { toolCall?: unknown; options?: unknown } }).params;\n const toolCall = params?.toolCall as ToolCallUpdateNotification | undefined;\n const options = Array.isArray(params?.options)\n ? (params.options as never as Parameters<PermissionPolicy>[0]['options'])\n : [];\n if (!toolCall) {\n await this.sendErrorResponse(id, -32602, 'toolCall is required');\n return;\n }\n const policyAbort = new AbortController();\n try {\n const outcome = await this.permissionPolicy({\n toolCall,\n options,\n signal: policyAbort.signal,\n });\n await this.sendResult(id, { outcome });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await this.sendErrorResponse(id, -32603, `permission policy failed: ${message}`);\n }\n }\n\n /**\n * Enforce authorization at privileged callback sinks (fs/write,\n * terminal/create). Unlike `handlePermissionRequest` which responds to\n * agent-initiated `session/request_permission` messages, this method is\n * called by the handler BEFORE dispatching to FileServer/TerminalServer,\n * closing the gap where the agent simply skips the voluntary permission\n * request and sends the privileged callback directly.\n *\n * Uses the session's permission policy. The default\n * (`readOnlyPermissionPolicy`) auto-approves only side-effect-free tool\n * calls (read/search/fetch/think) and rejects everything else \u2014 this is\n * the safe-by-default posture. For trusted local agents (CLI `acp spawn`,\n * Director fan-out), inject `defaultPermissionPolicy` to grant\n * write/execute access.\n *\n * Returns true if the callback is authorized, false if denied.\n */\n private async authorizeCallback(partial: {\n toolCallId: string;\n title: string;\n kind: import('../types/acp-v1.js').ToolKind;\n rawInput?: Record<string, unknown>;\n }): Promise<boolean> {\n try {\n const outcome = await this.permissionPolicy({\n toolCall: {\n sessionUpdate: 'tool_call_update',\n toolCallId: partial.toolCallId as import('../types/acp-v1.js').ToolCallId,\n title: partial.title,\n kind: partial.kind,\n status: 'pending',\n ...(partial.rawInput ? { rawInput: partial.rawInput } : {}),\n },\n options: [\n { optionId: 'allow', name: 'Allow', kind: 'allow_once' },\n { optionId: 'reject', name: 'Reject', kind: 'reject_once' },\n ],\n signal: new AbortController().signal,\n });\n return (\n outcome.outcome === 'selected' &&\n outcome.optionId !== 'reject' &&\n outcome.optionId !== 'reject_once' &&\n outcome.optionId !== 'reject_always'\n );\n } catch {\n // If the policy throws, deny rather than crash.\n return false;\n }\n }\n\n private async handleFsRequest(msg: ACPMessage): Promise<void> {\n const id = msg.id;\n if (id === undefined) return;\n const params = (msg as { params?: { sessionId?: string; path?: string; content?: string } })\n .params;\n if (!params?.path) {\n await this.sendErrorResponse(id, -32602, 'path is required');\n return;\n }\n // Authorization gate: the ACP spec makes session/request_permission\n // voluntary \u2014 the agent is NOT required to ask before sending fs/*.\n // Enforce authorization at the sink: synthesize a permission request\n // for write operations (reads are auto-approved), and reject if the\n // policy denies.\n if (msg.method === 'fs/write_text_file') {\n const allowed = await this.authorizeCallback({\n toolCallId: `acp-fs-write-${id}`,\n title: `Write file: ${params.path}`,\n kind: 'edit',\n rawInput: { path: params.path, sessionId: params.sessionId },\n });\n if (!allowed) {\n await this.sendErrorResponse(id, -32602, 'filesystem write denied by permission policy');\n return;\n }\n }\n try {\n if (msg.method === 'fs/read_text_file') {\n const result = await this.fileServer.readTextFile({\n sessionId: params.sessionId ?? '',\n path: params.path,\n });\n await this.sendResult(id, result);\n } else {\n await this.fileServer.writeTextFile({\n sessionId: params.sessionId ?? '',\n path: params.path,\n content: params.content ?? '',\n });\n await this.sendResult(id, {});\n }\n } catch (err) {\n const code = err instanceof FsError ? -32602 : -32603;\n const message = err instanceof Error ? err.message : String(err);\n await this.sendErrorResponse(id, code, message);\n }\n }\n\n private async handleTerminalRequest(msg: ACPMessage): Promise<void> {\n const id = msg.id;\n if (id === undefined) return;\n const params = (msg as { params?: Record<string, unknown> }).params ?? {};\n try {\n switch (msg.method) {\n case 'terminal/create': {\n // Authorization gate: terminal/create spawns a process with the\n // agent's chosen command/args. Require explicit policy approval\n // before allowing it, since this is arbitrary code execution.\n const allowed = await this.authorizeCallback({\n toolCallId: `acp-terminal-create-${id}`,\n title:\n `Run command: ${String(params.command ?? '')} ${(Array.isArray(params.args) ? params.args : []).join(' ')}`.trim(),\n kind: 'execute',\n rawInput: {\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n sessionId: params.sessionId,\n },\n });\n if (!allowed) {\n await this.sendErrorResponse(id, -32602, 'terminal create denied by permission policy');\n return;\n }\n const createOpts: Parameters<TerminalServer['create']>[0] = {\n sessionId: String(params.sessionId ?? ''),\n command: String(params.command ?? ''),\n args: Array.isArray(params.args) ? (params.args as string[]) : [],\n };\n if (Array.isArray(params.env)) {\n createOpts.env = params.env as { name: string; value: string }[];\n }\n if (typeof params.cwd === 'string') {\n createOpts.cwd = params.cwd;\n }\n if (typeof params.outputByteLimit === 'number') {\n createOpts.outputByteLimit = params.outputByteLimit;\n }\n const result = this.terminalServer.create(createOpts);\n await this.sendResult(id, result);\n return;\n }\n case 'terminal/output': {\n const terminalId = String(params.terminalId ?? '');\n const out = this.terminalServer.output(terminalId);\n await this.sendResult(id, out);\n return;\n }\n case 'terminal/wait_for_exit': {\n const terminalId = String(params.terminalId ?? '');\n const exit = await this.terminalServer.waitForExit(terminalId);\n await this.sendResult(id, exit);\n return;\n }\n case 'terminal/kill': {\n const terminalId = String(params.terminalId ?? '');\n this.terminalServer.kill(terminalId);\n await this.sendResult(id, {});\n return;\n }\n case 'terminal/release': {\n const terminalId = String(params.terminalId ?? '');\n this.terminalServer.release(terminalId);\n await this.sendResult(id, {});\n return;\n }\n default:\n await this.sendErrorResponse(id, -32601, `unknown method: ${msg.method}`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await this.sendErrorResponse(id, -32603, message);\n }\n }\n}\n\n/**\n * Create a text ContentBlock. Convenience helper for callers of\n * `session.prompt()`.\n */\nexport function textContent(text: string): ContentBlock {\n return { type: 'text', text };\n}\n\n/**\n * Create an image ContentBlock. Only send this if the agent's\n * `promptCapabilities.image` is `true` (check via\n * `session.getCapabilities().promptCapabilities?.image`).\n */\nexport function imageContent(mimeType: string, data: string): ContentBlock {\n return { type: 'image', mimeType, data };\n}\n\n/**\n * Create an audio ContentBlock. Only send this if the agent's\n * `promptCapabilities.audio` is `true` (check via\n * `session.getCapabilities().promptCapabilities?.audio`).\n */\nexport function audioContent(mimeType: string, data: string): ContentBlock {\n return { type: 'audio', mimeType, data };\n}\n\nfunction extractText(block: unknown): string {\n if (typeof block !== 'object' || block === null) return '';\n const b = block as {\n type?: string;\n text?: unknown;\n resource?: { text?: unknown };\n };\n if (b.type === 'text' && typeof b.text === 'string') return b.text;\n // Embedded text resources carry their content under `resource.text`.\n if (\n b.type === 'resource' &&\n b.resource &&\n typeof b.resource === 'object' &&\n typeof b.resource.text === 'string'\n ) {\n return b.resource.text;\n }\n return '';\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/** A fully-populated empty run result (used for pre-aborted short-circuits). */\nfunction emptyRunResult(stopReason: StopReason): ACPSessionRunResult {\n return {\n text: '',\n stopReason,\n hasText: false,\n toolCalls: [],\n diffs: [],\n thoughts: '',\n };\n}\n", "/**\n * ACPSubagentRunner \u2014 `SubagentRunner` implementation for DIR-1.\n *\n * Wraps an external ACP-supporting agent (Claude Code, Gemini CLI, Codex\n * CLI, Cline, Goose, OpenHands, etc.) as a WrongStack subagent. The\n * external agent runs its own agent loop; we send it a task via the ACP\n * v1 protocol and return the result.\n *\n * v1 spec: https://agentclientprotocol.com/protocol/v1/overview\n *\n * Connected to the Director / MultiAgentCoordinator via the\n * `SubagentRunner` interface (same shape as `AgentSubagentRunner`).\n */\nimport type {\n SubagentError,\n SubagentErrorKind,\n SubagentRunContext,\n SubagentRunOutcome,\n SubagentRunner,\n TaskSpec,\n} from '@wrongstack/core/types';\nimport {\n ACPSession,\n ACPSessionError,\n textContent,\n type ACPProgressEvent,\n type ACPProgressHandler,\n} from '../client/acp-session.js';\nimport type { ACPSessionErrorKind } from '../client/acp-session.js';\nimport type { PermissionPolicy } from '../client/permission.js';\nimport { findAgentDescriptor } from '../registry/agents.catalog.js';\nimport type { McpServer } from '../types/acp-v1.js';\n\nexport interface ACPSubagentRunnerOptions {\n /** How to spawn the external agent. */\n command: string;\n args?: string[] | undefined;\n env?: Record<string, string> | undefined;\n cwd?: string | undefined;\n /** Subagent role label \u2014 surfaced in errors and used for logging. */\n role?: string | undefined;\n /**\n * Hard wall-clock cap for one prompt turn. Defaults to 5 minutes.\n * Overrides `SubagentRunContext.budget.limits.timeoutMs` if both are set.\n */\n timeoutMs?: number | undefined;\n /**\n * Filesystem sandbox root. Defaults to `options.cwd` (when set) or\n * the process's current working directory. All `fs/read_text_file` /\n * `fs/write_text_file` calls are bounded to this root.\n */\n projectRoot?: string | undefined;\n /**\n * Live progress callback. Forwarded to `ACPSession.prompt` so the host\n * can render the external agent's tool calls / diffs / text as they\n * stream, instead of waiting for the buffered final result.\n */\n onProgress?: ACPProgressHandler | undefined;\n /**\n * Permission policy for the external agent's `session/request_permission`\n * calls. Defaults to the session's own default. Inject the host's\n * confirm/trust UI here so an external agent's file writes / commands\n * are surfaced to a human instead of silently auto-approved.\n */\n permissionPolicy?: PermissionPolicy | undefined;\n /**\n * MCP servers to expose to the external agent (passed through\n * `session/new` / `session/load`). Stdio servers are always sent;\n * HTTP/SSE are filtered by the agent's advertised capabilities.\n */\n mcpServers?: McpServer[] | undefined;\n /**\n * When true, the underlying `ACPSession` is kept open across multiple\n * runner invocations (multi-turn conversation \u2014 the external agent\n * keeps its context). The caller MUST call `stop()` to tear it down.\n * Defaults to false (one process per task).\n */\n persistent?: boolean | undefined;\n}\n\n/**\n * Static catalog of agent ids \u2192 spawn options.\n *\n * The CLI and the host's `buildACPRunner` look up entries by id. The\n * canonical, multi-source catalog is `packages/acp/src/registry/agents.catalog.ts`\n * (the 12-entry static catalog introduced in commit 4ad287b4). This\n * map stays for backward compatibility with existing call sites that\n * import it directly; new code should prefer the registry.\n */\nexport const ACP_AGENT_COMMANDS: Record<string, ACPSubagentRunnerOptions> = {\n cline: {\n command: 'npx',\n args: ['-y', '@agentify/cline'],\n role: 'cline',\n },\n 'gemini-cli': {\n command: 'gemini',\n role: 'gemini-cli',\n },\n copilot: {\n command: 'gh',\n args: ['copilot', 'agent'],\n role: 'copilot',\n },\n openhands: {\n command: 'openhands',\n role: 'openhands',\n },\n goose: {\n command: 'goose',\n role: 'goose',\n },\n};\n\n/**\n * Build a one-shot `SubagentRunner` for a single agent invocation. Each\n * call to the returned function spawns a fresh child process, runs one\n * prompt turn, and tears everything down. The cost is ~1 second of\n * process-startup per call; for long-lived sessions (multi-turn\n * conversations), use `makeACPSubagentRunnerWithStop` and call `stop()`\n * explicitly.\n */\nexport async function makeACPSubagentRunner(\n options: ACPSubagentRunnerOptions,\n): Promise<SubagentRunner> {\n const { runner, stop } = await makeACPSubagentRunnerWithStop(options);\n // Wrap so we always tear down after the turn, even if the caller\n // forgot to call `stop()`. stop() is idempotent, so a double-call is\n // safe.\n const wrappedRunner: SubagentRunner = async (task, ctx) => {\n try {\n return await runner(task, ctx);\n } finally {\n stop();\n }\n };\n return wrappedRunner;\n}\n\n/**\n * Build a long-lived `SubagentRunner` plus an explicit `stop()` for\n * teardown. The caller is responsible for calling `stop()` when done\n * (or when the host's signal fires). Useful for the `wstack acp spawn`\n * CLI command, which holds the child open for the duration of a user\n * task and tears down on SIGINT.\n */\nexport async function makeACPSubagentRunnerWithStop(\n options: ACPSubagentRunnerOptions,\n): Promise<{ runner: SubagentRunner; stop: () => void | Promise<void> }> {\n const projectRoot = options.projectRoot ?? options.cwd ?? process.cwd();\n const timeoutMs = options.timeoutMs ?? 5 * 60_000;\n const persistent = options.persistent === true;\n\n // In persistent mode we keep a single session alive across runner calls\n // so the external agent retains its conversation context (multi-turn).\n let shared: ACPSession | null = null;\n\n const startSession = async (): Promise<ACPSession> => {\n return ACPSession.start({\n command: options.command,\n ...(options.args !== undefined ? { args: options.args } : {}),\n ...(options.env !== undefined ? { env: options.env } : {}),\n ...(options.cwd !== undefined ? { cwd: options.cwd } : {}),\n projectRoot,\n timeoutMs,\n role: options.role,\n ...(options.permissionPolicy !== undefined\n ? { permissionPolicy: options.permissionPolicy }\n : {}),\n ...(options.mcpServers !== undefined ? { mcpServers: options.mcpServers } : {}),\n });\n };\n\n const runner: SubagentRunner = async (\n task: TaskSpec,\n ctx: SubagentRunContext,\n ): Promise<SubagentRunOutcome> => {\n let session: ACPSession;\n const reuse = persistent && shared !== null;\n try {\n session = reuse ? (shared as ACPSession) : await startSession();\n if (persistent) shared = session;\n } catch (err) {\n // init / spawn failure. Throw a structured error so the host can\n // classify it (SubagentErrorKind).\n throw acpErrorToSubagentError(err, options.role ?? 'acp-subagent');\n }\n\n // Count real tool calls from the captured stream, and keep the\n // budget's idle clock fresh on every update so a long-but-working\n // external agent is never reaped by the watchdog as \"stalled\".\n const onProgress: ACPProgressHandler = (event: ACPProgressEvent) => {\n try {\n ctx.budget.markActivity();\n } catch {\n // markActivity never throws today; guard defensively anyway.\n }\n options.onProgress?.(event);\n };\n\n try {\n const result = await session.prompt(\n [textContent(task.description)],\n ctx.signal,\n onProgress,\n );\n // Surface the real tool-call count captured from the stream. A\n // text-less turn is a soft signal (an ACP agent may legitimately\n // end with no message), not an error.\n return {\n result: result.text,\n iterations: 1,\n toolCalls: result.toolCalls.length,\n };\n } catch (err) {\n throw acpErrorToSubagentError(err, options.role ?? 'acp-subagent');\n } finally {\n // One-shot mode closes after each turn. Persistent mode keeps the\n // session open; the caller tears it down via stop().\n if (!persistent) {\n try {\n await session.close();\n } catch {\n // best-effort cleanup\n }\n }\n }\n };\n\n // In persistent mode stop() closes the long-lived session; in one-shot\n // mode it's a no-op (each session is closed in the runner's finally).\n const stop = async (): Promise<void> => {\n if (shared) {\n const s = shared;\n shared = null;\n try {\n await s.close();\n } catch {\n // best-effort\n }\n }\n };\n\n return { runner, stop };\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Error mapping\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Map an ACPSessionError (or arbitrary Error from the session layer)\n * to a structured `SubagentError` that the existing coordinator can\n * classify and act on. Unknown error shapes get `kind: 'unknown'` \u2014\n * they shouldn't crash the parent.\n */\nfunction acpErrorToSubagentError(\n err: unknown,\n subagentId: string,\n): SubagentError {\n if (err instanceof ACPSessionError) {\n const kind = mapACPKind(err.kind);\n return {\n kind,\n message: `${subagentId}: ${err.message}`,\n retryable: isRetryable(kind),\n cause: {\n name: err.name,\n message: err.message,\n ...(err.stack !== undefined ? { stack: err.stack } : {}),\n },\n };\n }\n const message = err instanceof Error ? err.message : String(err);\n return {\n kind: 'bridge_failed',\n message: `${subagentId}: ${message}`,\n retryable: false,\n cause: {\n name: err instanceof Error ? err.name : 'Error',\n message,\n ...(err instanceof Error && err.stack !== undefined ? { stack: err.stack } : {}),\n },\n };\n}\n\nfunction mapACPKind(acpKind: ACPSessionErrorKind): SubagentErrorKind {\n switch (acpKind) {\n case 'spawn_failed':\n case 'init_failed':\n case 'session_create_failed':\n case 'agent_died':\n case 'protocol_error':\n return 'bridge_failed';\n case 'prompt_failed':\n return 'tool_failed';\n case 'auth_failed':\n case 'logout_failed':\n return 'bridge_failed';\n case 'aborted':\n return 'aborted_by_parent';\n case 'closed':\n case 'unsupported_capability':\n return 'unknown';\n }\n}\n\nfunction isRetryable(kind: SubagentErrorKind): boolean {\n // Conservative: spawn / init / protocol / agent-died are NOT\n // retryable as-is (they need config or a re-install). Timeouts and\n // prompt failures might be \u2014 the parent's classifier will branch on\n // `kind` and decide.\n // None of the ACP error kinds currently map to a retryable coordinator\n // kind. Keep the parameter so this policy remains explicit at the callsite.\n void kind;\n return false;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Unused but exported for future use\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Re-export so the CLI handler can import the session type. */\nexport type { ACPSession };\n\n/** Exposed for the `wstack acp list` renderer. */\nexport function describeAgent(id: string): {\n command: string;\n args: readonly string[];\n role: string;\n} | null {\n const entry = ACP_AGENT_COMMANDS[id];\n if (!entry) return null;\n return {\n command: entry.command,\n args: entry.args ?? [],\n role: entry.role ?? id,\n };\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Shared command resolution + single-task run + handshake probe\n//\n// These are the building blocks both the `wstack acp` CLI handler and the\n// `/acp` slash command consume, so the two surfaces stay in lock-step.\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Per-agent ACP invocation overrides, sourced from the user's\n * active profile config (`~/.wrongstack/profiles/<name>/config.json`,\n * `config.acp.agents`). Lets a user point an\n * agent id at the correct ACP entry \u2014 e.g. the Zed Claude-Code adapter \u2014\n * without a code change. NEVER honoured from in-project config (it is an\n * arbitrary-command exec surface); see `config-loader.ts`.\n */\nexport type AcpAgentCommandOverrides = Record<\n string,\n { command: string; args?: string[]; env?: Record<string, string> }\n>;\n\n/** A synced-registry catalog keyed by registry id (from `fetchAcpRegistry`). */\nexport type AcpLiveCatalog = Record<\n string,\n { command: string; args?: readonly string[]; env?: Record<string, string> }\n>;\n\n/**\n * Map our stable, human-friendly catalog ids to the official registry's ids,\n * so a live-synced registry (keyed by registry id) still resolves when the\n * user types our id. Our id is preferred in the UI; the alias is the bridge.\n */\nexport const REGISTRY_ID_ALIASES: Readonly<Record<string, string>> = {\n 'claude-code': 'claude-acp',\n 'gemini-cli': 'gemini',\n 'codex-cli': 'codex-acp',\n copilot: 'github-copilot-cli',\n // Kimi's live registry id is `kimi` \u2014 same as our catalog id, so the\n // alias is identity. Listed explicitly so `resolveAcpAgentCommand`\n // finds the live entry when the registry is synced.\n kimi: 'kimi',\n};\n\n/**\n * Resolve an agent id to its spawn command. Precedence:\n * 1. user override (`config.acp.agents[id]`)\n * 2. the bundled static `AGENTS_CATALOG` (curated LOCAL-binary invocations)\n * 3. live synced registry (`fetchAcpRegistry` \u2192 cache), by id or alias\n * 4. legacy `ACP_AGENT_COMMANDS` map (last resort, kept for back-compat)\n * Returns `null` for an id present in none of them.\n *\n * Why catalog BEFORE the live registry: our goal is to drive the user's\n * already-installed, logged-in CLI. The catalog hand-curates the LOCAL-binary\n * ACP entry for each popular agent (`gemini --acp`, `opencode acp`, \u2026), which\n * preserves the agent's own login and starts instantly. The official registry,\n * by contrast, encodes \"run a fresh copy\" invocations \u2014 pinned `npx <pkg>@ver`\n * downloads (no local login, slow first run) and platform binaries like\n * `opencode.exe` that may not match a shim on PATH. So the registry is the\n * source for the long tail of agents the catalog doesn't cover, NOT an\n * override of the curated 12. Users force a specific command via the override.\n */\nexport function resolveAcpAgentCommand(\n id: string,\n overrides?: AcpAgentCommandOverrides,\n live?: AcpLiveCatalog,\n): ACPSubagentRunnerOptions | null {\n const ov = overrides?.[id];\n if (ov && typeof ov.command === 'string' && ov.command.length > 0) {\n const out: ACPSubagentRunnerOptions = {\n command: ov.command,\n args: [...(ov.args ?? [])],\n role: id,\n };\n if (ov.env) out.env = ov.env;\n return out;\n }\n const desc = findAgentDescriptor(id);\n if (desc) {\n const out: ACPSubagentRunnerOptions = {\n command: desc.acp.command,\n args: [...(desc.acp.args ?? [])],\n role: id,\n };\n if (desc.acp.env) out.env = desc.acp.env;\n return out;\n }\n const liveEntry = live?.[id] ?? live?.[REGISTRY_ID_ALIASES[id] ?? ''];\n if (liveEntry && typeof liveEntry.command === 'string' && liveEntry.command.length > 0) {\n const out: ACPSubagentRunnerOptions = {\n command: liveEntry.command,\n args: [...(liveEntry.args ?? [])],\n role: id,\n };\n if (liveEntry.env) out.env = liveEntry.env;\n return out;\n }\n const fromMap = ACP_AGENT_COMMANDS[id];\n if (fromMap) return fromMap;\n return null;\n}\n\nexport interface AcpProbeResult {\n id: string;\n ok: boolean;\n ms: number;\n agentInfo?: { name: string; title?: string | undefined; version: string } | undefined;\n error?: string | undefined;\n}\n\n/**\n * Empirically test whether an agent actually speaks ACP on this machine:\n * spawn it, run the `initialize` handshake, and close. `ok: true` means the\n * agent answered `initialize` within `timeoutMs` (default 8s) \u2014 the truth,\n * regardless of what the static catalog guesses. A bare CLI that drops into\n * an interactive prompt fails here (init times out) instead of hanging a\n * real turn.\n */\nexport async function probeAcpAgent(\n idOrCmd: string | ACPSubagentRunnerOptions,\n opts?: {\n timeoutMs?: number | undefined;\n projectRoot?: string | undefined;\n overrides?: AcpAgentCommandOverrides | undefined;\n live?: AcpLiveCatalog | undefined;\n },\n): Promise<AcpProbeResult> {\n const id =\n typeof idOrCmd === 'string' ? idOrCmd : (idOrCmd.role ?? idOrCmd.command);\n const cmd =\n typeof idOrCmd === 'string'\n ? resolveAcpAgentCommand(idOrCmd, opts?.overrides, opts?.live)\n : idOrCmd;\n if (!cmd) return { id, ok: false, ms: 0, error: 'unknown agent' };\n\n const timeoutMs = opts?.timeoutMs ?? 8_000;\n const startedAt = Date.now();\n let session: ACPSession | null = null;\n try {\n session = await ACPSession.start({\n command: cmd.command,\n ...(cmd.args !== undefined ? { args: cmd.args } : {}),\n ...(cmd.env !== undefined ? { env: cmd.env } : {}),\n projectRoot: opts?.projectRoot ?? process.cwd(),\n // Bounds the `initialize` request: a CLI that spawns but never answers\n // the handshake fails after this instead of blocking.\n timeoutMs,\n });\n const info = session.getAgentInfo();\n return {\n id,\n ok: true,\n ms: Date.now() - startedAt,\n ...(info ? { agentInfo: info } : {}),\n };\n } catch (err) {\n return {\n id,\n ok: false,\n ms: Date.now() - startedAt,\n error: err instanceof Error ? err.message : String(err),\n };\n } finally {\n if (session) {\n try {\n await session.close();\n } catch {\n // best-effort\n }\n }\n }\n}\n\nexport interface ProbeAcpAgentsOptions {\n agentIds: string[];\n resolveCmd: (id: string) => ACPSubagentRunnerOptions | null;\n projectRoot?: string | undefined;\n /** Max agents probed at once. Default 4. Keeps concurrent first-run `npx`\n * downloads from starving local agents' stdout past their timeout. */\n concurrency?: number | undefined;\n /** Per-agent handshake timeout for LOCAL binary commands. Default 20s. */\n timeoutMs?: number | undefined;\n /** Per-agent timeout for `npx`/`uvx` commands (first run downloads the\n * package, which is slow). Default 90s. */\n packageTimeoutMs?: number | undefined;\n signal?: AbortSignal | undefined;\n onProgress?: ((id: string, result: AcpProbeResult) => void) | undefined;\n}\n\n/**\n * Probe many agents with BOUNDED concurrency. Unbounded `Promise.all` over the\n * full set spawns every agent at once \u2014 and a few concurrent `npx` downloads\n * peg the machine hard enough that even already-installed local agents miss\n * their handshake window. Bounding the fan-out (and giving npx/uvx a longer\n * timeout) is what makes a mixed install probe reliably.\n */\nexport async function probeAcpAgents(\n opts: ProbeAcpAgentsOptions,\n): Promise<AcpProbeResult[]> {\n const localTimeout = opts.timeoutMs ?? 20_000;\n const pkgTimeout = opts.packageTimeoutMs ?? 90_000;\n const ids = opts.agentIds;\n const byId = new Map<string, AcpProbeResult>();\n\n // Partition: local binaries vs npx/uvx package launchers. A first-run `npx`\n // download is heavy enough to starve a LOCAL agent sharing the same batch\n // (its stdout 'data' misses the handshake window \u2192 false timeout). So probe\n // all locals first (clean resources, fast), THEN the package ones \u2014 which\n // are inherently slow on first run \u2014 at low concurrency.\n const local: string[] = [];\n const pkg: string[] = [];\n const cmds = new Map<string, ACPSubagentRunnerOptions | null>();\n for (const id of ids) {\n const cmd = opts.resolveCmd(id);\n cmds.set(id, cmd);\n if (!cmd) continue;\n if (cmd.command === 'npx' || cmd.command === 'uvx') pkg.push(id);\n else local.push(id);\n }\n\n const runPhase = async (phaseIds: string[], concurrency: number, timeoutMs: number): Promise<void> => {\n let next = 0;\n const workerCount = Math.min(Math.max(1, concurrency), Math.max(1, phaseIds.length));\n const workers: Promise<void>[] = [];\n for (let w = 0; w < workerCount; w++) {\n workers.push(\n (async () => {\n while (true) {\n const current = next++;\n if (current >= phaseIds.length) return;\n const id = phaseIds[current]!;\n if (opts.signal?.aborted) {\n byId.set(id, { id, ok: false, ms: 0, error: 'aborted' });\n continue;\n }\n const cmd = cmds.get(id)!;\n const r = await probeAcpAgent(cmd!, {\n timeoutMs,\n ...(opts.projectRoot !== undefined ? { projectRoot: opts.projectRoot } : {}),\n });\n r.id = id; // probeAcpAgent derives id from cmd.role; pin to our id.\n byId.set(id, r);\n opts.onProgress?.(id, r);\n }\n })(),\n );\n }\n await Promise.all(workers);\n };\n\n // Unknown ids resolve to null \u2014 record immediately.\n for (const id of ids) {\n if (cmds.get(id) === null) {\n const r: AcpProbeResult = { id, ok: false, ms: 0, error: 'unknown agent' };\n byId.set(id, r);\n opts.onProgress?.(id, r);\n }\n }\n\n await runPhase(local, opts.concurrency ?? 4, localTimeout);\n // Package launchers run AFTER locals (so npm downloads never starve a local\n // agent's handshake), at low concurrency with a long timeout \u2014 first-run\n // `npx`/`uvx` fetches are inherently slow.\n await runPhase(pkg, 2, pkgTimeout);\n\n // Preserve the caller's input order.\n return ids.map((id) => byId.get(id)!);\n}\n", "import { expectDefined } from '@wrongstack/core/utils';\n/**\n * ToolTranslator \u2014 bidirectional translation between WrongStack tools and\n * ACP tool representations.\n *\n * Used by DIR-1 (WrongStack as ACP client) to:\n * - Map WrongStack TaskSpec \u2192 ACP task payload\n * - Map ACP tool responses \u2192 TaskResult\n *\n * Used by DIR-2 (WrongStack as ACP server) to:\n * - Convert the WrongStack Tool.inputSchema \u2192 ACPToolDefinition.inputSchema\n * - (handled by tools-registry.ts \u2014 same logic lives there)\n *\n * For DIR-1 async tool calls: ACP agents send progress notifications while\n * a tool is running, then send a final result. The translator handles this\n * by polling for the final [result] notification on the transport.\n */\nimport type {ACPMessage, ACPToolDefinition, ACPToolCallResponse, ContentBlock} from '../types/acp-messages.js';\nimport type { TaskSpec, TaskResult } from '@wrongstack/core/types';\nexport interface ToolTranslatorOptions {\n /**\n * If true (default), wrap tool calls in an async poll loop that waits\n * for progress notifications until a final result arrives.\n */\n asyncTools?: boolean | undefined;\n pollIntervalMs?: number | undefined;\n totalTimeoutMs?: number | undefined;\n}\n\nconst DEFAULT_OPTIONS: Required<ToolTranslatorOptions> = {\n asyncTools: true,\n pollIntervalMs: 500,\n totalTimeoutMs: 120_000,\n};\n\n/** Convert an ACP ACPToolDefinition \u2192 a JSON schema object recognisable by WrongStack */\nexport function acpToolToSchema(def: ACPToolDefinition): Record<string, unknown> {\n if (!def.inputSchema) return {type: 'object', properties: {}};\n return def.inputSchema as Record<string, unknown>;\n}\n\n/** Extract tool result text from ACP ContentBlock[] */\nexport function extractTextFromContent(blocks: ContentBlock[]): string {\n const parts: string[] = [];\n for (const b of blocks) {\n if (b.type === 'text') parts.push(b.text);\n else if (b.type === 'resource') parts.push(`[resource: ${b.resource.uri}]`);\n else if (b.type === 'image') parts.push(`[image: ${b.data.slice(0, 20)}...]`);\n else if (b.type === 'progress') {\n if (b.messages?.length) parts.push(b.messages.join('\\n'));\n }\n }\n return parts.join('\\n');\n}\n\n/** Build a TaskSpec from an ACP task payload */\nexport function buildTaskSpec(payload: {\n taskId: string;\n task: string;\n subagentId?: string | undefined;\n}): TaskSpec {\n return {\n id: payload.taskId,\n description: payload.task,\n subagentId: payload.subagentId,\n };\n}\n\n/** Parse an ACP tools/call response \u2192 TaskResult */\nexport function parseToolResponse(\n taskId: string,\n subagentId: string,\n response: ACPToolCallResponse,\n): TaskResult {\n const blocks = response.result.content;\n const text = extractTextFromContent(blocks);\n\n // Detect error state from isError flag or error-like text\n const isError =\n response.result.isError || text.toLowerCase().includes('error') ||\n text.toLowerCase().includes('failed');\n\n return {\n taskId,\n subagentId,\n status: isError ? 'failed' : 'success',\n result: text,\n iterations: 1,\n toolCalls: 1,\n durationMs: 0,\n };\n}\n\n/** ToolTranslator for DIR-1 \u2014 wraps ACP client transport, adds task semantics */\nexport class ToolTranslator {\n private readonly opts: Required<ToolTranslatorOptions>;\n private readonly pending = new Map<string | number, {\n resolve: (v: ACPToolCallResponse) => void;\n reject: (e: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n }>();\n\n constructor(opts: ToolTranslatorOptions = {}) {\n this.opts = {...DEFAULT_OPTIONS, ...opts};\n }\n\n /**\n * Start listening to a transport for tool responses and cancellations.\n * Call this once after constructing the translator and before sending tasks.\n */\n attachToTransport(\n transport: {onMessage: (h: (msg: ACPMessage) => void) => () => void; send: (msg: ACPMessage) => Promise<void>},\n ): void {\n transport.onMessage((msg) => {\n if (msg.method === 'tools/call' && msg.id !== undefined) {\n const pending = this.pending.get(msg.id);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(expectDefined(msg.id));\n pending.resolve(msg as never as ACPToolCallResponse);\n }\n }\n\n // Handle cancellation notifications\n if (msg.method === 'cancel' && msg.id !== undefined) {\n const pending = this.pending.get(msg.id);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(expectDefined(msg.id));\n pending.reject(new Error('Call cancelled by client'));\n }\n }\n });\n }\n\n /**\n * Send a tool call over the transport and wait for a response.\n * If asyncTools is true, polls for progress and resolves when the final\n * response arrives.\n */\n async callTool(\n transport: {send: (msg: ACPMessage) => Promise<void>},\n name: string,\n args: Record<string, unknown>,\n callId: string | number = crypto.randomUUID(),\n ): Promise<ACPToolCallResponse> {\n await transport.send({\n jsonrpc: '2.0',\n method: 'tools/call',\n id: callId,\n params: {name, arguments: args},\n } as never as ACPMessage);\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pending.delete(callId);\n reject(new Error(`Tool call ${name} timed out after ${this.opts.totalTimeoutMs}ms`));\n }, this.opts.totalTimeoutMs);\n\n this.pending.set(callId, {resolve, reject, timeout});\n });\n }\n\n cancelAll(): void {\n for (const [, p] of this.pending) {\n clearTimeout(p.timeout);\n }\n this.pending.clear();\n }\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../src/agent/stdio-transport.ts", "../src/win32-cmd.ts", "../src/types/acp-v1.ts", "../src/client/acp-session-content.ts", "../src/client/file-server.ts", "../src/client/permission.ts", "../src/client/terminal-server.ts", "../src/client/trust-boundary-permission.ts", "../src/client/websocket-transport.ts", "../src/client/acp-session-errors.ts", "../src/client/acp-session-updates.ts", "../src/client/acp-session-callbacks.ts", "../src/client/acp-message-routing.ts", "../src/client/acp-session.ts", "../src/integration/acp-subagent-runner.ts", "../src/client/tool-translator.ts"],
4
+ "sourcesContent": ["/**\n * StdioTransport \u2014 bidirectional stdin/stdout communication for ACP.\n *\n * ACP uses newline-delimited JSON-RPC 2.0 messages over stdio:\n * client \u2192 agent: JSON-RPC request/notification on stdin\n * agent \u2192 client: JSON-RPC response/notification on stdout\n *\n * Legacy startup marker support remains for older internal harnesses, but\n * standard ACP agents must not write non-JSON data to stdout.\n */\nimport { expectDefined, writeErr } from '@wrongstack/core/utils';\nimport { treeKill } from '@wrongstack/core/utils/tree-kill';\nimport type { ACPMessage } from '../types/acp-messages.js';\nimport { buildWin32CmdShimInvocation } from '../win32-cmd.js';\n\nconst DEFAULT_MAX_FRAME_CHARS = 20 * 1024 * 1024;\nconst DEFAULT_MAX_QUEUED_MESSAGES = 1_000;\n\nfunction positiveLimit(value: number | undefined, fallback: number): number {\n return value !== undefined && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;\n}\n\nexport interface AgentServerTransport {\n send(msg: ACPMessage): Promise<void>;\n sendRaw(chunk: string): void;\n read(): Promise<ACPMessage | null>;\n close(): void;\n onMessage(handler: (msg: ACPMessage) => void): () => void;\n}\n\n/**\n * Minimal client-side transport contract `ACPSession` drives. `ClientTransport`\n * (stdio subprocess) and `WebSocketClientTransport` (remote) both implement it,\n * so the session is agnostic to how bytes reach the agent.\n */\nexport interface ACPClientTransport {\n start(): Promise<void>;\n send(msg: ACPMessage): Promise<void>;\n onMessage(handler: (msg: ACPMessage) => void): () => void;\n stop(): void;\n}\n\nexport class StdioTransport implements AgentServerTransport {\n private readonly stdin = process.stdin;\n private readonly stdout = process.stdout;\n private readonly stderr = process.stderr;\n\n private buffer = '';\n private readonly handlers = new Set<(msg: ACPMessage) => void>();\n private closed = false;\n private resolveRead: ((msg: ACPMessage | null) => void) | null = null;\n private messageQueue: ACPMessage[] = [];\n private readonly maxFrameChars: number;\n private readonly maxQueuedMessages: number;\n\n constructor(opts: { maxFrameChars?: number; maxQueuedMessages?: number } = {}) {\n this.maxFrameChars = positiveLimit(opts.maxFrameChars, DEFAULT_MAX_FRAME_CHARS);\n this.maxQueuedMessages = positiveLimit(opts.maxQueuedMessages, DEFAULT_MAX_QUEUED_MESSAGES);\n this.stdin.resume();\n this.stdin.setEncoding('utf8');\n this.stdin.on('data', (chunk: string) => this.onData(chunk));\n this.stdin.on('end', () => this.handleClose());\n this.stdin.on('error', (err: Error) => this.failAll(err));\n }\n\n sendStartupMarker(): void {\n this.stdout.write('[wstack-acp]\\n', 'utf8');\n }\n\n send(msg: ACPMessage): Promise<void> {\n if (this.closed) return Promise.resolve();\n return new Promise((resolve) => {\n const line = JSON.stringify(msg) + '\\n';\n this.stdout.write(line, 'utf8', () => resolve());\n });\n }\n\n sendRaw(chunk: string): void {\n this.stdout.write(chunk, 'utf8');\n }\n\n read(): Promise<ACPMessage | null> {\n if (this.messageQueue.length > 0)\n return Promise.resolve(expectDefined(this.messageQueue.shift()));\n if (this.closed) return Promise.resolve(null);\n return new Promise((resolve) => {\n this.resolveRead = resolve;\n });\n }\n\n onMessage(handler: (msg: ACPMessage) => void): () => void {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n close(): void {\n this.closed = true;\n this.stdin.pause();\n this.resolveRead?.(null);\n this.resolveRead = null;\n this.buffer = '';\n this.messageQueue.length = 0;\n this.handlers.clear();\n }\n\n private onData(chunk: string): void {\n this.buffer += chunk;\n const lines = this.buffer.split('\\n');\n /* v8 ignore next -- split() always yields \u22651 element, so pop() is never undefined; the ?? '' is defensive. */\n this.buffer = lines.pop() ?? '';\n if (this.buffer.length > this.maxFrameChars) {\n this.stderr.write(\n `[wstack-acp frame error] pending frame exceeds ${this.maxFrameChars} characters\\n`,\n 'utf8',\n );\n this.close();\n return;\n }\n\n for (const raw of lines) {\n if (!raw.trim()) continue;\n if (raw.length > this.maxFrameChars) {\n this.stderr.write(\n `[wstack-acp frame error] frame exceeds ${this.maxFrameChars} characters\\n`,\n 'utf8',\n );\n this.close();\n return;\n }\n try {\n this.dispatch(JSON.parse(raw) as ACPMessage);\n } catch (err) {\n this.stderr.write(`[wstack-acp parse error] ${err}\\n`, 'utf8');\n }\n }\n }\n\n private dispatch(msg: ACPMessage): void {\n if (this.resolveRead) {\n const resolve = this.resolveRead;\n this.resolveRead = null;\n resolve(msg);\n } else {\n if (this.messageQueue.length >= this.maxQueuedMessages) {\n this.stderr.write(\n `[wstack-acp queue error] pending message queue exceeds ${this.maxQueuedMessages} entries\\n`,\n 'utf8',\n );\n this.close();\n return;\n }\n this.messageQueue.push(msg);\n }\n for (const handler of this.handlers) {\n try {\n handler(msg);\n } catch (err) {\n this.stderr.write(`[wstack-acp handler error] ${err}\\n`, 'utf8');\n }\n }\n }\n\n private handleClose(): void {\n this.close();\n }\n\n private failAll(err: Error): void {\n this.stderr.write(`[wstack-acp stdin error] ${err.message}\\n`, 'utf8');\n this.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// ClientTransport \u2014 spawns a child ACP agent process (DIR-1)\n// ---------------------------------------------------------------------------\n\nimport type { EventEmitter } from 'node:events';\n\nexport interface ClientTransportOptions {\n command: string;\n args?: string[] | undefined;\n env?: Record<string, string>;\n cwd?: string | undefined;\n handshakeTimeoutMs?: number | undefined;\n /**\n * Set to true when the child is an external ACP agent (Claude Code,\n * Gemini CLI, Codex CLI, \u2026) that does NOT emit a `[wstack-acp]\\n`\n * marker on startup. The v1 client (`ACPSession`) sets this; the\n * server-side transport (the default) keeps the marker check.\n */\n skipHandshakeMarker?: boolean | undefined;\n /** Maximum pending newline-delimited JSON frame size. Default 20 MiB. */\n maxFrameChars?: number | undefined;\n /** Maximum messages retained for the optional read() API. Default 1000. */\n maxQueuedMessages?: number | undefined;\n}\n\nexport interface ACPChildProcess extends EventEmitter {\n stdout: NodeJS.ReadableStream;\n stdin: NodeJS.WritableStream;\n stderr: NodeJS.ReadableStream;\n pid: number | undefined;\n kill(): void;\n}\n\nexport class ClientTransport implements ACPClientTransport {\n private child: ACPChildProcess | null = null;\n private buffer = '';\n private readonly handlers = new Set<(msg: ACPMessage) => void>();\n private closed = false;\n private resolveRead: ((msg: ACPMessage | null) => void) | null = null;\n private messageQueue: ACPMessage[] = [];\n private readonly opts: Required<Pick<ClientTransportOptions, 'handshakeTimeoutMs'>> &\n ClientTransportOptions;\n private readonly maxFrameChars: number;\n private readonly maxQueuedMessages: number;\n\n constructor(options: ClientTransportOptions) {\n this.opts = {\n handshakeTimeoutMs: 30_000,\n ...options,\n };\n this.maxFrameChars = positiveLimit(options.maxFrameChars, DEFAULT_MAX_FRAME_CHARS);\n this.maxQueuedMessages = positiveLimit(options.maxQueuedMessages, DEFAULT_MAX_QUEUED_MESSAGES);\n }\n\n async start(): Promise<void> {\n if (this.child) return;\n const [{ spawn }, { buildChildEnv }, os] = await Promise.all([\n import('node:child_process'),\n import('@wrongstack/core/utils'),\n import('node:os'),\n ]);\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(\n new Error(`ACP child process failed to start within ${this.opts.handshakeTimeoutMs}ms`),\n );\n }, this.opts.handshakeTimeoutMs);\n\n // `npx`/`uvx` resolve+install the package using the npm/pip config of\n // the spawn cwd. Inside a repo with dependency `overrides` (WrongStack\n // pins undici/jsdom), that install fails with EOVERRIDE and the adapter\n // never starts \u2192 handshake timeout. Spawn package launchers from a\n // NEUTRAL dir (home) so they install cleanly; the agent still learns the\n // project directory via the ACP `session/new` `cwd` param, not this cwd.\n const isPkgLauncher = this.opts.command === 'npx' || this.opts.command === 'uvx';\n const spawnCwd = isPkgLauncher ? os.homedir() : this.opts.cwd;\n\n try {\n const childArgs = this.opts.args ?? [];\n const invocation = spawnInvocation(this.opts.command, childArgs, process.platform);\n this.child = spawn(invocation.command, invocation.args, {\n env: { ...buildChildEnv(), ...this.opts.env },\n cwd: spawnCwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n ...verbatimOptions(invocation),\n }) as never as ACPChildProcess;\n /* v8 ignore start -- spawn() throwing synchronously is a defensive guard (e.g. argv0 type errors); the realistic async failure path is the child 'error' event, covered by tests. */\n } catch (err) {\n clearTimeout(timeout);\n reject(err);\n return;\n }\n /* v8 ignore stop */\n\n const child = this.child;\n\n child.stdout.setEncoding('utf8');\n\n let settled = false;\n // Register failure handlers IMMEDIATELY, before either readiness path,\n // so a spawn failure (ENOENT / EACCES) rejects start() instead of\n // emitting an unhandled 'error' event that crashes the host process.\n // This is critical for the skip-marker path (external ACP agents),\n // which previously returned before any 'error' listener was attached.\n const onSpawnFailure = (err: Error): void => {\n if (settled) {\n // Post-ready error: just tear the connection down.\n this.closed = true;\n return;\n }\n settled = true;\n clearTimeout(timeout);\n reject(err);\n };\n child.on('error', onSpawnFailure);\n child.stdout.on('error', onSpawnFailure);\n\n if (this.opts.skipHandshakeMarker) {\n // External ACP agents don't emit a startup marker. Attach the data\n // pump right away so no early output is dropped, then resolve once\n // the OS confirms the process actually spawned (the 'spawn' event).\n // If the binary is missing, 'error' fires instead and rejects above.\n child.stdout.on('data', (c: string) => this.onChildData(c));\n child.stderr.on('data', (c: string) => this.onChildError(c));\n child.on('close', (code: number | null) => this.onChildClose(code));\n child.once('spawn', () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolve();\n });\n return;\n }\n\n const onReady = (): void => {\n if (settled) return;\n settled = true;\n child.stdout.on('data', (c: string) => this.onChildData(c));\n child.stderr.on('data', (c: string) => this.onChildError(c));\n child.on('close', (code: number | null) => this.onChildClose(code));\n clearTimeout(timeout);\n resolve();\n };\n\n const waitForMarker = (chunk: string) => {\n this.buffer += chunk;\n // The framed paths (onData / onChildData) both enforce maxFrameChars;\n // this handshake path did not, so a child that never emits the marker\n // grew the buffer without limit until the connect timeout fired.\n // Keeping the tail is safe: the marker can only be found near the end.\n if (this.buffer.length > this.maxFrameChars) {\n this.buffer = this.buffer.slice(-this.maxFrameChars);\n }\n const idx = this.buffer.indexOf('[wstack-acp]\\n');\n if (idx !== -1) {\n this.buffer = this.buffer.slice(idx + '[wstack-acp]\\n'.length);\n child.stdout.removeListener('data', waitForMarker);\n onReady();\n }\n };\n\n child.stdout.on('data', waitForMarker);\n });\n }\n\n send(msg: ACPMessage): Promise<void> {\n if (!this.child) return Promise.reject(new Error('ClientTransport not started'));\n return new Promise((resolve, reject) => {\n const line = JSON.stringify(msg) + '\\n';\n this.child?.stdin.write(line, 'utf8', (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n read(): Promise<ACPMessage | null> {\n if (this.messageQueue.length > 0)\n return Promise.resolve(expectDefined(this.messageQueue.shift()));\n if (this.closed) return Promise.resolve(null);\n return new Promise((resolve) => {\n this.resolveRead = resolve;\n });\n }\n\n onMessage(handler: (msg: ACPMessage) => void): () => void {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n stop(): void {\n this.closed = true;\n this.resolveRead?.(null);\n this.resolveRead = null;\n this.buffer = '';\n this.messageQueue.length = 0;\n this.handlers.clear();\n const child = this.child;\n if (!child) return;\n // On Windows `child` is the cmd.exe shim wrapper; a bare kill() orphans the\n // real agent grandchild. treeKill tears down the whole process tree.\n treeKill(child);\n this.child = null;\n }\n\n private onChildData(chunk: string): void {\n this.buffer += chunk;\n const lines = this.buffer.split('\\n');\n /* v8 ignore next -- split() always yields \u22651 element, so pop() is never undefined; the ?? '' is defensive. */\n this.buffer = lines.pop() ?? '';\n if (this.buffer.length > this.maxFrameChars) {\n writeErr(`[acp-child pending frame exceeds ${this.maxFrameChars} characters]\\n`);\n this.stop();\n return;\n }\n\n for (const raw of lines) {\n if (!raw.trim()) continue;\n if (raw.length > this.maxFrameChars) {\n writeErr(`[acp-child frame exceeds ${this.maxFrameChars} characters]\\n`);\n this.stop();\n return;\n }\n try {\n this.dispatch(JSON.parse(raw) as ACPMessage);\n } catch {\n // skip malformed\n }\n }\n }\n\n private onChildError(chunk: string): void {\n writeErr(`[acp-child stderr] ${chunk}`);\n }\n\n private onChildClose(code: number | null): void {\n this.closed = true;\n this.resolveRead?.(null);\n this.resolveRead = null;\n this.buffer = '';\n this.messageQueue.length = 0;\n this.handlers.clear();\n if (code !== 0 && code !== null) {\n writeErr(`[acp-child exited with code ${code}]\\n`);\n }\n }\n\n private dispatch(msg: ACPMessage): void {\n if (this.resolveRead) {\n const resolve = this.resolveRead;\n this.resolveRead = null;\n resolve(msg);\n } else if (this.handlers.size === 0) {\n if (this.messageQueue.length >= this.maxQueuedMessages) {\n writeErr(`[acp-child message queue exceeds ${this.maxQueuedMessages} entries]\\n`);\n this.stop();\n return;\n }\n this.messageQueue.push(msg);\n }\n for (const handler of this.handlers) {\n try {\n handler(msg);\n } catch {\n // non-fatal\n }\n }\n }\n}\n\nfunction spawnInvocation(\n command: string,\n args: string[],\n platform: NodeJS.Platform,\n): {\n command: string;\n args: string[];\n windowsVerbatimArguments?: true;\n} {\n if (platform !== 'win32') return { command, args };\n return buildWin32CmdShimInvocation(command, args);\n}\n\nfunction verbatimOptions(invocation: { windowsVerbatimArguments?: true }): {\n windowsVerbatimArguments?: true;\n} {\n return invocation.windowsVerbatimArguments\n ? { windowsVerbatimArguments: invocation.windowsVerbatimArguments }\n : {};\n}\n\n/** Direct-module test seam; not re-exported by the package barrel. */\nexport const stdioTransportCoverage = { positiveLimit, spawnInvocation, verbatimOptions };\n", "const WIN32_CMD_META = /[&|<>\"\\r\\n\\0]/;\n\nexport interface Win32CmdShimInvocation {\n command: string;\n args: string[];\n windowsVerbatimArguments: true;\n}\n\nexport function buildWin32CmdShimInvocation(\n command: string,\n args: readonly string[] = [],\n): Win32CmdShimInvocation {\n assertSafeWin32CmdArgs([command, ...args]);\n const line = ['call', quoteWin32CmdArg(command), ...args.map(quoteWin32CmdArg)].join(' ');\n return {\n command: process.env['COMSPEC'] ?? 'cmd.exe',\n args: ['/d', '/c', line],\n windowsVerbatimArguments: true,\n };\n}\n\nfunction assertSafeWin32CmdArgs(args: readonly unknown[]): void {\n for (const arg of args) {\n if (typeof arg === 'string' && WIN32_CMD_META.test(arg)) {\n throw new Error(\n 'win32 cmd shim spawn: argument contains a shell metacharacter ' +\n '(one of & | < > \", or a newline) that could enable command injection ' +\n 'through the .cmd/.bat wrapper - refusing to run. Offending argument: ' +\n JSON.stringify(arg),\n );\n }\n }\n}\n\nfunction quoteWin32CmdArg(arg: string): string {\n return `\"${arg}\"`;\n}\n", "/**\n * ACP v1 type definitions \u2014 Agent Client Protocol, stable v1 spec.\n *\n * Scope: discriminated union for the `session/update` notification payload\n * (the `update` field of a `session/update` JSON-RPC notification), plus the\n * subset of supporting types it depends on.\n *\n * Spec: https://agentclientprotocol.com/protocol/v1/overview\n *\n * Design notes\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * \u2022 The stable v1 spec defines 11 `sessionUpdate` discriminator values. We\n * type exactly those 11, plus an `_unstable_*` escape hatch for v2-RFD\n * kinds (e.g. `next_edit_suggestions`, `elicitation`) that real agents\n * may emit before the spec stabilises them, and an `unknown` fallback for\n * everything else. We do NOT synthesise 32 fake variants to match a\n * number cited in passing \u2014 the union is honest about the surface.\n *\n * \u2022 Per the spec's conventions, discriminator values use snake_case. The\n * property keys inside each variant are camelCase (the JSON-RPC envelope\n * is JSON-RPC 2.0, everything else is camelCase unless the spec says\n * otherwise).\n *\n * \u2022 Optional fields that the spec marks optional are marked `?:`. Required\n * fields have no `?`. We do not include spec fields the spec marks\n * \"SHOULD NOT\" or \"reserved\".\n *\n * \u2022 The existing `acp-messages.ts` types describe an older draft of the\n * protocol (string `protocolVersion: '2024-11'`, fake `tools/call`\n * method, etc.). Do NOT import from it here \u2014 `acp-v1.ts` is\n * self-contained so the new code path can be reviewed in isolation and\n * deleted wholesale if the rewrite is ever reverted.\n */\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Shared building blocks\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Stable protocol version (integer per the spec, not a date-string). */\nexport const ACP_PROTOCOL_VERSION = 1 as const;\nexport type ACPProtocolVersion = typeof ACP_PROTOCOL_VERSION;\n\n/** Per the spec: opaque, unique id. We type as branded string. */\nexport type SessionId = string & { readonly __acpSessionId: unique symbol };\nexport type ToolCallId = string & { readonly __acpToolCallId: unique symbol };\nexport type MessageId = string & { readonly __acpMessageId: unique symbol };\nexport type TerminalId = string & { readonly __acpTerminalId: unique symbol };\nexport type PlanEntryId = string & { readonly __acpPlanEntryId: unique symbol };\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Implementation metadata\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface Implementation {\n /** Programmatic/logical name \u2014 also a display-name fallback if title is absent. */\n name: string;\n /** Human-readable display name for UI contexts. */\n title?: string | undefined;\n /** Version string (display/debug/metrics). */\n version: string;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Client capabilities \u2014 sent in the initialize request\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ClientCapabilities {\n fs?: {\n readTextFile?: boolean | undefined;\n writeTextFile?: boolean | undefined;\n } | undefined;\n terminal?: boolean | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Agent capabilities \u2014 received in the initialize response\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface PromptCapabilities {\n image?: boolean | undefined;\n audio?: boolean | undefined;\n embeddedContext?: boolean | undefined;\n}\n\nexport interface McpCapabilities {\n http?: boolean | undefined;\n sse?: boolean | undefined;\n}\n\nexport interface SessionCapabilities {\n close?: Record<string, unknown> | undefined;\n list?: Record<string, unknown> | undefined;\n delete?: Record<string, unknown> | undefined;\n resume?: Record<string, unknown> | undefined;\n additionalDirectories?: Record<string, unknown> | undefined;\n}\n\nexport interface AuthCapabilities {\n logout?: Record<string, unknown> | undefined;\n}\n\nexport interface AgentCapabilities {\n loadSession?: boolean | undefined;\n promptCapabilities?: PromptCapabilities | undefined;\n mcpCapabilities?: McpCapabilities | undefined;\n sessionCapabilities?: SessionCapabilities | undefined;\n auth?: AuthCapabilities | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Authentication\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface AuthMethod {\n id: string;\n name: string;\n description?: string | undefined;\n type?: 'agent' | 'oauth' | 'http' | undefined;\n}\n\nexport interface AuthenticateRequest {\n methodId: string;\n}\n\nexport type AuthenticateResponse = {}\n\nexport type LogoutRequest = {}\n\nexport type LogoutResponse = {}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// MCP server configuration \u2014 sent in session lifecycle requests\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface StdioMcpServer {\n name: string;\n command: string;\n args?: string[] | undefined;\n env?: { name: string; value: string }[] | undefined;\n}\n\nexport interface HttpMcpServer {\n type: 'http';\n name: string;\n url: string;\n headers?: { name: string; value: string }[] | undefined;\n}\n\nexport interface SseMcpServer {\n type: 'sse';\n name: string;\n url: string;\n headers?: { name: string; value: string }[] | undefined;\n}\n\nexport type McpServer = StdioMcpServer | HttpMcpServer | SseMcpServer;\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Session lifecycle \u2014 request/response types\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface NewSessionRequest {\n cwd: string;\n mcpServers: McpServer[];\n additionalDirectories?: string[] | undefined;\n}\n\nexport interface NewSessionResponse {\n sessionId: SessionId;\n initialMode?: SessionModeState | null | undefined;\n configOptions?: SessionConfigOption[] | null | undefined;\n}\n\nexport interface LoadSessionRequest {\n sessionId: SessionId;\n cwd: string;\n mcpServers: McpServer[];\n additionalDirectories?: string[] | undefined;\n}\n\nexport interface LoadSessionResponse {\n initialMode?: SessionModeState | null | undefined;\n configOptions?: SessionConfigOption[] | null | undefined;\n}\n\nexport interface ResumeSessionRequest {\n sessionId: SessionId;\n cwd: string;\n mcpServers: McpServer[];\n additionalDirectories?: string[] | undefined;\n}\n\nexport interface ResumeSessionResponse {\n initialMode?: SessionModeState | null | undefined;\n configOptions?: SessionConfigOption[] | null | undefined;\n}\n\nexport interface CloseSessionRequest {\n sessionId: SessionId;\n}\n\nexport type CloseSessionResponse = {}\n\nexport interface ListSessionsRequest {\n cursor?: string | undefined;\n cwd?: string | undefined;\n}\n\nexport interface ListSessionsResponse {\n sessions: SessionInfo[];\n nextCursor?: string | undefined;\n}\n\nexport interface DeleteSessionRequest {\n sessionId: SessionId;\n}\n\nexport type DeleteSessionResponse = {}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Session config options\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface SessionConfigOption {\n id: string;\n name: string;\n description?: string | undefined;\n category?: ConfigOptionCategory | undefined;\n type: ConfigOptionType;\n defaultValue?: string | undefined;\n currentValue: string;\n options: ConfigOptionValue[];\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Content blocks \u2014 reused from MCP per the spec\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Annotations attached to a content block. Optional, agent-supplied hint\n * about audience/priority. Spec leaves shape open; we mirror the fields\n * the spec shows in its examples.\n */\nexport interface ContentAnnotations {\n audience?: ('user' | 'assistant')[] | undefined;\n priority?: number | undefined;\n [key: string]: unknown;\n}\n\nexport interface TextContent {\n type: 'text';\n text: string;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface ImageContent {\n type: 'image';\n mimeType: string;\n /** Base64-encoded image data. */\n data: string;\n uri?: string | undefined;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface AudioContent {\n type: 'audio';\n mimeType: string;\n /** Base64-encoded audio data. */\n data: string;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface TextResourceContents {\n uri: string;\n mimeType?: string | undefined;\n text: string;\n}\n\nexport interface BlobResourceContents {\n uri: string;\n mimeType?: string | undefined;\n /** Base64-encoded binary. */\n blob: string;\n}\n\nexport type EmbeddedResourceContents = TextResourceContents | BlobResourceContents;\n\nexport interface EmbeddedResourceContent {\n type: 'resource';\n resource: EmbeddedResourceContents;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface ResourceLinkContent {\n type: 'resource_link';\n uri: string;\n name: string;\n mimeType?: string | undefined;\n title?: string | undefined;\n description?: string | undefined;\n size?: number | undefined;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport type ContentBlock =\n | TextContent\n | ImageContent\n | AudioContent\n | EmbeddedResourceContent\n | ResourceLinkContent;\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Tool calls\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type ToolKind =\n | 'read'\n | 'edit'\n | 'delete'\n | 'move'\n | 'search'\n | 'execute'\n | 'think'\n | 'fetch'\n | 'switch_mode'\n | 'other';\n\nexport type ToolCallStatus = 'pending' | 'in_progress' | 'completed' | 'failed';\n\n/** A single concrete content payload attached to a tool call. */\nexport type ToolCallContent =\n | { type: 'content'; content: ContentBlock }\n | {\n type: 'diff';\n path: string;\n oldText: string | null;\n newText: string;\n }\n | { type: 'terminal'; terminalId: TerminalId };\n\nexport interface ToolCallLocation {\n path: string;\n /** 1-based per the spec's argument requirements. */\n line?: number | undefined;\n}\n\nexport interface ToolCall {\n toolCallId: ToolCallId;\n title: string;\n kind?: ToolKind | undefined;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n rawOutput?: Record<string, unknown> | undefined;\n}\n\n/**\n * Partial update of a previously-emitted tool call. All fields except\n * `toolCallId` are optional \u2014 only the changed fields are included.\n * Declared standalone (not `extends ToolCall`) because `title` is required\n * on `ToolCall` but optional here; the structural variance is the point.\n */\nexport interface ToolCallUpdateFields {\n toolCallId: ToolCallId;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n title?: string | undefined;\n kind?: ToolKind | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n rawOutput?: Record<string, unknown> | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Plan\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type PlanEntryPriority = 'high' | 'medium' | 'low';\nexport type PlanEntryStatus = 'pending' | 'in_progress' | 'completed';\n\nexport interface PlanEntry {\n /** Required by the spec for the array shape, but per-entry id is optional. */\n content: string;\n priority: PlanEntryPriority;\n status: PlanEntryStatus;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Slash commands\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface AvailableCommandInput {\n hint: string;\n}\n\nexport interface AvailableCommand {\n name: string;\n description: string;\n input?: AvailableCommandInput | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Session modes\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type SessionModeId = string & { readonly __acpModeId: unique symbol };\n\nexport interface SessionMode {\n id: SessionModeId;\n name: string;\n description?: string | undefined;\n}\n\nexport interface SessionModeState {\n currentModeId: SessionModeId;\n availableModes: SessionMode[];\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Config options\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Reserved spec categories. Underscore-prefixed names are free for custom use. */\nexport type ConfigOptionCategory =\n | 'mode'\n | 'model'\n | 'thought_level'\n | `_${string}`;\n\nexport type ConfigOptionType = 'select' | string;\n\nexport interface ConfigOptionValue {\n value: string;\n name: string;\n description?: string | undefined;\n}\n\nexport interface ConfigOption {\n id: string;\n name: string;\n description?: string | undefined;\n category?: ConfigOptionCategory | undefined;\n type: ConfigOptionType;\n currentValue: string;\n options: ConfigOptionValue[];\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Session info\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface SessionInfo {\n sessionId: SessionId;\n cwd: string;\n title?: string | undefined;\n updatedAt?: string | undefined;\n /** Agent-supplied extension metadata; opaque to clients. */\n _meta?: Record<string, unknown> | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Usage (token / cost) updates\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface UsageCost {\n amount: number;\n /** ISO 4217 currency code, e.g. \"USD\". */\n currency: string;\n}\n\nexport interface UsageUpdate {\n /** Tokens used in the current session context. Required, non-null. */\n used: number;\n /** Total context window size in tokens. Required, non-null. */\n size: number;\n cost?: UsageCost | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Permission requests\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type PermissionOptionKind =\n | 'allow_once'\n | 'allow_always'\n | 'reject_once'\n | 'reject_always';\n\nexport interface PermissionOption {\n optionId: string;\n name: string;\n kind: PermissionOptionKind;\n}\n\nexport type RequestPermissionOutcome =\n | { outcome: 'cancelled' }\n | { outcome: 'selected'; optionId: string };\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Stop reasons\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type StopReason =\n | 'end_turn'\n | 'max_tokens'\n | 'max_turn_requests'\n | 'refusal'\n | 'cancelled'\n | string;\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// SessionUpdate \u2014 the discriminated union\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Stable v1 variants. The spec currently defines exactly 11. */\nexport type SessionUpdate =\n | UserMessageChunkUpdate\n | AgentMessageChunkUpdate\n | ThoughtChunkUpdate\n | ToolCallUpdateUpdate\n | ToolCallUpdateNotification\n | PlanUpdate\n | AvailableCommandsUpdate\n | CurrentModeUpdate\n | ConfigOptionUpdate\n | SessionInfoUpdate\n | UsageUpdateUpdate;\n\n// --- Streaming message chunks -----------------------------------------------\n\nexport interface UserMessageChunkUpdate {\n sessionUpdate: 'user_message_chunk';\n messageId?: MessageId | undefined;\n content: ContentBlock;\n}\n\nexport interface AgentMessageChunkUpdate {\n sessionUpdate: 'agent_message_chunk';\n messageId?: MessageId | undefined;\n content: ContentBlock;\n}\n\nexport interface ThoughtChunkUpdate {\n sessionUpdate: 'thought_chunk';\n messageId?: MessageId | undefined;\n content: ContentBlock;\n}\n\n// --- Tool calls ------------------------------------------------------------\n\n/** First notification for a new tool call. */\nexport interface ToolCallUpdateUpdate {\n sessionUpdate: 'tool_call';\n toolCallId: ToolCallId;\n title: string;\n kind?: ToolKind | undefined;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n}\n\n/** Subsequent updates to a previously-emitted tool call. */\nexport interface ToolCallUpdateNotification {\n sessionUpdate: 'tool_call_update';\n toolCallId: ToolCallId;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n title?: string | undefined;\n kind?: ToolKind | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n rawOutput?: Record<string, unknown> | undefined;\n}\n\n// --- Plan ------------------------------------------------------------------\n\nexport interface PlanUpdate {\n sessionUpdate: 'plan';\n entries: PlanEntry[];\n}\n\n// --- Commands / modes / config ---------------------------------------------\n\nexport interface AvailableCommandsUpdate {\n sessionUpdate: 'available_commands_update';\n availableCommands: AvailableCommand[];\n}\n\nexport interface CurrentModeUpdate {\n sessionUpdate: 'current_mode_update';\n modeId: SessionModeId;\n}\n\nexport interface ConfigOptionUpdate {\n sessionUpdate: 'config_option_update';\n configOptions: ConfigOption[];\n}\n\n// --- Session metadata ------------------------------------------------------\n\nexport interface SessionInfoUpdate {\n sessionUpdate: 'session_info_update';\n title?: string | null | undefined;\n updatedAt?: string | null | undefined;\n _meta?: Record<string, unknown> | undefined;\n}\n\n// --- Usage -----------------------------------------------------------------\n\nexport interface UsageUpdateUpdate {\n sessionUpdate: 'usage_update';\n used: number;\n size: number;\n cost?: UsageCost | undefined;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Escape hatches: unknown / unstable\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Escape hatch for v2-RFD `sessionUpdate` kinds that have been published\n * but are not yet stabilised in the v1 spec. Examples seen in the wild:\n * `next_edit_suggestions`, `elicitation`, `proxy_extension`. We surface\n * the raw payload so forward-compat code can switch on\n * `kind === 'next_edit_suggestions'` etc. without losing the data.\n */\nexport interface UnstableSessionUpdate {\n sessionUpdate: `_unstable_${string}`;\n [key: string]: unknown;\n}\n\n/**\n * Last-resort variant: the agent sent a discriminator string we don't\n * recognise at all. The full payload is preserved as a record so consumers\n * can still log/inspect it. Prefer matching the known variants first.\n */\nexport interface UnknownSessionUpdate {\n sessionUpdate: string;\n [key: string]: unknown;\n}\n\n/** The full union, including escape hatches. */\nexport type AnySessionUpdate = SessionUpdate | UnstableSessionUpdate | UnknownSessionUpdate;\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Top-level notification envelope\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface SessionUpdateNotification {\n jsonrpc?: '2.0' | undefined;\n method: 'session/update';\n params: {\n sessionId: SessionId;\n update: AnySessionUpdate;\n };\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Type guards\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Exhaustiveness helper. Call from the `default:` branch of a switch on\n * `sessionUpdate` to get a compile-time error when a new variant is added\n * without updating the consumer.\n */\nexport function assertNeverSessionUpdate(x: never): never {\n throw new Error(\n `Unhandled sessionUpdate: ${JSON.stringify(x)}`,\n );\n}\n", "import type {\n ContentBlock,\n StopReason,\n} from '../types/acp-v1.js';\nimport type { ACPSessionRunResult } from './acp-session-types.js';\n\n/**\n * Create a text ContentBlock. Convenience helper for callers of\n * `session.prompt()`.\n */\nexport function textContent(text: string): ContentBlock {\n return { type: 'text', text };\n}\n\n/**\n * Create an image ContentBlock. Only send this if the agent's\n * `promptCapabilities.image` is `true` (check via\n * `session.getCapabilities().promptCapabilities?.image`).\n */\nexport function imageContent(mimeType: string, data: string): ContentBlock {\n return { type: 'image', mimeType, data };\n}\n\n/**\n * Create an audio ContentBlock. Only send this if the agent's\n * `promptCapabilities.audio` is `true` (check via\n * `session.getCapabilities().promptCapabilities?.audio`).\n */\nexport function audioContent(mimeType: string, data: string): ContentBlock {\n return { type: 'audio', mimeType, data };\n}\n\nexport function extractText(block: unknown): string {\n if (typeof block !== 'object' || block === null) return '';\n const b = block as {\n type?: string;\n text?: unknown;\n resource?: { text?: unknown };\n };\n if (b.type === 'text' && typeof b.text === 'string') return b.text;\n // Embedded text resources carry their content under `resource.text`.\n if (\n b.type === 'resource' &&\n b.resource &&\n typeof b.resource === 'object' &&\n typeof b.resource.text === 'string'\n ) {\n return b.resource.text;\n }\n return '';\n}\n\nexport function isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/** A fully-populated empty run result (used for pre-aborted short-circuits). */\nexport function emptyRunResult(stopReason: StopReason): ACPSessionRunResult {\n return {\n text: '',\n stopReason,\n hasText: false,\n toolCalls: [],\n diffs: [],\n thoughts: '',\n };\n}\n", "/**\n * FileServer \u2014 answers `fs/read_text_file` and `fs/write_text_file`\n * from an ACP agent, scoped to a single project root.\n *\n * Per the spec, all file paths in ACP MUST be absolute. We additionally\n * require them to resolve under `projectRoot` after normalisation AND after\n * resolving symlinks (`fs.realpath`). A path that passes a lexical prefix\n * check but points outside the root via an in-project symlink/junction is\n * rejected. This closes the CWE-22/CWE-59 symlink-escape vector that a\n * purely textual containment check leaves open.\n *\n * The server itself is transport-agnostic: the caller (ACPSession)\n * routes incoming fs/* requests to `readTextFile`/`writeTextFile` and\n * sends the result back. Keeping the routing out of this class lets the\n * file logic be unit-tested in isolation.\n */\nimport { randomBytes } from 'node:crypto';\nimport { realpathSync } from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\n\nexport interface FileServerOptions {\n /** Absolute path; only files under this root are accessible. */\n projectRoot: string;\n /** Per-call timeout, default 30s. */\n timeoutMs?: number;\n /**\n * Hard cap on the number of bytes that may be read in a single\n * `readTextFile` call. Protects against a malicious agent requesting a\n * gigantic file to exhaust host memory. Default 5 MiB.\n */\n maxReadBytes?: number;\n /**\n * Hard cap on the number of bytes that may be written in a single\n * `writeTextFile` call. Default 5 MiB.\n */\n maxWriteBytes?: number;\n /** Filesystem implementation override for deterministic tests. */\n operations?: FileServerOperations;\n}\n\nexport interface FileServerOperations {\n stat(file: string): Promise<{ size: number }>;\n readFile(\n file: string,\n options: { encoding: 'utf8'; signal: AbortSignal },\n ): Promise<string>;\n writeFile(\n file: string,\n content: string,\n options: { encoding: 'utf8'; signal: AbortSignal },\n ): Promise<void>;\n realpath(file: string): Promise<string>;\n rename(from: string, to: string): Promise<void>;\n unlink(file: string): Promise<void>;\n}\n\nconst DEFAULT_FILE_OPERATIONS: FileServerOperations = {\n stat: fsp.stat,\n readFile: fsp.readFile,\n writeFile: fsp.writeFile,\n realpath: fsp.realpath,\n rename: fsp.rename,\n unlink: fsp.unlink,\n};\n\nexport interface ReadFileParams {\n sessionId: string;\n path: string;\n}\n\nexport interface WriteFileParams {\n sessionId: string;\n path: string;\n content: string;\n}\n\nexport type FsErrorCode =\n | 'ENOENT'\n | 'EACCES'\n | 'OUTSIDE_ROOT'\n | 'TIMEOUT'\n | 'INVALID_PATH'\n | 'TOO_LARGE';\n\nconst DEFAULT_MAX_READ_BYTES = 5 * 1024 * 1024;\nconst DEFAULT_MAX_WRITE_BYTES = 5 * 1024 * 1024;\n\n/**\n * Thrown for protocol-level rejections (path outside root, etc.).\n * The session converts these into JSON-RPC error responses.\n */\nexport class FsError extends Error {\n readonly code: FsErrorCode;\n readonly path: string;\n constructor(code: FsErrorCode, path: string, message: string) {\n super(message);\n this.name = 'FsError';\n this.code = code;\n this.path = path;\n }\n}\n\nexport class FileServer {\n private readonly root: string;\n private readonly realRoot: string;\n private readonly timeoutMs: number;\n private readonly maxReadBytes: number;\n private readonly maxWriteBytes: number;\n private readonly operations: FileServerOperations;\n\n constructor(opts: FileServerOptions) {\n this.root = path.resolve(opts.projectRoot);\n // Resolve the root itself once \u2014 it may be a symlink (macOS /var \u2192\n // /private/var, Windows 8.3 short names, etc.). All realpaths are\n // compared against this canonical root for a like-for-like check.\n // Synchronous in constructor: the root must exist for an ACP session\n // and this keeps the per-call path simple.\n this.realRoot = safeRealpathSync(this.root);\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.maxReadBytes = opts.maxReadBytes ?? DEFAULT_MAX_READ_BYTES;\n this.maxWriteBytes = opts.maxWriteBytes ?? DEFAULT_MAX_WRITE_BYTES;\n this.operations = opts.operations ?? DEFAULT_FILE_OPERATIONS;\n }\n\n /** Read a text file. Returns the content as a string. */\n async readTextFile(params: ReadFileParams): Promise<{ content: string }> {\n const safe = await this.resolveInside(params.path);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n // Size check before reading to prevent loading a huge file.\n const stat = await this.operations.stat(safe).catch((err) => {\n throw mapFsError(err, safe);\n });\n if (stat.size > this.maxReadBytes) {\n throw new FsError(\n 'TOO_LARGE',\n safe,\n `file is ${stat.size} bytes, max read is ${this.maxReadBytes} bytes`,\n );\n }\n const content = await this.operations.readFile(safe, {\n encoding: 'utf8',\n signal: controller.signal,\n });\n return { content };\n } catch (err) {\n if (err instanceof FsError) throw err;\n if (controller.signal.aborted) {\n throw new FsError('TIMEOUT', safe, `readTextFile timed out after ${this.timeoutMs}ms`);\n }\n throw mapFsError(err, safe);\n } finally {\n clearTimeout(timer);\n }\n }\n\n /** Write a text file. Atomic via write-then-rename. */\n async writeTextFile(params: WriteFileParams): Promise<void> {\n const byteLength = Buffer.byteLength(params.content, 'utf8');\n if (byteLength > this.maxWriteBytes) {\n throw new FsError(\n 'TOO_LARGE',\n params.path,\n `content is ${byteLength} bytes, max write is ${this.maxWriteBytes} bytes`,\n );\n }\n\n const safe = await this.resolveInside(params.path);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n const tmp = `${safe}.${randomBytes(6).toString('hex')}.tmp`;\n try {\n await this.operations.writeFile(tmp, params.content, {\n encoding: 'utf8',\n signal: controller.signal,\n });\n // Re-verify both tmp and the rename destination's parent dir before\n // rename. This closes the TOCTOU window where an attacker could plant a\n // symlink at the destination's parent between resolveInside and rename.\n await this.assertRealInside(tmp);\n await this.assertRealInside(path.dirname(safe));\n await this.operations.rename(tmp, safe);\n } catch (err) {\n if (err instanceof FsError) {\n // Best-effort cleanup of the tmp file\n await this.operations.unlink(tmp).catch(() => undefined);\n throw err;\n }\n // Best-effort cleanup of the tmp file\n try {\n await this.operations.unlink(tmp);\n } catch {\n // tmp didn't exist; ignore\n }\n if (controller.signal.aborted) {\n throw new FsError('TIMEOUT', safe, `writeTextFile timed out after ${this.timeoutMs}ms`);\n }\n throw mapFsError(err, safe);\n } finally {\n clearTimeout(timer);\n }\n }\n\n /**\n * Resolve a path and verify it is inside the project root by realpath.\n * Rejects with `FsError` if the textual path, the resolved path, or the\n * real (symlink-resolved) path escapes the project root.\n *\n * For files that don't exist yet (e.g. a write to a new file), the\n * nearest existing ancestor directory is realpath-checked instead.\n */\n private async resolveInside(p: string): Promise<string> {\n if (typeof p !== 'string' || p.length === 0) {\n throw new FsError('INVALID_PATH', p, 'path is empty or not a string');\n }\n if (!path.isAbsolute(p)) {\n throw new FsError('INVALID_PATH', p, 'path must be absolute (ACP requirement)');\n }\n const resolved = path.resolve(p);\n // +path.sep prevents \"/project-evil\" matching \"/project\" as a prefix.\n const rootWithSep = this.root.endsWith(path.sep) ? this.root : this.root + path.sep;\n if (resolved !== this.root && !resolved.startsWith(rootWithSep)) {\n throw new FsError('OUTSIDE_ROOT', resolved, 'path is outside the project root');\n }\n\n // Now resolve symlinks and compare against the canonical root.\n await this.assertRealInside(resolved);\n return resolved;\n }\n\n /**\n * Resolve `resolvedPath` through `fs.realpath` and verify the result is\n * inside `realRoot`. For non-existent paths (new files), walk up to the\n * nearest existing ancestor and check that instead.\n */\n private async assertRealInside(resolvedPath: string): Promise<void> {\n let probe = resolvedPath;\n for (;;) {\n let real: string;\n try {\n real = await this.operations.realpath(probe);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) {\n throw new FsError('ENOENT', resolvedPath, `no existing ancestor: ${resolvedPath}`);\n }\n probe = parent;\n continue;\n }\n throw mapFsError(err, resolvedPath);\n }\n if (real === this.realRoot || real.startsWith(this.realRoot + path.sep)) return;\n throw new FsError(\n 'OUTSIDE_ROOT',\n resolvedPath,\n 'path resolves through a symlink outside the project root',\n );\n }\n }\n}\n\nfunction mapFsError(err: unknown, p: string): FsError {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'ENOENT') return new FsError('ENOENT', p, `no such file: ${p}`);\n if (code === 'EACCES' || code === 'EPERM') {\n return new FsError('EACCES', p, `permission denied: ${p}`);\n }\n const msg = err instanceof Error ? err.message : String(err);\n return new FsError('INVALID_PATH', p, msg);\n}\n\n/**\n * Synchronous realpath that falls back to the input on failure (root may be\n * a fresh directory that hasn't been statted yet). The constructor needs the\n * canonical root before any async call so per-request resolveInside has a\n * stable comparison target.\n */\nfunction safeRealpathSync(p: string): string {\n try {\n return realpathSync(p);\n } catch {\n return p;\n }\n}\n", "/**\n * Permission policy for ACP v1 client sessions.\n *\n * ACP agents can call `session/request_permission` to ask the user\n * before executing a tool call. The client is expected to surface\n * the question, get a decision, and respond. This module is the seam\n * where WrongStack-specific permission UI can plug in; for v1 we ship\n * a minimal default that auto-approves the first `allow_once` option\n * (or `allow_always` if present) and rejects on abort.\n */\nimport type {\n PermissionOption,\n RequestPermissionOutcome,\n ToolCallUpdateNotification,\n} from '../types/acp-v1.js';\n\n/** A single permission decision request. */\nexport interface PermissionRequest {\n toolCall: ToolCallUpdateNotification;\n options: readonly PermissionOption[];\n signal: AbortSignal;\n}\n\n/** A permission policy decides how to respond to a request. */\nexport type PermissionPolicy = (\n req: PermissionRequest,\n) => Promise<RequestPermissionOutcome>;\n\n/**\n * Default policy: pick the safest-looking allow option if the signal\n * is not aborted, otherwise report cancelled. Order of preference:\n *\n * 1. `allow_always`\n * 2. `allow_once`\n * 3. anything else with `optionId` (last resort)\n *\n * Real WrongStack permission UIs replace this; the contract is the\n * `PermissionPolicy` function type, not the implementation.\n */\nfunction pickAllow(\n options: readonly PermissionOption[],\n): RequestPermissionOutcome {\n const ranked = [...options].sort((a, b) => {\n const score = (k: PermissionOption['kind']): number => {\n if (k === 'allow_once') return 0; // prefer once over always \u2014 least standing grant\n if (k === 'allow_always') return 1;\n if (k === 'reject_once') return 2;\n return 3;\n };\n return score(a.kind) - score(b.kind);\n });\n const chosen = ranked[0];\n if (!chosen || chosen.kind === 'reject_once' || chosen.kind === 'reject_always') {\n return { outcome: 'cancelled' };\n }\n return { outcome: 'selected', optionId: chosen.optionId };\n}\n\nfunction pickReject(\n options: readonly PermissionOption[],\n): RequestPermissionOutcome {\n const reject = options.find(\n (o) => o.kind === 'reject_once' || o.kind === 'reject_always',\n );\n return reject ? { outcome: 'selected', optionId: reject.optionId } : { outcome: 'cancelled' };\n}\n\n/**\n * Tool kinds considered side-effect-free (safe to auto-approve even in a\n * non-interactive run). Everything else (edit/delete/move/execute) mutates\n * the workspace or runs commands and should be gated by a real policy.\n */\nconst READ_ONLY_KINDS = new Set(['read', 'search', 'fetch', 'think']);\n\n/**\n * Default policy: auto-approve the least-standing allow option.\n *\n * \u26A0\uFE0F This auto-approves EVERY tool call, including file writes and shell\n * commands. It exists so non-interactive contexts (CLI `acp spawn`,\n * the Director fan-out) work without a human in the loop. Interactive\n * surfaces (TUI/WebUI) MUST inject a policy that surfaces the request to\n * the user \u2014 pass `permissionPolicy` to `ACPSession` / the subagent runner.\n *\n * NOTE: This is NO LONGER the default for `ACPSession` \u2014 the session now\n * defaults to {@link readOnlyPermissionPolicy} (safe-by-default). Pass this\n * policy explicitly when the agent is trusted and needs write/execute access.\n */\nexport const defaultPermissionPolicy: PermissionPolicy = async (req) => {\n if (req.signal.aborted) return { outcome: 'cancelled' };\n return pickAllow(req.options);\n};\n\n/**\n * Safe-by-default policy: auto-approve only side-effect-free tool calls\n * (read/search/fetch/think); reject anything that would write files or\n * run commands. This is the DEFAULT policy for `ACPSession` when no\n * `permissionPolicy` is provided. Pass {@link defaultPermissionPolicy}\n * explicitly when the agent is trusted and needs write/execute access.\n */\nexport const readOnlyPermissionPolicy: PermissionPolicy = async (req) => {\n if (req.signal.aborted) return { outcome: 'cancelled' };\n const kind = req.toolCall.kind;\n if (kind && READ_ONLY_KINDS.has(kind)) {\n return pickAllow(req.options);\n }\n return pickReject(req.options);\n};\n\n/**\n * Build a policy from a yes/no decision function. The decider receives the\n * tool call (title + kind + rawInput) and returns whether to allow it.\n * This is the seam an interactive host (TUI/WebUI confirm prompt, trust\n * store, exec-allowlist) plugs into.\n */\nexport function makePermissionPolicy(\n decide: (req: PermissionRequest) => boolean | Promise<boolean>,\n): PermissionPolicy {\n return async (req) => {\n if (req.signal.aborted) return { outcome: 'cancelled' };\n const allow = await decide(req);\n return allow ? pickAllow(req.options) : pickReject(req.options);\n };\n}\n", "/**\n * TerminalServer \u2014 answers `terminal/*` methods from an ACP agent.\n *\n * The spec lets agents spawn shell commands inside the client's\n * environment and observe their output. We honour the protocol, but\n * every command runs under a per-process timeout and a byte limit on\n * retained output, both to keep runaway agents from filling memory\n * and to give the runner a clean signal when something is stuck.\n *\n * Scoping: commands run with `cwd` set to the agent's requested cwd\n * if its canonical path is inside `projectRoot`, else `projectRoot`.\n * Agent env entries are overlaid on a credential-scrubbed base, except\n * for variables that enable preload injection or path hijacking.\n */\nimport { spawn } from 'node:child_process';\nimport { realpathSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { buildChildEnv } from '@wrongstack/core/utils';\n\nconst EMPTY_BUFFER = Buffer.alloc(0);\n\nexport interface TerminalServerOptions {\n projectRoot: string;\n /** Hard cap on per-command wall-clock. Default 5 minutes. */\n commandTimeoutMs?: number;\n /** Bytes of output to retain per terminal. Default 1 MiB. */\n outputByteLimit?: number;\n /**\n * Hard maximum cap on the per-call `outputByteLimit`. The agent can request\n * a lower limit per terminal/create, but it can never raise it above this\n * host-configured ceiling. Protects against memory exhaustion from a\n * malicious agent requesting `outputByteLimit: Infinity`. Default 16 MiB.\n */\n maxOutputByteLimit?: number;\n /** Maximum terminal records retained concurrently. Default 32. */\n maxTerminals?: number;\n /** Optional abort signal that kills ALL active terminals. */\n signal?: AbortSignal;\n}\n\ninterface TerminalState {\n proc: ReturnType<typeof spawn>;\n cwd: string;\n command: string;\n args: string[];\n /** Byte chunks retained as a bounded queue. Joined only when output is read. */\n outputChunks: Buffer[];\n /** Index of the first live chunk; avoids O(n) Array.shift calls. */\n outputHead: number;\n /** Bytes currently retained (post-truncation). */\n retainedBytes: number;\n /** True once we've dropped output to fit under the per-call byte limit. */\n truncated: boolean;\n exitStatus?: { exitCode: number | null; signal: string | null } | undefined;\n /** Resolves when the process exits. */\n exitPromise: Promise<{ exitCode: number | null; signal: string | null }>;\n /** Per-terminal timeout handle. */\n timeoutHandle: ReturnType<typeof setTimeout> | null;\n}\n\nexport class TerminalServer {\n private readonly terminals = new Map<string, TerminalState>();\n private readonly projectRoot: string;\n private readonly commandTimeoutMs: number;\n private readonly outputByteLimit: number;\n private readonly maxOutputByteLimit: number;\n private readonly maxTerminals: number;\n private readonly abortSignal: AbortSignal | undefined;\n private readonly abortHandler = (): void => this.releaseAll();\n private nextId = 1;\n\n constructor(opts: TerminalServerOptions) {\n this.projectRoot = path.resolve(opts.projectRoot);\n this.commandTimeoutMs = opts.commandTimeoutMs ?? 5 * 60_000;\n this.outputByteLimit = opts.outputByteLimit ?? 1024 * 1024;\n this.maxOutputByteLimit = opts.maxOutputByteLimit ?? 16 * 1024 * 1024;\n this.maxTerminals = this.clampFiniteInt(opts.maxTerminals, 32);\n this.abortSignal = opts.signal;\n if (opts.signal) {\n opts.signal.addEventListener('abort', this.abortHandler, { once: true });\n }\n }\n\n /** Spawn a new terminal. Returns the agent-facing id. */\n create(params: {\n sessionId: string;\n command: string;\n args?: string[];\n env?: { name: string; value: string }[];\n cwd?: string;\n outputByteLimit?: number;\n }): { terminalId: string } {\n if (this.terminals.size >= this.maxTerminals) {\n throw new Error(\n `terminal limit reached (${this.maxTerminals}); release an existing terminal before creating another`,\n );\n }\n const id = `term_${this.nextId++}`;\n const cwd = this.resolveCwd(params.cwd);\n const perCallByteLimit = Math.min(\n Math.max(1, this.clampFiniteInt(params.outputByteLimit, this.outputByteLimit)),\n this.maxOutputByteLimit,\n );\n const proc = spawn(params.command, params.args ?? [], {\n cwd,\n env: this.buildEnv(params.env),\n stdio: ['ignore', 'pipe', 'pipe'],\n windowsHide: true,\n // shell: false on purpose. The terminal server is invoked with\n // the agent's explicit argv; turning on shell-mode would make\n // the command a single shell-parsed string, which breaks\n // Windows cmd quoting for the common case of running node with\n // `-e \"<script>\"`. If a future feature needs shell features\n // (pipes, redirects), it should be opt-in per-call, not the\n // default.\n });\n\n const state: TerminalState = {\n proc,\n cwd,\n command: params.command,\n args: params.args ?? [],\n outputChunks: [],\n outputHead: 0,\n retainedBytes: 0,\n truncated: false,\n exitStatus: undefined,\n timeoutHandle: null,\n exitPromise: new Promise((resolve) => {\n proc.on('close', (code, signalName) => {\n if (state.timeoutHandle) {\n clearTimeout(state.timeoutHandle);\n state.timeoutHandle = null;\n }\n const exitStatus = {\n exitCode: typeof code === 'number' ? code : null,\n signal: typeof signalName === 'string' ? signalName : null,\n };\n state.exitStatus = exitStatus;\n resolve(exitStatus);\n });\n proc.on('error', (err) => {\n // Spawn-time errors (ENOENT etc.) \u2014 surface as a special\n // exit status with exitCode 127 (command not found).\n if (state.timeoutHandle) {\n clearTimeout(state.timeoutHandle);\n state.timeoutHandle = null;\n }\n const exitStatus = { exitCode: 127, signal: null };\n state.exitStatus = exitStatus;\n let errorOutput = Buffer.from(`[spawn error] ${err.message}\\n`, 'utf8');\n if (errorOutput.length > perCallByteLimit) {\n let start = errorOutput.length - perCallByteLimit;\n while (start < errorOutput.length && (errorOutput[start]! & 0xc0) === 0x80) start++;\n errorOutput = errorOutput.subarray(start);\n state.truncated = true;\n }\n state.outputChunks.push(errorOutput);\n state.retainedBytes = errorOutput.length;\n resolve(exitStatus);\n });\n }),\n };\n\n proc.stdout?.setEncoding('utf8');\n proc.stderr?.setEncoding('utf8');\n const onData = (chunk: string): void => {\n const outputChunk = Buffer.from(chunk, 'utf8');\n state.outputChunks.push(outputChunk);\n state.retainedBytes += outputChunk.length;\n if (state.retainedBytes > perCallByteLimit) state.truncated = true;\n\n // Evict from the head. Each chunk is visited at most twice, so frequent\n // tiny writes do not repeatedly copy the full retained output window.\n while (\n state.retainedBytes > perCallByteLimit &&\n state.outputHead < state.outputChunks.length\n ) {\n const first = state.outputChunks[state.outputHead]!;\n const overflow = state.retainedBytes - perCallByteLimit;\n if (first.length <= overflow) {\n state.outputChunks[state.outputHead] = EMPTY_BUFFER;\n state.outputHead++;\n state.retainedBytes -= first.length;\n continue;\n }\n\n let start = overflow;\n while (start < first.length && (first[start]! & 0xc0) === 0x80) start++;\n state.outputChunks[state.outputHead] = first.subarray(start);\n state.retainedBytes -= start;\n }\n\n if (state.outputHead >= 256 && state.outputHead * 2 >= state.outputChunks.length) {\n state.outputChunks = state.outputChunks.slice(state.outputHead);\n state.outputHead = 0;\n }\n };\n proc.stdout?.on('data', onData);\n proc.stderr?.on('data', onData);\n\n state.timeoutHandle = setTimeout(() => {\n // Best-effort kill; we don't have an exact \"TIMEOUT\" stop reason\n // so we just exit with -1.\n try {\n proc.kill('SIGTERM');\n } catch {\n // already dead\n }\n }, this.commandTimeoutMs);\n\n this.terminals.set(id, state);\n return { terminalId: id };\n }\n\n /** Return captured output and (if available) the exit status. */\n output(terminalId: string): {\n output: string;\n truncated: boolean;\n exitStatus?: { exitCode: number | null; signal: string | null };\n } {\n const state = this.terminals.get(terminalId);\n if (!state) throw new Error(`unknown terminal: ${terminalId}`);\n return {\n output: Buffer.concat(\n state.outputChunks.slice(state.outputHead),\n state.retainedBytes,\n ).toString('utf8'),\n truncated: state.truncated,\n ...(state.exitStatus ? { exitStatus: state.exitStatus } : {}),\n };\n }\n\n /** Block until the process exits. Resolves with the exit status. */\n async waitForExit(\n terminalId: string,\n ): Promise<{ exitCode: number | null; signal: string | null }> {\n const state = this.terminals.get(terminalId);\n if (!state) throw new Error(`unknown terminal: ${terminalId}`);\n return state.exitPromise;\n }\n\n /** Kill the process but keep the terminal record (agent can still read output). */\n kill(terminalId: string): void {\n const state = this.terminals.get(terminalId);\n if (!state) throw new Error(`unknown terminal: ${terminalId}`);\n try {\n state.proc.kill('SIGTERM');\n } catch {\n // already dead\n }\n }\n\n /** Kill the process if alive and remove the record. */\n release(terminalId: string): void {\n const state = this.terminals.get(terminalId);\n if (!state) return;\n if (state.timeoutHandle) {\n clearTimeout(state.timeoutHandle);\n state.timeoutHandle = null;\n }\n try {\n state.proc.kill('SIGKILL');\n } catch {\n // already dead\n }\n this.terminals.delete(terminalId);\n }\n\n /** Kill all active terminals. Used on session close. */\n releaseAll(): void {\n this.abortSignal?.removeEventListener('abort', this.abortHandler);\n for (const id of [...this.terminals.keys()]) {\n this.release(id);\n }\n }\n\n private resolveCwd(cwd: string | undefined): string {\n if (!cwd) return this.projectRoot;\n const resolved = path.resolve(cwd);\n const rootWithSep = this.projectRoot.endsWith(path.sep)\n ? this.projectRoot\n : this.projectRoot + path.sep;\n if (resolved !== this.projectRoot && !resolved.startsWith(rootWithSep)) {\n return this.projectRoot;\n }\n try {\n const realRoot = realpathSync(this.projectRoot);\n const realCwd = realpathSync(resolved);\n const realRootWithSep = realRoot.endsWith(path.sep) ? realRoot : realRoot + path.sep;\n if (realCwd !== realRoot && !realCwd.startsWith(realRootWithSep)) {\n return realRoot;\n }\n return realCwd;\n } catch {\n // A process cannot start in a missing/unresolvable cwd. Fall back to the\n // configured root rather than allowing spawn to fail or follow a bad link.\n return this.projectRoot;\n }\n }\n\n private buildEnv(agentEnv?: { name: string; value: string }[]): NodeJS.ProcessEnv {\n // Use the sanitized child env from @wrongstack/core instead of raw\n // process.env. This strips API keys, tokens, and other credentials from\n // the host environment so a compromised ACP agent cannot exfiltrate them\n // via `terminal/create`. buildChildEnv preserves system/tooling variables\n // (PATH, HOME, LANG, ...) and handles the Windows Path/PATH aliasing.\n const env: NodeJS.ProcessEnv = buildChildEnv();\n if (agentEnv) {\n for (const { name, value } of agentEnv) {\n // Deny agent overrides of environment variables that could re-introduce\n // code injection (NODE_OPTIONS --require/--import/--loader), shared-library\n // preloading (LD_PRELOAD, DYLD_*), or path hijacking (PATH). buildChildEnv\n // already stripped these from the host env; the agent must not be able to\n // add them back.\n const upper = name.toUpperCase();\n if (DENIED_AGENT_ENV_KEYS.has(upper)) continue;\n env[name] = value;\n }\n }\n return env;\n }\n\n /**\n * Clamp an agent-supplied numeric to a finite positive safe integer, falling\n * back to `defaultValue` for undefined/NaN/non-finite values. Prevents\n * negative, NaN, or Infinity values from disabling output caps or causing\n * unbounded memory growth.\n */\n private clampFiniteInt(value: number | undefined, defaultValue: number): number {\n if (value === undefined || !Number.isFinite(value) || value < 1) {\n return defaultValue;\n }\n return Math.trunc(value);\n }\n}\n\n/**\n * Environment variables an ACP agent must NOT be allowed to set, because they\n * can re-introduce code injection or path hijacking after `buildChildEnv`\n * already stripped them from the host env. Checked case-insensitively.\n */\nconst DENIED_AGENT_ENV_KEYS: ReadonlySet<string> = new Set([\n 'NODE_OPTIONS',\n 'LD_PRELOAD',\n 'LD_LIBRARY_PATH',\n 'DYLD_INSERT_LIBRARIES',\n 'DYLD_LIBRARY_PATH',\n 'DYLD_FALLBACK_LIBRARY_PATH',\n 'PATH',\n 'PYTHONPATH',\n 'PYTHONSTARTUP',\n 'PERL5OPT',\n 'PERLLIB',\n 'RUBYOPT',\n 'RUBYLIB',\n]);\n", "import type {\n TrustActor,\n TrustAuthContext,\n TrustBoundary,\n TrustBoundaryDecision,\n TrustBoundaryRequest,\n TrustRisk,\n TrustScope,\n TrustSubject,\n} from '@wrongstack/core/security';\nimport type { PermissionOption, RequestPermissionOutcome, ToolKind } from '../types/acp-v1.js';\nimport type { PermissionPolicy, PermissionRequest } from './permission.js';\n\nexport interface ACPTrustBoundaryAdapterOptions {\n boundary: TrustBoundary;\n actor?: TrustActor | undefined;\n scope?: TrustScope | undefined;\n authContext?: TrustAuthContext | undefined;\n}\n\nfunction pickOption(\n options: readonly PermissionOption[],\n allowed: boolean,\n): RequestPermissionOutcome {\n const kinds = allowed ? ['allow_once', 'allow_always'] : ['reject_once', 'reject_always'];\n for (const kind of kinds) {\n const option = options.find((candidate) => candidate.kind === kind);\n if (option) return { outcome: 'selected', optionId: option.optionId };\n }\n return { outcome: 'cancelled' };\n}\n\nfunction riskFor(kind: ToolKind | undefined): TrustRisk {\n if (kind === 'read' || kind === 'search' || kind === 'fetch' || kind === 'think') return 'low';\n if (kind === 'edit' || kind === 'move') return 'elevated';\n if (kind === 'delete' || kind === 'execute') return 'high';\n return 'elevated';\n}\n\nfunction capabilityFor(request: PermissionRequest): string {\n const raw = request.toolCall.rawInput;\n if (typeof raw?.path === 'string') {\n return request.toolCall.kind === 'read' || request.toolCall.kind === 'search'\n ? 'filesystem.read'\n : 'filesystem.write';\n }\n if (typeof raw?.command === 'string' || request.toolCall.kind === 'execute')\n return 'process.spawn';\n if (request.toolCall.kind === 'fetch') return 'network.fetch';\n return `tool.${request.toolCall.kind ?? 'unknown'}`;\n}\n\nfunction subjectFor(request: PermissionRequest): TrustSubject {\n const raw = request.toolCall.rawInput;\n const title = request.toolCall.title ?? `ACP tool call ${String(request.toolCall.toolCallId)}`;\n if (typeof raw?.path === 'string') {\n return { kind: 'path', id: raw.path, attributes: { toolKind: request.toolCall.kind ?? null } };\n }\n if (typeof raw?.command === 'string') {\n return {\n kind: 'command',\n id: raw.command,\n attributes: { toolKind: request.toolCall.kind ?? null },\n };\n }\n return {\n kind: 'resource',\n id: title,\n attributes: { toolKind: request.toolCall.kind ?? null },\n };\n}\n\nfunction isAllowed(decision: TrustBoundaryDecision): boolean {\n return decision.kind === 'allow' || decision.kind === 'scoped-token';\n}\n\nexport function toTrustBoundaryRequest(\n request: PermissionRequest,\n options: Omit<ACPTrustBoundaryAdapterOptions, 'boundary'>,\n): TrustBoundaryRequest {\n const rawSessionId = request.toolCall.rawInput?.sessionId;\n const sessionId =\n typeof rawSessionId === 'string' && rawSessionId.length > 0\n ? rawSessionId\n : options.actor?.sessionId;\n return {\n version: 1,\n requestId: String(request.toolCall.toolCallId),\n actor: {\n ...(options.actor ?? { kind: 'agent' as const }),\n ...(sessionId ? { sessionId } : {}),\n },\n surface: 'acp',\n capability: capabilityFor(request),\n subject: subjectFor(request),\n risk: riskFor(request.toolCall.kind),\n scope: {\n ...(options.scope ?? {}),\n ...(sessionId ? { sessionId } : {}),\n },\n ...(options.authContext ? { authContext: options.authContext } : {}),\n metadata: {\n ...(request.toolCall.title ? { title: request.toolCall.title } : {}),\n toolKind: request.toolCall.kind ?? null,\n },\n };\n}\n\n/**\n * Adapts ACP permission callbacks to the shared TrustBoundary authority.\n * `confirm` remains denied here: interactive confirmation belongs in the\n * boundary's host adapter, which must return a final `allow` after consent.\n */\nexport function makeTrustBoundaryPermissionPolicy(\n options: ACPTrustBoundaryAdapterOptions,\n): PermissionPolicy {\n return async (request) => {\n if (request.signal.aborted) return { outcome: 'cancelled' };\n const decision = await options.boundary.evaluate(toTrustBoundaryRequest(request, options));\n if (request.signal.aborted) return { outcome: 'cancelled' };\n return pickOption(request.options, isAllowed(decision));\n };\n}\n\n/** Direct-module test seam; not re-exported by the package barrel. */\nexport const trustBoundaryPermissionCoverage = {\n pickOption,\n riskFor,\n capabilityFor,\n subjectFor,\n isAllowed,\n};\n", "/**\n * WebSocketClientTransport \u2014 remote ACP transport for `ACPSession`.\n *\n * Connects to a remote ACP agent over a WebSocket (cloud-hosted agents,\n * separate-process agents reachable over the network). Each WebSocket\n * message carries exactly one JSON-RPC 2.0 object \u2014 message boundaries\n * are preserved by the WS framing, so (unlike stdio) no newline delimiter\n * is needed.\n *\n * Uses the Node \u2265 22 built-in global `WebSocket` (undici), so there is no\n * runtime dependency. Per-connection auth headers are not supported by the\n * WHATWG WebSocket client; authenticate over the protocol instead\n * (`ACPSession.authenticate`) or embed a token in the URL query string.\n *\n * Spec: https://agentclientprotocol.com/protocol/v1/overview (remote transport)\n */\n\nimport type { ACPClientTransport } from '../agent/stdio-transport.js';\nimport type { ACPMessage } from '../types/acp-messages.js';\n\nexport interface WebSocketClientTransportOptions {\n /** ws:// or wss:// URL of the remote ACP agent. */\n url: string;\n /** Optional WebSocket subprotocols. */\n protocols?: string | string[] | undefined;\n /** How long to wait for the socket to open. Default 30s. */\n handshakeTimeoutMs?: number | undefined;\n /** Maximum unsent bytes retained by the WebSocket implementation. Default 32 MiB. */\n maxBufferedBytes?: number | undefined;\n /** Maximum inbound message size in characters. Default 20 MiB. */\n maxMessageChars?: number | undefined;\n}\n\n/** Narrow view of the global WebSocket we rely on (avoids lib.dom typings). */\ninterface WSLike {\n readonly bufferedAmount?: number;\n send(data: string): void;\n close(): void;\n addEventListener(type: 'open', cb: () => void): void;\n addEventListener(type: 'error', cb: (ev: unknown) => void): void;\n addEventListener(type: 'close', cb: () => void): void;\n addEventListener(type: 'message', cb: (ev: { data: unknown }) => void): void;\n}\n\ntype WSConstructor = new (url: string, protocols?: string | string[]) => WSLike;\n\nexport class WebSocketClientTransport implements ACPClientTransport {\n private ws: WSLike | null = null;\n private readonly handlers = new Set<(msg: ACPMessage) => void>();\n private closed = false;\n private readonly opts: WebSocketClientTransportOptions;\n private readonly maxBufferedBytes: number;\n private readonly maxMessageChars: number;\n\n constructor(opts: WebSocketClientTransportOptions) {\n this.opts = opts;\n this.maxBufferedBytes = finitePositiveLimit(opts.maxBufferedBytes, 32 * 1024 * 1024);\n this.maxMessageChars = finitePositiveLimit(opts.maxMessageChars, 20 * 1024 * 1024);\n }\n\n start(): Promise<void> {\n const WS = (globalThis as { WebSocket?: WSConstructor }).WebSocket;\n if (!WS) {\n return Promise.reject(\n new Error(\n 'global WebSocket is not available \u2014 Node \u2265 22 is required for the remote ACP transport',\n ),\n );\n }\n const timeoutMs = this.opts.handshakeTimeoutMs ?? 30_000;\n return new Promise<void>((resolve, reject) => {\n let settled = false;\n const ws = new WS(this.opts.url, this.opts.protocols);\n this.ws = ws;\n const timer = setTimeout(() => {\n settled = true;\n try {\n ws.close();\n } catch {\n // ignore\n }\n reject(new Error(`WebSocket failed to open within ${timeoutMs}ms`));\n }, timeoutMs);\n\n ws.addEventListener('open', () => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve();\n });\n ws.addEventListener('error', (ev: unknown) => {\n if (settled) {\n // Post-open errors just tear the connection down.\n this.closed = true;\n return;\n }\n settled = true;\n clearTimeout(timer);\n const message =\n ev && typeof ev === 'object' && 'message' in ev\n ? String((ev as { message: unknown }).message)\n : 'WebSocket error';\n reject(new Error(message));\n });\n ws.addEventListener('close', () => {\n this.closed = true;\n });\n ws.addEventListener('message', (ev: { data: unknown }) => {\n this.onData(ev.data);\n });\n });\n }\n\n send(msg: ACPMessage): Promise<void> {\n if (this.closed || !this.ws) {\n return Promise.reject(new Error('WebSocket transport is not open'));\n }\n try {\n const serialized = JSON.stringify(msg);\n const buffered = Number.isFinite(this.ws.bufferedAmount)\n ? (this.ws.bufferedAmount as number)\n : 0;\n if (buffered + Buffer.byteLength(serialized, 'utf8') > this.maxBufferedBytes) {\n this.stop();\n return Promise.reject(new Error('WebSocket transport send buffer limit exceeded'));\n }\n this.ws.send(serialized);\n return Promise.resolve();\n } catch (err) {\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n onMessage(handler: (msg: ACPMessage) => void): () => void {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n stop(): void {\n this.closed = true;\n if (this.ws) {\n try {\n this.ws.close();\n } catch {\n // already closed\n }\n this.ws = null;\n }\n }\n\n private onData(data: unknown): void {\n const text =\n typeof data === 'string'\n ? data\n : data instanceof ArrayBuffer\n ? Buffer.from(data).toString('utf8')\n : Buffer.isBuffer(data)\n ? data.toString('utf8')\n : String(data);\n if (text.length > this.maxMessageChars) {\n this.stop();\n return;\n }\n if (!text.trim()) return;\n let msg: ACPMessage;\n try {\n msg = JSON.parse(text) as ACPMessage;\n } catch {\n // A remote agent that frames multiple JSON objects per message is\n // non-conformant; try newline-splitting as a fallback before dropping.\n for (const line of text.split('\\n')) {\n if (!line.trim()) continue;\n try {\n this.dispatch(JSON.parse(line) as ACPMessage);\n } catch {\n // skip malformed fragment\n }\n }\n return;\n }\n this.dispatch(msg);\n }\n\n private dispatch(msg: ACPMessage): void {\n for (const handler of [...this.handlers]) {\n try {\n handler(msg);\n } catch {\n // a faulty consumer must not break the socket pump\n }\n }\n }\n}\n\nfunction finitePositiveLimit(value: number | undefined, fallback: number): number {\n return value !== undefined && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;\n}\n", "import type { ACPSessionErrorKind } from './acp-session-types.js';\n\nexport class ACPSessionError extends Error {\n readonly kind: ACPSessionErrorKind;\n override readonly cause: unknown;\n constructor(kind: ACPSessionErrorKind, message: string, cause?: unknown) {\n super(message);\n this.name = 'ACPSessionError';\n this.kind = kind;\n this.cause = cause;\n }\n}\n\nexport interface JsonRpcError {\n code: number;\n message: string;\n data?: unknown;\n}\n\nexport function isJsonRpcError(v: unknown): v is JsonRpcError {\n return (\n typeof v === 'object' &&\n v !== null &&\n typeof (v as { code?: unknown }).code === 'number' &&\n typeof (v as { message?: unknown }).message === 'string'\n );\n}\n", "import type { ACPMessage } from '../types/acp-messages.js';\nimport type {\n AnySessionUpdate,\n PlanEntry,\n ToolCallContent,\n ToolCallStatus,\n ToolKind,\n UsageCost,\n} from '../types/acp-v1.js';\nimport type {\n ACPCapturedDiff,\n ACPCapturedToolCall,\n ACPProgressEvent,\n} from './acp-session-types.js';\nimport { extractText, isRecord } from './acp-session-content.js';\n\nexport interface ACPSessionScratch {\n text: string;\n thoughts: string;\n plan?: PlanEntry[];\n usage?: { used: number; size: number; cost?: UsageCost | undefined };\n toolCalls: Map<string, ACPCapturedToolCall>;\n diffs: ACPCapturedDiff[];\n}\n\nexport function createSessionScratch(): ACPSessionScratch {\n return { text: '', thoughts: '', toolCalls: new Map(), diffs: [] };\n}\n\nexport function handleAcpSessionUpdate(\n msg: ACPMessage,\n scratch: ACPSessionScratch,\n emitProgress: (event: ACPProgressEvent) => void,\n): void {\n const update = (msg as { params?: { update?: unknown } }).params?.update;\n if (typeof update !== 'object' || update === null) return;\n const u = update as { sessionUpdate?: string; [k: string]: unknown };\n emitProgress({ type: 'raw', update: u as AnySessionUpdate });\n switch (u.sessionUpdate) {\n case 'agent_message_chunk': {\n const text = extractText(u.content);\n if (text) {\n scratch.text += text;\n emitProgress({ type: 'message', text });\n }\n return;\n }\n case 'thought_chunk': {\n const text = extractText(u.content);\n if (text) {\n scratch.thoughts += text;\n emitProgress({ type: 'thought', text });\n }\n return;\n }\n case 'tool_call':\n case 'tool_call_update':\n captureToolCall(u, u.sessionUpdate === 'tool_call', scratch, emitProgress);\n return;\n case 'plan':\n if (Array.isArray(u.entries)) {\n scratch.plan = u.entries as PlanEntry[];\n emitProgress({ type: 'plan', entries: u.entries as PlanEntry[] });\n }\n return;\n case 'usage_update':\n if (typeof u.used === 'number' && typeof u.size === 'number') {\n const usage = {\n used: u.used,\n size: u.size,\n ...(typeof u.cost === 'object' && u.cost !== null ? { cost: u.cost as UsageCost } : {}),\n };\n scratch.usage = usage;\n emitProgress({ type: 'usage', usage });\n }\n return;\n case 'available_commands_update':\n case 'current_mode_update':\n case 'config_option_update':\n case 'session_info_update':\n case 'user_message_chunk':\n case 'next_edit_suggestions':\n case 'elicitation':\n return;\n default:\n return;\n }\n}\n\nfunction captureToolCall(\n u: { [k: string]: unknown },\n isNew: boolean,\n scratch: ACPSessionScratch,\n emitProgress: (event: ACPProgressEvent) => void,\n): void {\n const toolCallId = typeof u.toolCallId === 'string' ? u.toolCallId : '';\n if (!toolCallId) return;\n const prev = scratch.toolCalls.get(toolCallId);\n const record: ACPCapturedToolCall = {\n toolCallId,\n title: typeof u.title === 'string' ? u.title : (prev?.title ?? toolCallId),\n kind: typeof u.kind === 'string' ? (u.kind as ToolKind) : prev?.kind,\n status:\n typeof u.status === 'string'\n ? (u.status as ToolCallStatus)\n : (prev?.status ?? (isNew ? 'pending' : 'in_progress')),\n rawInput: isRecord(u.rawInput) ? u.rawInput : prev?.rawInput,\n rawOutput: isRecord(u.rawOutput) ? u.rawOutput : prev?.rawOutput,\n };\n scratch.toolCalls.set(toolCallId, record);\n\n if (Array.isArray(u.content)) {\n for (const c of u.content as ToolCallContent[]) {\n if (c && typeof c === 'object' && c.type === 'diff') {\n const diff: ACPCapturedDiff = {\n path: c.path,\n oldText: c.oldText,\n newText: c.newText,\n };\n scratch.diffs.push(diff);\n emitProgress({ type: 'diff', diff });\n }\n }\n }\n\n emitProgress({\n type: isNew ? 'tool_call' : 'tool_call_update',\n toolCall: record,\n });\n}\n", "import type { ACPMessage } from '../types/acp-messages.js';\nimport type { ToolCallId, ToolCallUpdateNotification } from '../types/acp-v1.js';\nimport { type FileServer, FsError } from './file-server.js';\nimport type { PermissionPolicy } from './permission.js';\nimport type { TerminalServer } from './terminal-server.js';\n\nexport interface ACPResponseSender {\n sendResult(id: string | number, result: unknown): Promise<void>;\n sendErrorResponse(id: string | number, code: number, message: string): Promise<void>;\n}\n\nexport async function handleAcpPermissionRequest(\n msg: ACPMessage,\n permissionPolicy: PermissionPolicy,\n sender: ACPResponseSender,\n): Promise<void> {\n const id = msg.id;\n if (id === undefined) return;\n const params = (msg as { params?: { toolCall?: unknown; options?: unknown } }).params;\n const toolCall = params?.toolCall as ToolCallUpdateNotification | undefined;\n const options = Array.isArray(params?.options)\n ? (params.options as never as Parameters<PermissionPolicy>[0]['options'])\n : [];\n if (!toolCall) {\n await sender.sendErrorResponse(id, -32602, 'toolCall is required');\n return;\n }\n const policyAbort = new AbortController();\n try {\n const outcome = await permissionPolicy({\n toolCall,\n options,\n signal: policyAbort.signal,\n });\n await sender.sendResult(id, { outcome });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await sender.sendErrorResponse(id, -32603, `permission policy failed: ${message}`);\n }\n}\n\nexport async function handleAcpFsRequest(\n msg: ACPMessage,\n fileServer: FileServer,\n permissionPolicy: PermissionPolicy,\n sender: ACPResponseSender,\n): Promise<void> {\n const id = msg.id;\n if (id === undefined) return;\n const params = (msg as { params?: { sessionId?: string; path?: string; content?: string } })\n .params;\n if (!params?.path) {\n await sender.sendErrorResponse(id, -32602, 'path is required');\n return;\n }\n if (msg.method === 'fs/write_text_file') {\n const allowed = await authorizeAcpCallback(permissionPolicy, {\n toolCallId: `acp-fs-write-${id}`,\n title: `Write file: ${params.path}`,\n kind: 'edit',\n rawInput: { path: params.path, sessionId: params.sessionId },\n });\n if (!allowed) {\n await sender.sendErrorResponse(id, -32602, 'filesystem write denied by permission policy');\n return;\n }\n }\n try {\n if (msg.method === 'fs/read_text_file') {\n const result = await fileServer.readTextFile({\n sessionId: params.sessionId ?? '',\n path: params.path,\n });\n await sender.sendResult(id, result);\n } else {\n await fileServer.writeTextFile({\n sessionId: params.sessionId ?? '',\n path: params.path,\n content: params.content ?? '',\n });\n await sender.sendResult(id, {});\n }\n } catch (err) {\n const code = err instanceof FsError ? -32602 : -32603;\n const message = err instanceof Error ? err.message : String(err);\n await sender.sendErrorResponse(id, code, message);\n }\n}\n\nexport async function handleAcpTerminalRequest(\n msg: ACPMessage,\n terminalServer: TerminalServer,\n permissionPolicy: PermissionPolicy,\n sender: ACPResponseSender,\n): Promise<void> {\n const id = msg.id;\n if (id === undefined) return;\n const params = (msg as { params?: Record<string, unknown> }).params ?? {};\n try {\n switch (msg.method) {\n case 'terminal/create': {\n const allowed = await authorizeAcpCallback(permissionPolicy, {\n toolCallId: `acp-terminal-create-${id}`,\n title:\n `Run command: ${String(params.command ?? '')} ${(Array.isArray(params.args) ? params.args : []).join(' ')}`.trim(),\n kind: 'execute',\n rawInput: {\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n sessionId: params.sessionId,\n },\n });\n if (!allowed) {\n await sender.sendErrorResponse(id, -32602, 'terminal create denied by permission policy');\n return;\n }\n const createOpts: Parameters<TerminalServer['create']>[0] = {\n sessionId: String(params.sessionId ?? ''),\n command: String(params.command ?? ''),\n args: Array.isArray(params.args) ? (params.args as string[]) : [],\n };\n if (Array.isArray(params.env)) {\n createOpts.env = params.env as { name: string; value: string }[];\n }\n if (typeof params.cwd === 'string') {\n createOpts.cwd = params.cwd;\n }\n if (typeof params.outputByteLimit === 'number') {\n createOpts.outputByteLimit = params.outputByteLimit;\n }\n const result = terminalServer.create(createOpts);\n await sender.sendResult(id, result);\n return;\n }\n case 'terminal/output': {\n const terminalId = String(params.terminalId ?? '');\n const out = terminalServer.output(terminalId);\n await sender.sendResult(id, out);\n return;\n }\n case 'terminal/wait_for_exit': {\n const terminalId = String(params.terminalId ?? '');\n const exit = await terminalServer.waitForExit(terminalId);\n await sender.sendResult(id, exit);\n return;\n }\n case 'terminal/kill': {\n const terminalId = String(params.terminalId ?? '');\n terminalServer.kill(terminalId);\n await sender.sendResult(id, {});\n return;\n }\n case 'terminal/release': {\n const terminalId = String(params.terminalId ?? '');\n terminalServer.release(terminalId);\n await sender.sendResult(id, {});\n return;\n }\n default:\n await sender.sendErrorResponse(id, -32601, `unknown method: ${msg.method}`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await sender.sendErrorResponse(id, -32603, message);\n }\n}\n\nasync function authorizeAcpCallback(\n permissionPolicy: PermissionPolicy,\n partial: {\n toolCallId: string;\n title: string;\n kind: import('../types/acp-v1.js').ToolKind;\n rawInput?: Record<string, unknown>;\n },\n): Promise<boolean> {\n try {\n const outcome = await permissionPolicy({\n toolCall: {\n sessionUpdate: 'tool_call_update',\n toolCallId: partial.toolCallId as ToolCallId,\n title: partial.title,\n kind: partial.kind,\n status: 'pending',\n ...(partial.rawInput ? { rawInput: partial.rawInput } : {}),\n },\n options: [\n { optionId: 'allow', name: 'Allow', kind: 'allow_once' },\n { optionId: 'reject', name: 'Reject', kind: 'reject_once' },\n ],\n signal: new AbortController().signal,\n });\n return (\n outcome.outcome === 'selected' &&\n outcome.optionId !== 'reject' &&\n outcome.optionId !== 'reject_once' &&\n outcome.optionId !== 'reject_always'\n );\n } catch {\n return false;\n }\n}\n", "export function isBestEffortAckMethod(method: string | undefined): boolean {\n return (\n method === 'mcp/connect' ||\n method === 'mcp/message' ||\n method === 'mcp/disconnect' ||\n method === 'elicitation/create' ||\n method === 'elicitation/complete'\n );\n}\n", "/**\n * ACPSession \u2014 v1-correct ACP client.\n *\n * Owns one child process running an ACP-supporting agent (Claude Code,\n * Gemini CLI, Codex CLI, etc.) and translates the wire protocol into\n * a `SubagentRunner`-shaped surface for the rest of WrongStack.\n *\n * Spec: https://agentclientprotocol.com/protocol/v1/overview\n * Design: see ./acp-session.design.md in this directory.\n */\nimport { type ACPClientTransport, ClientTransport } from '../agent/stdio-transport.js';\nimport type { ACPMessage } from '../types/acp-messages.js';\nimport {\n ACP_PROTOCOL_VERSION,\n type AgentCapabilities,\n type AuthMethod,\n type ContentBlock,\n type McpServer,\n type SessionId,\n type SessionInfo,\n type StopReason,\n} from '../types/acp-v1.js';\nimport { emptyRunResult } from './acp-session-content.js';\nimport { FileServer } from './file-server.js';\nimport { type PermissionPolicy, readOnlyPermissionPolicy } from './permission.js';\nimport { TerminalServer } from './terminal-server.js';\nimport { makeTrustBoundaryPermissionPolicy } from './trust-boundary-permission.js';\nimport {\n WebSocketClientTransport,\n type WebSocketClientTransportOptions,\n} from './websocket-transport.js';\nimport type {\n ACPProgressEvent,\n ACPProgressHandler,\n ACPSessionOptions,\n ACPSessionRunResult,\n} from './acp-session-types.js';\nimport { ACPSessionError, isJsonRpcError } from './acp-session-errors.js';\nimport {\n createSessionScratch,\n handleAcpSessionUpdate,\n type ACPSessionScratch,\n} from './acp-session-updates.js';\nimport {\n type ACPResponseSender,\n handleAcpFsRequest,\n handleAcpPermissionRequest,\n handleAcpTerminalRequest,\n} from './acp-session-callbacks.js';\nimport { isBestEffortAckMethod } from './acp-message-routing.js';\n\nexport type {\n ACPCapturedDiff,\n ACPCapturedToolCall,\n ACPProgressEvent,\n ACPProgressHandler,\n ACPSessionErrorKind,\n ACPSessionOptions,\n ACPSessionRunResult,\n} from './acp-session-types.js';\nexport { ACPSessionError } from './acp-session-errors.js';\nexport { audioContent, imageContent, textContent } from './acp-session-content.js';\n\ninterface PendingRequest {\n method: string;\n resolve: (v: unknown) => void;\n reject: (e: Error) => void;\n timeoutMs: number;\n timeoutHandle: ReturnType<typeof setTimeout>;\n}\n\ntype State = 'init' | 'ready' | 'authenticated' | 'sessioning' | 'prompting' | 'done' | 'closed';\n\nexport class ACPSession {\n private readonly transport: ACPClientTransport;\n private readonly fileServer: FileServer;\n private readonly terminalServer: TerminalServer;\n private readonly permissionPolicy: PermissionPolicy;\n private readonly timeoutMs: number;\n private readonly opts: ACPSessionOptions;\n private transportOff: (() => void) | null = null;\n\n private state: State = 'init';\n private sessionId: SessionId | null = null;\n /** Pending outbound requests (initialize, session/new, session/prompt, etc). */\n private readonly pending = new Map<string | number, PendingRequest>();\n private nextId = 1;\n /** True after close() has been called. */\n private closed = false;\n\n // Agent-provided info from the initialize handshake\n private agentCapabilities: AgentCapabilities = {};\n private agentInfo: { name: string; title?: string | undefined; version: string } | null = null;\n private authMethods: AuthMethod[] = [];\n /** Protocol version negotiated with the agent during initialize. */\n private negotiatedVersion: number = ACP_PROTOCOL_VERSION;\n\n private constructor(opts: ACPSessionOptions, transport: ACPClientTransport) {\n this.opts = opts;\n this.transport = transport;\n this.timeoutMs = opts.timeoutMs ?? 5 * 60_000;\n const fsOpts: ConstructorParameters<typeof FileServer>[0] = {\n projectRoot: opts.projectRoot,\n };\n if (opts.fsTimeoutMs !== undefined) fsOpts.timeoutMs = opts.fsTimeoutMs;\n this.fileServer = new FileServer(fsOpts);\n const termOpts: ConstructorParameters<typeof TerminalServer>[0] = {\n projectRoot: opts.projectRoot,\n };\n if (opts.terminalTimeoutMs !== undefined) {\n termOpts.commandTimeoutMs = opts.terminalTimeoutMs;\n }\n if (opts.terminalOutputByteLimit !== undefined) {\n termOpts.outputByteLimit = opts.terminalOutputByteLimit;\n }\n if (opts.terminalMaxCount !== undefined) {\n termOpts.maxTerminals = opts.terminalMaxCount;\n }\n this.terminalServer = new TerminalServer(termOpts);\n if (opts.permissionPolicy && opts.trustBoundary) {\n throw new TypeError('permissionPolicy and trustBoundary are mutually exclusive');\n }\n this.permissionPolicy = opts.trustBoundary\n ? makeTrustBoundaryPermissionPolicy({\n boundary: opts.trustBoundary,\n ...(opts.trustActor ? { actor: opts.trustActor } : {}),\n scope: opts.trustScope ?? { cwd: opts.projectRoot },\n ...(opts.trustAuthContext ? { authContext: opts.trustAuthContext } : {}),\n })\n : (opts.permissionPolicy ?? readOnlyPermissionPolicy);\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Public accessors\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Agent capabilities advertised during initialize. */\n getCapabilities(): AgentCapabilities {\n return { ...this.agentCapabilities };\n }\n\n /** Authentication methods advertised by the agent. */\n getAuthMethods(): AuthMethod[] {\n return [...this.authMethods];\n }\n\n /** Agent info (name, title, version) from initialize. */\n getAgentInfo(): { name: string; title?: string | undefined; version: string } | null {\n return this.agentInfo;\n }\n\n /** Whether the agent requires authentication (has auth methods). */\n requiresAuth(): boolean {\n return this.authMethods.length > 0;\n }\n\n /** Current session id, if one exists. */\n getSessionId(): SessionId | null {\n return this.sessionId;\n }\n\n /** Protocol version negotiated during initialize. */\n getNegotiatedVersion(): number {\n return this.negotiatedVersion;\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Lifecycle \u2014 start\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Spawn the child, run the initialize handshake, install the\n * message dispatch, and return a ready session.\n */\n static async start(opts: ACPSessionOptions): Promise<ACPSession> {\n const transportOpts: ConstructorParameters<typeof ClientTransport>[0] = {\n command: opts.command,\n args: opts.args ? [...opts.args] : [],\n handshakeTimeoutMs: 30_000,\n skipHandshakeMarker: true,\n };\n if (opts.env !== undefined) transportOpts.env = opts.env;\n if (opts.cwd !== undefined) transportOpts.cwd = opts.cwd;\n const transport = new ClientTransport(transportOpts);\n return ACPSession.attach(opts, transport, `failed to spawn ${opts.command}`);\n }\n\n /**\n * Connect to a REMOTE ACP agent over a WebSocket instead of spawning a\n * local subprocess. `opts.command` is ignored for the wire (a label is\n * still useful for `role`); everything else (projectRoot sandbox for\n * fs/terminal, timeouts, permission policy, MCP servers) applies the same.\n */\n static async connectWebSocket(\n wsOpts: WebSocketClientTransportOptions,\n opts: ACPSessionOptions,\n ): Promise<ACPSession> {\n const transport = new WebSocketClientTransport(wsOpts);\n return ACPSession.attach(opts, transport, `failed to connect to ${wsOpts.url}`);\n }\n\n /**\n * Connect using a caller-supplied transport. Lets advanced callers plug\n * in their own wire (SDK streams, in-process pipes, test doubles).\n */\n static async connect(\n transport: ACPClientTransport,\n opts: ACPSessionOptions,\n ): Promise<ACPSession> {\n return ACPSession.attach(opts, transport, 'failed to connect transport');\n }\n\n /** Shared connect path: start the transport, install dispatch, handshake. */\n private static async attach(\n opts: ACPSessionOptions,\n transport: ACPClientTransport,\n spawnErrLabel: string,\n ): Promise<ACPSession> {\n try {\n await transport.start();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new ACPSessionError('spawn_failed', `${spawnErrLabel}: ${msg}`, err);\n }\n\n const session = new ACPSession(opts, transport);\n session.transportOff = transport.onMessage((msg) => session.handleMessage(msg));\n\n try {\n await session.initialize();\n } catch (err) {\n session.transportOff?.();\n session.transportOff = null;\n try {\n transport.stop();\n } catch {\n // best effort\n }\n throw err;\n }\n return session;\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Initialization\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async initialize(): Promise<void> {\n const id = this.allocId();\n const result = await this.sendRequest(id, 'initialize', {\n protocolVersion: ACP_PROTOCOL_VERSION,\n clientCapabilities: {\n fs: { readTextFile: true, writeTextFile: true },\n terminal: true,\n },\n clientInfo: { name: 'wrongstack', title: 'WrongStack', version: '0.287.0' },\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('init_failed', `initialize failed: ${result.message}`, result);\n }\n if (\n typeof result !== 'object' ||\n result === null ||\n typeof (result as { protocolVersion?: unknown }).protocolVersion !== 'number'\n ) {\n throw new ACPSessionError('protocol_error', 'initialize returned no protocolVersion');\n }\n const r = result as {\n protocolVersion: number;\n agentCapabilities?: AgentCapabilities;\n agentInfo?: { name: string; title?: string | undefined; version: string };\n authMethods?: AuthMethod[];\n };\n // Negotiation per spec: the client advertises its latest supported\n // version; the agent replies with the version both will use \u2014 the\n // client's if the agent supports it, otherwise the agent's own latest.\n // We therefore accept any version <= ours (we can speak it) and only\n // reject a version HIGHER than we support (the agent demands a protocol\n // we don't implement). Equal is the common path.\n if (r.protocolVersion > ACP_PROTOCOL_VERSION) {\n throw new ACPSessionError(\n 'unsupported_capability',\n `agent requires protocolVersion=${r.protocolVersion}, client supports up to ${ACP_PROTOCOL_VERSION}`,\n );\n }\n this.negotiatedVersion = r.protocolVersion;\n // Store agent metadata\n this.agentCapabilities = r.agentCapabilities ?? {};\n this.agentInfo = r.agentInfo ?? null;\n this.authMethods = r.authMethods ?? [];\n this.state = 'ready';\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Authentication\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Authenticate with the agent using one of the advertised auth methods.\n * Call this AFTER start() and BEFORE any session/new call.\n *\n * Throws ACPSessionError('auth_failed') if the agent rejects the\n * authentication or if the methodId is not in the advertised list.\n */\n async authenticate(methodId: string): Promise<void> {\n if (this.state === 'closed') {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (this.state !== 'ready') {\n throw new ACPSessionError(\n 'protocol_error',\n `authenticate called in state=${this.state} (expected 'ready')`,\n );\n }\n if (!this.authMethods.some((m) => m.id === methodId)) {\n throw new ACPSessionError(\n 'auth_failed',\n `auth method \"${methodId}\" not in advertised methods: ${this.authMethods.map((m) => m.id).join(', ')}`,\n );\n }\n\n const id = this.allocId();\n const result = await this.sendRequest(id, 'authenticate', { methodId });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('auth_failed', `authenticate failed: ${result.message}`, result);\n }\n this.state = 'authenticated';\n }\n\n /**\n * Log out from the current authenticated session.\n * Only callable if the agent advertises `auth.logout` capability.\n */\n async logout(): Promise<void> {\n if (this.state === 'closed') {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (!this.agentCapabilities.auth?.logout) {\n throw new ACPSessionError(\n 'unsupported_capability',\n 'agent does not support logout (auth.logout capability not advertised)',\n );\n }\n\n const id = this.allocId();\n const result = await this.sendRequest(id, 'logout', {});\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('logout_failed', `logout failed: ${result.message}`, result);\n }\n this.state = 'ready';\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Session management\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Load an existing session. The agent replays the conversation history\n * via session/update notifications before responding.\n *\n * Only works if the agent advertises `loadSession` capability.\n *\n * @param sessionId - The session to load\n * @param mcpServers - Optional MCP servers (defaults to options.mcpServers)\n * @param cwd - Optional working directory (defaults to options.cwd or projectRoot)\n */\n async loadSession(sessionId: SessionId, mcpServers?: McpServer[], cwd?: string): Promise<void> {\n if (this.closed) {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (!this.agentCapabilities.loadSession) {\n throw new ACPSessionError(\n 'unsupported_capability',\n 'agent does not support session/load (loadSession capability not advertised)',\n );\n }\n if (this.sessionId) {\n // Close current session first\n await this.closeSession();\n }\n\n this.resetScratch();\n const servers = this.filterMcpServers(mcpServers ?? this.opts.mcpServers);\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/load', {\n sessionId,\n cwd: cwd ?? this.opts.cwd ?? this.opts.projectRoot,\n mcpServers: servers,\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('prompt_failed', `session/load failed: ${result.message}`, result);\n }\n this.sessionId = sessionId;\n }\n\n /**\n * Resume an existing session without replaying history.\n *\n * Only works if the agent advertises `sessionCapabilities.resume`.\n *\n * @param sessionId - The session to resume\n * @param mcpServers - Optional MCP servers (defaults to options.mcpServers)\n * @param cwd - Optional working directory (defaults to options.cwd or projectRoot)\n */\n async resumeSession(sessionId: SessionId, mcpServers?: McpServer[], cwd?: string): Promise<void> {\n if (this.closed) {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (!this.agentCapabilities.sessionCapabilities?.resume) {\n throw new ACPSessionError(\n 'unsupported_capability',\n 'agent does not support session/resume (sessionCapabilities.resume not advertised)',\n );\n }\n if (this.sessionId) {\n await this.closeSession();\n }\n\n const servers = this.filterMcpServers(mcpServers ?? this.opts.mcpServers);\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/resume', {\n sessionId,\n cwd: cwd ?? this.opts.cwd ?? this.opts.projectRoot,\n mcpServers: servers,\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `session/resume failed: ${result.message}`,\n result,\n );\n }\n this.sessionId = sessionId;\n }\n\n /**\n * List existing sessions known to the agent.\n *\n * Only works if the agent advertises `sessionCapabilities.list`.\n */\n async listSessions(\n cursor?: string,\n cwd?: string,\n ): Promise<{ sessions: SessionInfo[]; nextCursor?: string | undefined }> {\n if (this.closed) {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (!this.agentCapabilities.sessionCapabilities?.list) {\n throw new ACPSessionError(\n 'unsupported_capability',\n 'agent does not support session/list (sessionCapabilities.list not advertised)',\n );\n }\n\n const id = this.allocId();\n const params: Record<string, unknown> = {};\n if (cursor !== undefined) params.cursor = cursor;\n if (cwd !== undefined) params.cwd = cwd;\n const result = await this.sendRequest(id, 'session/list', params);\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('prompt_failed', `session/list failed: ${result.message}`, result);\n }\n const r = result as { sessions?: SessionInfo[]; nextCursor?: string };\n return {\n sessions: r.sessions ?? [],\n nextCursor: r.nextCursor,\n };\n }\n\n /**\n * Delete a session from the agent's session list.\n *\n * Only works if the agent advertises `sessionCapabilities.delete`.\n */\n async deleteSession(sessionId: SessionId): Promise<void> {\n if (this.closed) {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (!this.agentCapabilities.sessionCapabilities?.delete) {\n throw new ACPSessionError(\n 'unsupported_capability',\n 'agent does not support session/delete (sessionCapabilities.delete not advertised)',\n );\n }\n\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/delete', { sessionId });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `session/delete failed: ${result.message}`,\n result,\n );\n }\n\n if (this.sessionId === sessionId) {\n this.sessionId = null;\n }\n }\n\n /**\n * Fork a session \u2014 create a new session from an existing one.\n */\n async forkSession(\n sourceSessionId: SessionId,\n cwd?: string,\n mcpServers?: McpServer[],\n ): Promise<SessionId> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n\n const servers = this.filterMcpServers(mcpServers ?? this.opts.mcpServers);\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/fork', {\n sessionId: sourceSessionId,\n cwd: cwd ?? this.opts.cwd ?? this.opts.projectRoot,\n ...(servers.length > 0 ? { mcpServers: servers } : {}),\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('prompt_failed', `session/fork failed: ${result.message}`, result);\n }\n const newId = (result as { sessionId?: unknown }).sessionId;\n if (typeof newId !== 'string' || !newId) {\n throw new ACPSessionError('protocol_error', 'session/fork returned no sessionId', result);\n }\n return newId as SessionId;\n }\n\n /**\n * Set the active mode for a session.\n */\n async setMode(sessionId: SessionId, modeId: string): Promise<void> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/set_mode', { sessionId, modeId });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `session/set_mode failed: ${result.message}`,\n result,\n );\n }\n }\n\n /**\n * Set a configuration option for a session.\n */\n async setConfigOption(sessionId: SessionId, configId: string, value: string): Promise<void> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/set_config_option', {\n sessionId,\n configId,\n value,\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `session/set_config_option failed: ${result.message}`,\n result,\n );\n }\n }\n\n /**\n * List available providers and the current provider.\n */\n async listProviders(): Promise<{ providers: unknown[]; currentProviderId: string | null }> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'providers/list', {});\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `providers/list failed: ${result.message}`,\n result,\n );\n }\n const r = result as { providers?: unknown[]; currentProviderId?: string | null };\n return { providers: r.providers ?? [], currentProviderId: r.currentProviderId ?? null };\n }\n\n /**\n * Send an MCP message to the agent for routing.\n */\n async mcpMessage(connectionId: string, message: Record<string, unknown>): Promise<unknown> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'mcp/message', { connectionId, message });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('prompt_failed', `mcp/message failed: ${result.message}`, result);\n }\n return result;\n }\n\n /**\n * Set the active provider for the agent.\n */\n async setProvider(providerId: string, config?: Record<string, unknown>): Promise<void> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'providers/set', { providerId, ...(config ?? {}) });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError('prompt_failed', `providers/set failed: ${result.message}`, result);\n }\n }\n\n /**\n * Disable the current provider.\n */\n async disableProvider(): Promise<void> {\n if (this.closed) throw new ACPSessionError('closed', 'session is closed');\n const id = this.allocId();\n const result = await this.sendRequest(id, 'providers/disable', {});\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'prompt_failed',\n `providers/disable failed: ${result.message}`,\n result,\n );\n }\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Prompt\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Run one prompt turn. Creates a session if needed, sends the\n * prompt, streams session/update notifications, and resolves with\n * the agent's response.\n *\n * @param blocks - Content blocks to send. Use `textContent()` for plain\n * text, or include ImageContent/AudioContent if the agent's\n * `promptCapabilities` allow it.\n * @param signal - AbortSignal for cancellation.\n *\n * Cancellation: if `signal` aborts mid-prompt, we send\n * `session/cancel` (a notification per spec) and keep accepting\n * updates until the agent returns with `stopReason: 'cancelled'`.\n * The result is the same shape as a normal turn, with\n * `stopReason === 'cancelled'`.\n */\n async prompt(\n blocks: ContentBlock[],\n signal: AbortSignal,\n onProgress?: ACPProgressHandler,\n ): Promise<ACPSessionRunResult> {\n if (this.closed) {\n throw new ACPSessionError('closed', 'session is closed');\n }\n if (this.state !== 'ready' && this.state !== 'authenticated' && this.state !== 'done') {\n throw new ACPSessionError('protocol_error', `prompt called in state=${this.state}`);\n }\n\n // Pre-aborted signals short-circuit BEFORE we create a session\n // and before any wire activity.\n if (signal.aborted) {\n return emptyRunResult('cancelled');\n }\n\n if (!this.sessionId) {\n await this.createSession();\n }\n\n this.resetScratch();\n this.progressHandler = onProgress ?? null;\n\n const promptId = this.allocId();\n const turnPromise = this.sendRequest(\n promptId,\n 'session/prompt',\n {\n sessionId: this.sessionId,\n prompt: blocks,\n },\n this.timeoutMs,\n );\n\n let cancelled = false;\n const onAbort = (): void => {\n cancelled = true;\n this.transport\n .send({\n jsonrpc: '2.0',\n method: 'session/cancel',\n params: { sessionId: this.sessionId },\n } as never as ACPMessage)\n .catch(() => {\n // transport may already be torn down \u2014 ignore\n });\n };\n signal.addEventListener('abort', onAbort, { once: true });\n\n this.state = 'prompting';\n let response: unknown;\n try {\n response = await turnPromise;\n } catch (err) {\n this.state = 'done';\n signal.removeEventListener('abort', onAbort);\n if (cancelled || signal.aborted) {\n throw new ACPSessionError('aborted', 'prompt was aborted by the parent');\n }\n const msg = err instanceof Error ? err.message : String(err);\n throw new ACPSessionError('prompt_failed', `session/prompt failed: ${msg}`, err);\n } finally {\n signal.removeEventListener('abort', onAbort);\n this.progressHandler = null;\n }\n\n this.state = 'done';\n if (isJsonRpcError(response)) {\n throw new ACPSessionError('prompt_failed', `agent error: ${response.message}`, response);\n }\n const stopReason = (response as { stopReason?: StopReason }).stopReason ?? 'end_turn';\n const finalText = this.scratch.text;\n return {\n text: finalText,\n stopReason,\n hasText: finalText.length > 0,\n usage: this.scratch.usage,\n plan: this.scratch.plan,\n toolCalls: [...this.scratch.toolCalls.values()],\n diffs: this.scratch.diffs,\n thoughts: this.scratch.thoughts,\n };\n }\n\n private async createSession(): Promise<void> {\n const servers = this.filterMcpServers(this.opts.mcpServers);\n const id = this.allocId();\n const result = await this.sendRequest(id, 'session/new', {\n cwd: this.opts.cwd ?? this.opts.projectRoot,\n mcpServers: servers,\n });\n if (isJsonRpcError(result)) {\n throw new ACPSessionError(\n 'session_create_failed',\n `session/new failed: ${result.message}`,\n result,\n );\n }\n const sessionId = (result as { sessionId?: unknown }).sessionId;\n if (typeof sessionId !== 'string' || sessionId.length === 0) {\n throw new ACPSessionError('protocol_error', 'session/new returned no sessionId', result);\n }\n this.sessionId = sessionId as SessionId;\n }\n\n /**\n * Close the current session gracefully (if the agent supports it).\n *\n * Sends `session/close` JSON-RPC request, then clears the local\n * session id. Best-effort \u2014 errors are swallowed so the caller can\n * always proceed to transport teardown.\n */\n private async closeSession(): Promise<void> {\n if (!this.sessionId) return;\n const sid = this.sessionId;\n this.sessionId = null;\n\n if (this.agentCapabilities.sessionCapabilities?.close) {\n const id = this.allocId();\n try {\n await this.sendRequest(id, 'session/close', { sessionId: sid }, 10_000);\n } catch {\n // Best-effort: if close fails, we still proceed with transport stop.\n }\n }\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Lifecycle \u2014 close\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Tear down the session and kill the child process. */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.state = 'closed';\n this.terminalServer.releaseAll();\n\n // Graceful session close (if session is active and agent supports it)\n if (this.sessionId && this.agentCapabilities.sessionCapabilities?.close) {\n try {\n await this.closeSession();\n } catch {\n // best-effort\n }\n }\n\n // Reject any pending outbound requests so their awaits return.\n for (const [, p] of this.pending) {\n clearTimeout(p.timeoutHandle);\n p.reject(new ACPSessionError('closed', 'session was closed'));\n }\n this.pending.clear();\n this.transportOff?.();\n this.transportOff = null;\n try {\n this.transport.stop();\n } catch {\n // best effort\n }\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Helpers\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Filter MCP servers according to agent capabilities.\n * - Stdio servers are always included.\n * - HTTP servers are only included if agent supports mcpCapabilities.http.\n * - SSE servers are only included if agent supports mcpCapabilities.sse.\n */\n private filterMcpServers(servers?: McpServer[]): McpServer[] {\n if (!servers || servers.length === 0) return [];\n const mcpCaps = this.agentCapabilities.mcpCapabilities ?? {};\n return servers.filter((s) => {\n if ('type' in s && s.type === 'http') return mcpCaps.http === true;\n if ('type' in s && s.type === 'sse') return mcpCaps.sse === true;\n return true; // stdio \u2014 always supported per spec\n });\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Wire layer\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private allocId(): number {\n return this.nextId++;\n }\n\n private async sendRequest(\n id: number,\n method: string,\n params: unknown,\n timeoutMs?: number,\n ): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n const effectiveTimeout = timeoutMs ?? this.timeoutMs;\n const handle = setTimeout(() => {\n this.pending.delete(id);\n reject(\n new ACPSessionError('protocol_error', `${method} timed out after ${effectiveTimeout}ms`),\n );\n }, effectiveTimeout);\n this.pending.set(id, {\n method,\n resolve: resolve as (v: unknown) => void,\n reject,\n timeoutMs: effectiveTimeout,\n timeoutHandle: handle,\n });\n this.transport\n .send({ jsonrpc: '2.0', id, method, params } as never as ACPMessage)\n .catch((err) => {\n clearTimeout(handle);\n this.pending.delete(id);\n const msg = err instanceof Error ? err.message : String(err);\n reject(new ACPSessionError('protocol_error', `send ${method} failed: ${msg}`, err));\n });\n });\n }\n\n /**\n * Send a JSON-RPC 2.0 success response to an agent-initiated request.\n *\n * Per JSON-RPC 2.0 (and the official ACP SDK's message router) a Response\n * object MUST carry `jsonrpc: \"2.0\"` and MUST NOT carry a `method` field \u2014\n * the SDK classifies any object with a `method` key as a Request and drops\n * it as a response, so an agent's `fs/*`, `terminal/*`, or\n * `session/request_permission` callback would hang forever. The legacy\n * `ACPMessage` type predates v1 (requires `method`, lacks `jsonrpc`), so we\n * build the correct wire object and cast at the boundary.\n */\n private sendResult(id: string | number, result: unknown): Promise<void> {\n return this.transport.send({ jsonrpc: '2.0', id, result } as never as ACPMessage);\n }\n\n /** Send a JSON-RPC 2.0 error response (no `method` field, per spec). */\n private sendErrorResponse(id: string | number, code: number, message: string): Promise<void> {\n return this.transport.send({\n jsonrpc: '2.0',\n id,\n error: { code, message },\n } as never as ACPMessage);\n }\n\n private responseSender(): ACPResponseSender {\n return {\n sendResult: (id, result) => this.sendResult(id, result),\n sendErrorResponse: (id, code, message) => this.sendErrorResponse(id, code, message),\n };\n }\n\n private handleMessage(msg: ACPMessage): void {\n // Response to an outbound request (has id and either result or error)\n if (msg.id !== undefined && (msg.result !== undefined || msg.error !== undefined)) {\n const pending = this.pending.get(msg.id);\n if (!pending) return;\n clearTimeout(pending.timeoutHandle);\n this.pending.delete(msg.id);\n if (msg.error !== undefined) {\n pending.reject(new Error(msg.error.message ?? 'unknown JSON-RPC error'));\n } else {\n pending.resolve(msg.result);\n }\n return;\n }\n\n // session/update notification (no id)\n if (msg.method === 'session/update') {\n handleAcpSessionUpdate(msg, this.scratch, (event) => this.emitProgress(event));\n return;\n }\n\n // session/request_permission (has id, expected response: outcome)\n if (msg.method === 'session/request_permission') {\n void handleAcpPermissionRequest(msg, this.permissionPolicy, this.responseSender());\n return;\n }\n\n // fs/* requests\n if (msg.method === 'fs/read_text_file' || msg.method === 'fs/write_text_file') {\n void handleAcpFsRequest(\n msg,\n this.fileServer,\n this.permissionPolicy,\n this.responseSender(),\n );\n return;\n }\n\n // terminal/* requests\n if (msg.method?.startsWith('terminal/')) {\n void handleAcpTerminalRequest(\n msg,\n this.terminalServer,\n this.permissionPolicy,\n this.responseSender(),\n );\n return;\n }\n\n if (isBestEffortAckMethod(msg.method)) {\n if (msg.id !== undefined) {\n this.sendResult(msg.id, {}).catch(() => {});\n }\n return;\n }\n\n // $/cancel_request protocol notification \u2014 no response expected.\n if (msg.method === '$/cancel_request') {\n return;\n }\n\n // Anything else: log to stderr and ignore. Don't crash.\n if (msg.method) {\n // eslint-disable-next-line no-console\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'acp_session.unhandled_method',\n method: msg.method,\n timestamp: new Date().toISOString(),\n }),\n );\n }\n }\n\n private emitProgress(event: ACPProgressEvent): void {\n if (!this.progressHandler) return;\n try {\n this.progressHandler(event);\n } catch {\n // A faulty host handler must never break the wire pump.\n }\n }\n\n /** Live progress handler installed for the duration of a `prompt()` turn. */\n private progressHandler: ACPProgressHandler | null = null;\n\n // Per-prompt scratch state\n private scratch: ACPSessionScratch = createSessionScratch();\n\n private resetScratch(): void {\n this.scratch = createSessionScratch();\n }\n}\n", "/**\n * ACPSubagentRunner \u2014 `SubagentRunner` implementation for DIR-1.\n *\n * Wraps an external ACP-supporting agent (Claude Code, Gemini CLI, Codex\n * CLI, Cline, Goose, OpenHands, etc.) as a WrongStack subagent. The\n * external agent runs its own agent loop; we send it a task via the ACP\n * v1 protocol and return the result.\n *\n * v1 spec: https://agentclientprotocol.com/protocol/v1/overview\n *\n * Connected to the Director / MultiAgentCoordinator via the\n * `SubagentRunner` interface (same shape as `AgentSubagentRunner`).\n */\nimport type {\n SubagentError,\n SubagentErrorKind,\n SubagentRunContext,\n SubagentRunOutcome,\n SubagentRunner,\n TaskSpec,\n} from '@wrongstack/core/types';\nimport {\n ACPSession,\n ACPSessionError,\n textContent,\n type ACPProgressEvent,\n type ACPProgressHandler,\n} from '../client/acp-session.js';\nimport type { ACPSessionErrorKind } from '../client/acp-session.js';\nimport type { PermissionPolicy } from '../client/permission.js';\nimport { findAgentDescriptor } from '../registry/agents.catalog.js';\nimport type { McpServer } from '../types/acp-v1.js';\n\nexport interface ACPSubagentRunnerOptions {\n /** How to spawn the external agent. */\n command: string;\n args?: string[] | undefined;\n env?: Record<string, string> | undefined;\n cwd?: string | undefined;\n /** Subagent role label \u2014 surfaced in errors and used for logging. */\n role?: string | undefined;\n /**\n * Hard wall-clock cap for one prompt turn. Defaults to 5 minutes.\n * Overrides `SubagentRunContext.budget.limits.timeoutMs` if both are set.\n */\n timeoutMs?: number | undefined;\n /**\n * Filesystem sandbox root. Defaults to `options.cwd` (when set) or\n * the process's current working directory. All `fs/read_text_file` /\n * `fs/write_text_file` calls are bounded to this root.\n */\n projectRoot?: string | undefined;\n /**\n * Live progress callback. Forwarded to `ACPSession.prompt` so the host\n * can render the external agent's tool calls / diffs / text as they\n * stream, instead of waiting for the buffered final result.\n */\n onProgress?: ACPProgressHandler | undefined;\n /**\n * Permission policy for the external agent's `session/request_permission`\n * calls. Defaults to the session's own default. Inject the host's\n * confirm/trust UI here so an external agent's file writes / commands\n * are surfaced to a human instead of silently auto-approved.\n */\n permissionPolicy?: PermissionPolicy | undefined;\n /**\n * MCP servers to expose to the external agent (passed through\n * `session/new` / `session/load`). Stdio servers are always sent;\n * HTTP/SSE are filtered by the agent's advertised capabilities.\n */\n mcpServers?: McpServer[] | undefined;\n /**\n * When true, the underlying `ACPSession` is kept open across multiple\n * runner invocations (multi-turn conversation \u2014 the external agent\n * keeps its context). The caller MUST call `stop()` to tear it down.\n * Defaults to false (one process per task).\n */\n persistent?: boolean | undefined;\n}\n\n/**\n * Static catalog of agent ids \u2192 spawn options.\n *\n * The CLI and the host's `buildACPRunner` look up entries by id. The\n * canonical, multi-source catalog is `packages/acp/src/registry/agents.catalog.ts`\n * (the 12-entry static catalog introduced in commit 4ad287b4). This\n * map stays for backward compatibility with existing call sites that\n * import it directly; new code should prefer the registry.\n */\nexport const ACP_AGENT_COMMANDS: Record<string, ACPSubagentRunnerOptions> = {\n cline: {\n command: 'npx',\n args: ['-y', '@agentify/cline'],\n role: 'cline',\n },\n 'gemini-cli': {\n command: 'gemini',\n role: 'gemini-cli',\n },\n copilot: {\n command: 'gh',\n args: ['copilot', 'agent'],\n role: 'copilot',\n },\n openhands: {\n command: 'openhands',\n role: 'openhands',\n },\n goose: {\n command: 'goose',\n role: 'goose',\n },\n};\n\n/**\n * Build a one-shot `SubagentRunner` for a single agent invocation. Each\n * call to the returned function spawns a fresh child process, runs one\n * prompt turn, and tears everything down. The cost is ~1 second of\n * process-startup per call; for long-lived sessions (multi-turn\n * conversations), use `makeACPSubagentRunnerWithStop` and call `stop()`\n * explicitly.\n */\nexport async function makeACPSubagentRunner(\n options: ACPSubagentRunnerOptions,\n): Promise<SubagentRunner> {\n const { runner, stop } = await makeACPSubagentRunnerWithStop(options);\n // Wrap so we always tear down after the turn, even if the caller\n // forgot to call `stop()`. stop() is idempotent, so a double-call is\n // safe.\n const wrappedRunner: SubagentRunner = async (task, ctx) => {\n try {\n return await runner(task, ctx);\n } finally {\n stop();\n }\n };\n return wrappedRunner;\n}\n\n/**\n * Build a long-lived `SubagentRunner` plus an explicit `stop()` for\n * teardown. The caller is responsible for calling `stop()` when done\n * (or when the host's signal fires). Useful for the `wstack acp spawn`\n * CLI command, which holds the child open for the duration of a user\n * task and tears down on SIGINT.\n */\nexport async function makeACPSubagentRunnerWithStop(\n options: ACPSubagentRunnerOptions,\n): Promise<{ runner: SubagentRunner; stop: () => void | Promise<void> }> {\n const projectRoot = options.projectRoot ?? options.cwd ?? process.cwd();\n const timeoutMs = options.timeoutMs ?? 5 * 60_000;\n const persistent = options.persistent === true;\n\n // In persistent mode we keep a single session alive across runner calls\n // so the external agent retains its conversation context (multi-turn).\n let shared: ACPSession | null = null;\n\n const startSession = async (): Promise<ACPSession> => {\n return ACPSession.start({\n command: options.command,\n ...(options.args !== undefined ? { args: options.args } : {}),\n ...(options.env !== undefined ? { env: options.env } : {}),\n ...(options.cwd !== undefined ? { cwd: options.cwd } : {}),\n projectRoot,\n timeoutMs,\n role: options.role,\n ...(options.permissionPolicy !== undefined\n ? { permissionPolicy: options.permissionPolicy }\n : {}),\n ...(options.mcpServers !== undefined ? { mcpServers: options.mcpServers } : {}),\n });\n };\n\n const runner: SubagentRunner = async (\n task: TaskSpec,\n ctx: SubagentRunContext,\n ): Promise<SubagentRunOutcome> => {\n let session: ACPSession;\n const reuse = persistent && shared !== null;\n try {\n session = reuse ? (shared as ACPSession) : await startSession();\n if (persistent) shared = session;\n } catch (err) {\n // init / spawn failure. Throw a structured error so the host can\n // classify it (SubagentErrorKind).\n throw acpErrorToSubagentError(err, options.role ?? 'acp-subagent');\n }\n\n // Count real tool calls from the captured stream, and keep the\n // budget's idle clock fresh on every update so a long-but-working\n // external agent is never reaped by the watchdog as \"stalled\".\n const onProgress: ACPProgressHandler = (event: ACPProgressEvent) => {\n try {\n ctx.budget.markActivity();\n } catch {\n // markActivity never throws today; guard defensively anyway.\n }\n options.onProgress?.(event);\n };\n\n try {\n const result = await session.prompt(\n [textContent(task.description)],\n ctx.signal,\n onProgress,\n );\n // Surface the real tool-call count captured from the stream. A\n // text-less turn is a soft signal (an ACP agent may legitimately\n // end with no message), not an error.\n return {\n result: result.text,\n iterations: 1,\n toolCalls: result.toolCalls.length,\n };\n } catch (err) {\n throw acpErrorToSubagentError(err, options.role ?? 'acp-subagent');\n } finally {\n // One-shot mode closes after each turn. Persistent mode keeps the\n // session open; the caller tears it down via stop().\n if (!persistent) {\n try {\n await session.close();\n } catch {\n // best-effort cleanup\n }\n }\n }\n };\n\n // In persistent mode stop() closes the long-lived session; in one-shot\n // mode it's a no-op (each session is closed in the runner's finally).\n const stop = async (): Promise<void> => {\n if (shared) {\n const s = shared;\n shared = null;\n try {\n await s.close();\n } catch {\n // best-effort\n }\n }\n };\n\n return { runner, stop };\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Error mapping\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Map an ACPSessionError (or arbitrary Error from the session layer)\n * to a structured `SubagentError` that the existing coordinator can\n * classify and act on. Unknown error shapes get `kind: 'unknown'` \u2014\n * they shouldn't crash the parent.\n */\nfunction acpErrorToSubagentError(\n err: unknown,\n subagentId: string,\n): SubagentError {\n if (err instanceof ACPSessionError) {\n const kind = mapACPKind(err.kind);\n return {\n kind,\n message: `${subagentId}: ${err.message}`,\n retryable: isRetryable(kind),\n cause: {\n name: err.name,\n message: err.message,\n ...(err.stack !== undefined ? { stack: err.stack } : {}),\n },\n };\n }\n const message = err instanceof Error ? err.message : String(err);\n return {\n kind: 'bridge_failed',\n message: `${subagentId}: ${message}`,\n retryable: false,\n cause: {\n name: err instanceof Error ? err.name : 'Error',\n message,\n ...(err instanceof Error && err.stack !== undefined ? { stack: err.stack } : {}),\n },\n };\n}\n\nfunction mapACPKind(acpKind: ACPSessionErrorKind): SubagentErrorKind {\n switch (acpKind) {\n case 'spawn_failed':\n case 'init_failed':\n case 'session_create_failed':\n case 'agent_died':\n case 'protocol_error':\n return 'bridge_failed';\n case 'prompt_failed':\n return 'tool_failed';\n case 'auth_failed':\n case 'logout_failed':\n return 'bridge_failed';\n case 'aborted':\n return 'aborted_by_parent';\n case 'closed':\n case 'unsupported_capability':\n return 'unknown';\n }\n}\n\nfunction isRetryable(kind: SubagentErrorKind): boolean {\n // Conservative: spawn / init / protocol / agent-died are NOT\n // retryable as-is (they need config or a re-install). Timeouts and\n // prompt failures might be \u2014 the parent's classifier will branch on\n // `kind` and decide.\n // None of the ACP error kinds currently map to a retryable coordinator\n // kind. Keep the parameter so this policy remains explicit at the callsite.\n void kind;\n return false;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Unused but exported for future use\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Re-export so the CLI handler can import the session type. */\nexport type { ACPSession };\n\n/** Exposed for the `wstack acp list` renderer. */\nexport function describeAgent(id: string): {\n command: string;\n args: readonly string[];\n role: string;\n} | null {\n const entry = ACP_AGENT_COMMANDS[id];\n if (!entry) return null;\n return {\n command: entry.command,\n args: entry.args ?? [],\n role: entry.role ?? id,\n };\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Shared command resolution + single-task run + handshake probe\n//\n// These are the building blocks both the `wstack acp` CLI handler and the\n// `/acp` slash command consume, so the two surfaces stay in lock-step.\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Per-agent ACP invocation overrides, sourced from the user's\n * active profile config (`~/.wrongstack/profiles/<name>/config.json`,\n * `config.acp.agents`). Lets a user point an\n * agent id at the correct ACP entry \u2014 e.g. the Zed Claude-Code adapter \u2014\n * without a code change. NEVER honoured from in-project config (it is an\n * arbitrary-command exec surface); see `config-loader.ts`.\n */\nexport type AcpAgentCommandOverrides = Record<\n string,\n { command: string; args?: string[]; env?: Record<string, string> }\n>;\n\n/** A synced-registry catalog keyed by registry id (from `fetchAcpRegistry`). */\nexport type AcpLiveCatalog = Record<\n string,\n { command: string; args?: readonly string[]; env?: Record<string, string> }\n>;\n\n/**\n * Map our stable, human-friendly catalog ids to the official registry's ids,\n * so a live-synced registry (keyed by registry id) still resolves when the\n * user types our id. Our id is preferred in the UI; the alias is the bridge.\n */\nexport const REGISTRY_ID_ALIASES: Readonly<Record<string, string>> = {\n 'claude-code': 'claude-acp',\n 'gemini-cli': 'gemini',\n 'codex-cli': 'codex-acp',\n copilot: 'github-copilot-cli',\n // Kimi's live registry id is `kimi` \u2014 same as our catalog id, so the\n // alias is identity. Listed explicitly so `resolveAcpAgentCommand`\n // finds the live entry when the registry is synced.\n kimi: 'kimi',\n};\n\n/**\n * Resolve an agent id to its spawn command. Precedence:\n * 1. user override (`config.acp.agents[id]`)\n * 2. the bundled static `AGENTS_CATALOG` (curated LOCAL-binary invocations)\n * 3. live synced registry (`fetchAcpRegistry` \u2192 cache), by id or alias\n * 4. legacy `ACP_AGENT_COMMANDS` map (last resort, kept for back-compat)\n * Returns `null` for an id present in none of them.\n *\n * Why catalog BEFORE the live registry: our goal is to drive the user's\n * already-installed, logged-in CLI. The catalog hand-curates the LOCAL-binary\n * ACP entry for each popular agent (`gemini --acp`, `opencode acp`, \u2026), which\n * preserves the agent's own login and starts instantly. The official registry,\n * by contrast, encodes \"run a fresh copy\" invocations \u2014 pinned `npx <pkg>@ver`\n * downloads (no local login, slow first run) and platform binaries like\n * `opencode.exe` that may not match a shim on PATH. So the registry is the\n * source for the long tail of agents the catalog doesn't cover, NOT an\n * override of the curated 12. Users force a specific command via the override.\n */\nexport function resolveAcpAgentCommand(\n id: string,\n overrides?: AcpAgentCommandOverrides,\n live?: AcpLiveCatalog,\n): ACPSubagentRunnerOptions | null {\n const ov = overrides?.[id];\n if (ov && typeof ov.command === 'string' && ov.command.length > 0) {\n const out: ACPSubagentRunnerOptions = {\n command: ov.command,\n args: [...(ov.args ?? [])],\n role: id,\n };\n if (ov.env) out.env = ov.env;\n return out;\n }\n const desc = findAgentDescriptor(id);\n if (desc) {\n const out: ACPSubagentRunnerOptions = {\n command: desc.acp.command,\n args: [...(desc.acp.args ?? [])],\n role: id,\n };\n if (desc.acp.env) out.env = desc.acp.env;\n return out;\n }\n const liveEntry = live?.[id] ?? live?.[REGISTRY_ID_ALIASES[id] ?? ''];\n if (liveEntry && typeof liveEntry.command === 'string' && liveEntry.command.length > 0) {\n const out: ACPSubagentRunnerOptions = {\n command: liveEntry.command,\n args: [...(liveEntry.args ?? [])],\n role: id,\n };\n if (liveEntry.env) out.env = liveEntry.env;\n return out;\n }\n const fromMap = ACP_AGENT_COMMANDS[id];\n if (fromMap) return fromMap;\n return null;\n}\n\nexport interface AcpProbeResult {\n id: string;\n ok: boolean;\n ms: number;\n agentInfo?: { name: string; title?: string | undefined; version: string } | undefined;\n error?: string | undefined;\n}\n\n/**\n * Empirically test whether an agent actually speaks ACP on this machine:\n * spawn it, run the `initialize` handshake, and close. `ok: true` means the\n * agent answered `initialize` within `timeoutMs` (default 8s) \u2014 the truth,\n * regardless of what the static catalog guesses. A bare CLI that drops into\n * an interactive prompt fails here (init times out) instead of hanging a\n * real turn.\n */\nexport async function probeAcpAgent(\n idOrCmd: string | ACPSubagentRunnerOptions,\n opts?: {\n timeoutMs?: number | undefined;\n projectRoot?: string | undefined;\n overrides?: AcpAgentCommandOverrides | undefined;\n live?: AcpLiveCatalog | undefined;\n },\n): Promise<AcpProbeResult> {\n const id =\n typeof idOrCmd === 'string' ? idOrCmd : (idOrCmd.role ?? idOrCmd.command);\n const cmd =\n typeof idOrCmd === 'string'\n ? resolveAcpAgentCommand(idOrCmd, opts?.overrides, opts?.live)\n : idOrCmd;\n if (!cmd) return { id, ok: false, ms: 0, error: 'unknown agent' };\n\n const timeoutMs = opts?.timeoutMs ?? 8_000;\n const startedAt = Date.now();\n let session: ACPSession | null = null;\n try {\n session = await ACPSession.start({\n command: cmd.command,\n ...(cmd.args !== undefined ? { args: cmd.args } : {}),\n ...(cmd.env !== undefined ? { env: cmd.env } : {}),\n projectRoot: opts?.projectRoot ?? process.cwd(),\n // Bounds the `initialize` request: a CLI that spawns but never answers\n // the handshake fails after this instead of blocking.\n timeoutMs,\n });\n const info = session.getAgentInfo();\n return {\n id,\n ok: true,\n ms: Date.now() - startedAt,\n ...(info ? { agentInfo: info } : {}),\n };\n } catch (err) {\n return {\n id,\n ok: false,\n ms: Date.now() - startedAt,\n error: err instanceof Error ? err.message : String(err),\n };\n } finally {\n if (session) {\n try {\n await session.close();\n } catch {\n // best-effort\n }\n }\n }\n}\n\nexport interface ProbeAcpAgentsOptions {\n agentIds: string[];\n resolveCmd: (id: string) => ACPSubagentRunnerOptions | null;\n projectRoot?: string | undefined;\n /** Max agents probed at once. Default 4. Keeps concurrent first-run `npx`\n * downloads from starving local agents' stdout past their timeout. */\n concurrency?: number | undefined;\n /** Per-agent handshake timeout for LOCAL binary commands. Default 20s. */\n timeoutMs?: number | undefined;\n /** Per-agent timeout for `npx`/`uvx` commands (first run downloads the\n * package, which is slow). Default 90s. */\n packageTimeoutMs?: number | undefined;\n signal?: AbortSignal | undefined;\n onProgress?: ((id: string, result: AcpProbeResult) => void) | undefined;\n}\n\n/**\n * Probe many agents with BOUNDED concurrency. Unbounded `Promise.all` over the\n * full set spawns every agent at once \u2014 and a few concurrent `npx` downloads\n * peg the machine hard enough that even already-installed local agents miss\n * their handshake window. Bounding the fan-out (and giving npx/uvx a longer\n * timeout) is what makes a mixed install probe reliably.\n */\nexport async function probeAcpAgents(\n opts: ProbeAcpAgentsOptions,\n): Promise<AcpProbeResult[]> {\n const localTimeout = opts.timeoutMs ?? 20_000;\n const pkgTimeout = opts.packageTimeoutMs ?? 90_000;\n const ids = opts.agentIds;\n const byId = new Map<string, AcpProbeResult>();\n\n // Partition: local binaries vs npx/uvx package launchers. A first-run `npx`\n // download is heavy enough to starve a LOCAL agent sharing the same batch\n // (its stdout 'data' misses the handshake window \u2192 false timeout). So probe\n // all locals first (clean resources, fast), THEN the package ones \u2014 which\n // are inherently slow on first run \u2014 at low concurrency.\n const local: string[] = [];\n const pkg: string[] = [];\n const cmds = new Map<string, ACPSubagentRunnerOptions | null>();\n for (const id of ids) {\n const cmd = opts.resolveCmd(id);\n cmds.set(id, cmd);\n if (!cmd) continue;\n if (cmd.command === 'npx' || cmd.command === 'uvx') pkg.push(id);\n else local.push(id);\n }\n\n const runPhase = async (phaseIds: string[], concurrency: number, timeoutMs: number): Promise<void> => {\n let next = 0;\n const workerCount = Math.min(Math.max(1, concurrency), Math.max(1, phaseIds.length));\n const workers: Promise<void>[] = [];\n for (let w = 0; w < workerCount; w++) {\n workers.push(\n (async () => {\n while (true) {\n const current = next++;\n if (current >= phaseIds.length) return;\n const id = phaseIds[current]!;\n if (opts.signal?.aborted) {\n byId.set(id, { id, ok: false, ms: 0, error: 'aborted' });\n continue;\n }\n const cmd = cmds.get(id)!;\n const r = await probeAcpAgent(cmd!, {\n timeoutMs,\n ...(opts.projectRoot !== undefined ? { projectRoot: opts.projectRoot } : {}),\n });\n r.id = id; // probeAcpAgent derives id from cmd.role; pin to our id.\n byId.set(id, r);\n opts.onProgress?.(id, r);\n }\n })(),\n );\n }\n await Promise.all(workers);\n };\n\n // Unknown ids resolve to null \u2014 record immediately.\n for (const id of ids) {\n if (cmds.get(id) === null) {\n const r: AcpProbeResult = { id, ok: false, ms: 0, error: 'unknown agent' };\n byId.set(id, r);\n opts.onProgress?.(id, r);\n }\n }\n\n await runPhase(local, opts.concurrency ?? 4, localTimeout);\n // Package launchers run AFTER locals (so npm downloads never starve a local\n // agent's handshake), at low concurrency with a long timeout \u2014 first-run\n // `npx`/`uvx` fetches are inherently slow.\n await runPhase(pkg, 2, pkgTimeout);\n\n // Preserve the caller's input order.\n return ids.map((id) => byId.get(id)!);\n}\n", "import { expectDefined } from '@wrongstack/core/utils';\n/**\n * ToolTranslator \u2014 bidirectional translation between WrongStack tools and\n * ACP tool representations.\n *\n * Used by DIR-1 (WrongStack as ACP client) to:\n * - Map WrongStack TaskSpec \u2192 ACP task payload\n * - Map ACP tool responses \u2192 TaskResult\n *\n * Used by DIR-2 (WrongStack as ACP server) to:\n * - Convert the WrongStack Tool.inputSchema \u2192 ACPToolDefinition.inputSchema\n * - (handled by tools-registry.ts \u2014 same logic lives there)\n *\n * For DIR-1 async tool calls: ACP agents send progress notifications while\n * a tool is running, then send a final result. The translator handles this\n * by polling for the final [result] notification on the transport.\n */\nimport type {ACPMessage, ACPToolDefinition, ACPToolCallResponse, ContentBlock} from '../types/acp-messages.js';\nimport type { TaskSpec, TaskResult } from '@wrongstack/core/types';\nexport interface ToolTranslatorOptions {\n /**\n * If true (default), wrap tool calls in an async poll loop that waits\n * for progress notifications until a final result arrives.\n */\n asyncTools?: boolean | undefined;\n pollIntervalMs?: number | undefined;\n totalTimeoutMs?: number | undefined;\n}\n\nconst DEFAULT_OPTIONS: Required<ToolTranslatorOptions> = {\n asyncTools: true,\n pollIntervalMs: 500,\n totalTimeoutMs: 120_000,\n};\n\n/** Convert an ACP ACPToolDefinition \u2192 a JSON schema object recognisable by WrongStack */\nexport function acpToolToSchema(def: ACPToolDefinition): Record<string, unknown> {\n if (!def.inputSchema) return {type: 'object', properties: {}};\n return def.inputSchema as Record<string, unknown>;\n}\n\n/** Extract tool result text from ACP ContentBlock[] */\nexport function extractTextFromContent(blocks: ContentBlock[]): string {\n const parts: string[] = [];\n for (const b of blocks) {\n if (b.type === 'text') parts.push(b.text);\n else if (b.type === 'resource') parts.push(`[resource: ${b.resource.uri}]`);\n else if (b.type === 'image') parts.push(`[image: ${b.data.slice(0, 20)}...]`);\n else if (b.type === 'progress') {\n if (b.messages?.length) parts.push(b.messages.join('\\n'));\n }\n }\n return parts.join('\\n');\n}\n\n/** Build a TaskSpec from an ACP task payload */\nexport function buildTaskSpec(payload: {\n taskId: string;\n task: string;\n subagentId?: string | undefined;\n}): TaskSpec {\n return {\n id: payload.taskId,\n description: payload.task,\n subagentId: payload.subagentId,\n };\n}\n\n/** Parse an ACP tools/call response \u2192 TaskResult */\nexport function parseToolResponse(\n taskId: string,\n subagentId: string,\n response: ACPToolCallResponse,\n): TaskResult {\n const blocks = response.result.content;\n const text = extractTextFromContent(blocks);\n\n // Detect error state from isError flag or error-like text\n const isError =\n response.result.isError || text.toLowerCase().includes('error') ||\n text.toLowerCase().includes('failed');\n\n return {\n taskId,\n subagentId,\n status: isError ? 'failed' : 'success',\n result: text,\n iterations: 1,\n toolCalls: 1,\n durationMs: 0,\n };\n}\n\n/** ToolTranslator for DIR-1 \u2014 wraps ACP client transport, adds task semantics */\nexport class ToolTranslator {\n private readonly opts: Required<ToolTranslatorOptions>;\n private readonly pending = new Map<string | number, {\n resolve: (v: ACPToolCallResponse) => void;\n reject: (e: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n }>();\n\n constructor(opts: ToolTranslatorOptions = {}) {\n this.opts = {...DEFAULT_OPTIONS, ...opts};\n }\n\n /**\n * Start listening to a transport for tool responses and cancellations.\n * Call this once after constructing the translator and before sending tasks.\n */\n attachToTransport(\n transport: {onMessage: (h: (msg: ACPMessage) => void) => () => void; send: (msg: ACPMessage) => Promise<void>},\n ): void {\n transport.onMessage((msg) => {\n if (msg.method === 'tools/call' && msg.id !== undefined) {\n const pending = this.pending.get(msg.id);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(expectDefined(msg.id));\n pending.resolve(msg as never as ACPToolCallResponse);\n }\n }\n\n // Handle cancellation notifications\n if (msg.method === 'cancel' && msg.id !== undefined) {\n const pending = this.pending.get(msg.id);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pending.delete(expectDefined(msg.id));\n pending.reject(new Error('Call cancelled by client'));\n }\n }\n });\n }\n\n /**\n * Send a tool call over the transport and wait for a response.\n * If asyncTools is true, polls for progress and resolves when the final\n * response arrives.\n */\n async callTool(\n transport: {send: (msg: ACPMessage) => Promise<void>},\n name: string,\n args: Record<string, unknown>,\n callId: string | number = crypto.randomUUID(),\n ): Promise<ACPToolCallResponse> {\n await transport.send({\n jsonrpc: '2.0',\n method: 'tools/call',\n id: callId,\n params: {name, arguments: args},\n } as never as ACPMessage);\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pending.delete(callId);\n reject(new Error(`Tool call ${name} timed out after ${this.opts.totalTimeoutMs}ms`));\n }, this.opts.totalTimeoutMs);\n\n this.pending.set(callId, {resolve, reject, timeout});\n });\n }\n\n cancelAll(): void {\n for (const [, p] of this.pending) {\n clearTimeout(p.timeout);\n }\n this.pending.clear();\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["spawn", "buildChildEnv", "resolve", "path", "stat", "realpathSync", "path", "resolve", "resolve", "resolve", "expectDefined", "expectDefined", "resolve"]
7
7
  }