@byok-sdk/client 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types.ts","../../src/adapters/pi/resolve-bin.ts","../../src/adapters/pi/permission-mapping.ts","../../src/adapters/pi/events.ts","../../src/util/async-queue.ts","../../src/adapters/pi/rpc-client.ts","../../src/adapters/pi/pi-adapter.ts","../../src/adapters/claude/resolve-bin.ts","../../src/adapters/claude/resolve-approval-mcp-bin.ts","../../src/adapters/claude/permission-mapping.ts","../../src/adapters/claude/events.ts","../../src/adapters/claude/process-client.ts","../../src/bin/approval-mcp-server.ts","../../src/adapters/claude/claude-adapter.ts","../../src/adapters/codex/resolve-bin.ts","../../src/adapters/codex/permission-mapping.ts","../../src/adapters/codex/events.ts","../../src/adapters/codex/process-runner.ts","../../src/adapters/codex/codex-adapter.ts"],"names":["path","fileURLToPath","READONLY_TOOLS","events","STDERR_RING_CAPACITY","spawn","spawnSync","execFileAsync","promisify","execFile","DETECT_TIMEOUT_MS","fs","toNonNegativeInt","guessContentType"],"mappings":"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","file":"index.js","sourcesContent":["import type { AgentEvent, PermissionPolicy, TaskOfferPayload } from '@byok-sdk/protocol';\nimport type { RuntimeEnvironmentRequirements } from './daemon/environment';\n\nexport type { RuntimeEnvironmentRequirements } from './daemon/environment';\n\nexport interface GitWorkspaceConfig {\n mode: 'local-checkpoints';\n}\n\n/**\n * Result of probing whether a runtime is usable on this machine. `authPresent`\n * is computed without ever reading the runtime's own credential storage (see\n * the credential-isolation rule on {@link RuntimeAdapter}) — it only reflects\n * whether a recognized environment variable name is set.\n */\nexport interface RuntimeDetectResult {\n present: boolean;\n version?: string;\n authPresent?: boolean;\n}\n\n/** What a runtime adapter can do, advertised so the daemon can pick/validate adapters. */\nexport interface RuntimeCapabilities {\n steer: boolean;\n resume: boolean;\n /**\n * Whether this adapter can genuinely pause a running session on\n * `needs_approval` and resume it from an out-of-band decision — i.e.\n * whether {@link Session.resolveApproval} really resolves rather than\n * throwing. This is the ONLY source of truth for the wire's\n * `RuntimeInfo.capabilities.approvalInteractive` (`daemon/\n * create-daemon.ts`'s `toRuntimeInfoCapabilities`); the daemon no longer\n * hardcodes a value.\n *\n * Required, deliberately: a new adapter (or a test fake) that forgets to\n * declare it fails to compile rather than silently defaulting to a claim\n * it cannot back.\n */\n approvalInteractive: boolean;\n /** Subset of {@link PermissionPolicy}'s `mode` values this adapter can express without widening. */\n permissionModes: string[];\n}\n\n/**\n * M4 Phase 3: the out-of-band approval channel `TaskRunner` (`daemon/\n * task-runner.ts`) hands to an adapter's `start()` via `TaskContext\n * .approvalChannel`, for a runtime whose approval mechanism genuinely needs\n * to reach back into the daemon from OUTSIDE the adapter's own process — the\n * claude adapter's concrete case: `claude`'s `--permission-prompt-tool`\n * resolves a pending permission entirely inside a SEPARATE MCP-server child\n * process claude itself spawns (see `bin/byok-approval-mcp.ts`), which has\n * no in-process handle to this task's `Session` at all and must instead call\n * back into the SAME daemon over its control socket. `storeDir`/`productId`\n * are exactly what that out-of-process helper needs to find and authenticate\n * against this daemon's control socket (`daemon/control-protocol.ts`\n * `controlEndpointPath`/`controlTokenPath`); `taskId` is how its request gets\n * correlated back to THIS task once it arrives. `resolve()` is the\n * daemon-side counterpart: it resolves the single most-recently-registered\n * pending approval for this task (via `TaskRunner.requestApproval`'s own\n * `ApprovalRegistry` entry — see `daemon/approvals.ts`), and rejects if none\n * is currently pending, mirroring `Session.resolveApproval`'s own\n * no-notion-of-approval-pending fail-closed contract one level up.\n *\n * Optional and adapter-agnostic on purpose: only an adapter whose runtime\n * genuinely supports an out-of-band pause (claude, today) ever reads this;\n * every other adapter (pi, codex) ignores it exactly as before this field\n * existed.\n */\nexport interface ApprovalChannel {\n taskId: string;\n storeDir: string;\n productId: string;\n /** Default wait (ms) before the daemon force-resolves an unanswered approval request as a fail-closed rejection — see `TaskRunner.requestApproval`. */\n timeoutMs: number;\n /** Resolve the single currently-pending out-of-band approval for this task. Rejects if none is pending right now. */\n resolve(approved: boolean, reason?: string): Promise<void>;\n}\n\n/**\n * Per-task execution context handed to {@link RuntimeAdapter.start}. `policy`\n * is the already fail-closed-checked *effective* policy (offer policy merged\n * against the daemon's configured ceiling) — the adapter must obey this, not\n * whatever the raw task offer's own `policy` field said.\n */\nexport interface TaskContext {\n workspaceDir: string;\n policy: PermissionPolicy;\n env: NodeJS.ProcessEnv;\n /** Prepared local checkpoint repository metadata; absent for legacy plain workspaces. */\n gitWorkspace?: {\n workspaceId: string;\n baseline?: string;\n };\n /** M4 Phase 3 — see {@link ApprovalChannel}. Optional/adapter-agnostic: unset for every adapter that never requests an out-of-band approval. */\n approvalChannel?: ApprovalChannel;\n}\n\n/**\n * A running (or resumable) unit of work on a runtime. One `Session` maps to\n * one underlying runtime process/session for the lifetime of a task.\n */\nexport interface Session {\n /** Opaque runtime session id, reported back to the server via `task.complete.sessionRef`. */\n sessionRef: string;\n /** Normalized events for this session; the daemon batches these into `task.progress`. */\n events: AsyncIterable<AgentEvent>;\n /** Inject steering text into a running turn (mid-stream). */\n steer(text: string): Promise<void>;\n /** Send a new instruction on the same session after it has gone idle. */\n followUp(task: TaskOfferPayload): Promise<void>;\n /** Best-effort abort of the current turn (used for `task.cancel`). */\n interrupt(): Promise<void>;\n /** Tear down the underlying runtime process/session. Idempotent. */\n close(): Promise<void>;\n /**\n * Resolve a session paused on `needs_approval` (protocol §5). The\n * server's own state has already moved by the time this is called (§4 —\n * `task.approve`/`task.reject` are best-effort notifications, not\n * requests awaiting a reply): `approved: true` must make the session\n * resume producing events (`task.progress` continuing is the proof);\n * `approved: false` means the caller will immediately follow up with\n * `interrupt()` + `close()` and report `task.fail` — an adapter that has\n * no notion of `needs_approval` at all (i.e. never emits one) should\n * throw a descriptive error here rather than silently no-op, since a\n * caller receiving `task.approve`/`task.reject` for one of its tasks\n * implies something upstream expected approval support that isn't there.\n */\n resolveApproval(approved: boolean, reason?: string): Promise<void>;\n}\n\n/**\n * Uniform seam every concrete runtime (pi now; claude/codex in M2) implements.\n *\n * Credential-isolation rule: an adapter spawns only the runtime's official\n * binary. It never reads, proxies, or forwards that runtime's own credential\n * storage (OAuth tokens, API keys on disk, `~/.claude`, `~/.codex`, `~/.pi`\n * auth state, etc). Presence checks are limited to environment variable\n * *names* (see {@link RuntimeDetectResult.authPresent}).\n *\n * M5: separately, {@link RuntimeAdapter.environmentRequirements} below\n * declares which environment variable NAMES (never values inspected here\n * either) this adapter's runtime needs forwarded into its own spawned\n * process — see that method's own doc comment and `daemon/environment.ts`.\n */\nexport interface RuntimeAdapter {\n id: string;\n detect(): Promise<RuntimeDetectResult>;\n capabilities(): RuntimeCapabilities;\n start(task: TaskOfferPayload, ctx: TaskContext): Promise<Session>;\n /**\n * M5: declares which environment variable names (exact, or `*`-suffixed\n * prefix) this runtime's own CLI needs beyond the always-included\n * platform baseline (`daemon/environment.ts`'s `buildRuntimeEnv`) —\n * `task-runner.ts` builds each task's `TaskContext.env` from this instead\n * of ever handing a spawned agent the daemon's raw `process.env` again.\n * Optional and fail-closed by omission: an adapter that doesn't implement\n * this gets the platform baseline ONLY, never an implicit \"everything.\"\n */\n environmentRequirements?(): RuntimeEnvironmentRequirements;\n}\n\n/**\n * Thrown by `RuntimeAdapter.start` when the task can never succeed on this\n * adapter as offered — an unsupported `PermissionPolicy` (fail-closed) or an\n * instruction shape the adapter can't handle (e.g. a blob-ref in M0) — as\n * opposed to a transient/environmental failure (spawn error, missing\n * credentials) that might succeed on a later retry. The daemon uses this\n * distinction to set `task.fail`'s `retryable` flag correctly instead of\n * treating every `start()` failure the same way.\n */\nexport class PolicyUnsupportedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PolicyUnsupportedError';\n }\n}\n\n/**\n * Thrown by {@link Session.steer} on an adapter whose runtime has no\n * mid-turn steering channel at all (`capabilities().steer === false`) — a\n * permanent property of the runtime, never a transient failure. Typed\n * rather than a bare `Error` so the daemon can classify an inbound\n * `task.steer` for such a runtime as non-retryable (record + ack, cursor\n * advances) instead of stalling the cursor on it forever, without matching\n * on message strings.\n */\nexport class SteerUnsupportedError extends Error {\n /** The `RuntimeAdapter.id` that cannot steer (e.g. `claude`, `codex`). */\n readonly runtimeId: string;\n\n constructor(runtimeId: string, message: string) {\n super(message);\n this.name = 'SteerUnsupportedError';\n this.runtimeId = runtimeId;\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * The pi coding-agent CLI's real npm package name.\n *\n * IMPORTANT (empirically verified 2026-07-16, see the M0-3 report): the name\n * `@mariozechner/pi` — the identifier this task was originally briefed with —\n * is NOT the coding agent. On npm it resolves to an unrelated \"CLI tool for\n * managing vLLM deployments on GPU pods\" (bin: `pi-pods`). The real coding\n * agent was `@mariozechner/pi-coding-agent`, which is now deprecated in\n * favor of this package. `package.json` carries the exact supported version\n * as a required dependency; pi is a core BYOK capability, not an optional\n * enhancement or an unversioned global executable.\n */\nexport const PI_PACKAGE_NAME = '@earendil-works/pi-coding-agent';\n\nexport interface ResolvedBin {\n command: string;\n source: 'package' | 'env';\n}\n\ninterface MinimalPackageJson {\n name?: string;\n bin?: string | Record<string, string>;\n}\n\nfunction readPackageJson(dir: string): MinimalPackageJson | undefined {\n const candidate = path.join(dir, 'package.json');\n if (!existsSync(candidate)) return undefined;\n try {\n return JSON.parse(readFileSync(candidate, 'utf8')) as MinimalPackageJson;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolve the pi CLI executable from the required package installed alongside\n * `@byok-sdk/client`. There is intentionally no automatic PATH fallback: a\n * global `pi` would create a second, unversioned authority for this contract.\n *\n * `BYOK_PI_BIN` explicitly overrides the package when set: `PiAdapterOptions.resolveBin`\n * is the injectable seam for in-process tests, but the `byok-agent` CLI bin\n * only ever constructs `new PiAdapter()` with no options (see `createDaemon`),\n * so an out-of-process substitution (e.g. examples/basic's e2e run swapping\n * in the fake-pi fixture, or a single-file product injecting its required\n * Node 22.19+ pi sidecar) has no other seam to use.\n *\n * Deliberately does NOT use `createRequire(...).resolve()`: this package is\n * pure ESM with no `require` export condition (`exports[\".\"]` only offers\n * `import`), so CJS-style resolution fails with\n * `ERR_PACKAGE_PATH_NOT_EXPORTED`. It also does NOT resolve the\n * `./package.json` subpath directly (also not exported); instead it resolves\n * the package's main entry via `import.meta.resolve` and walks upward to the\n * package root identified by its manifest name.\n */\nexport function resolvePiBin(): ResolvedBin {\n const override = process.env.BYOK_PI_BIN;\n if (override) {\n return { command: override, source: 'env' };\n }\n try {\n const mainEntryUrl = import.meta.resolve(PI_PACKAGE_NAME);\n let dir = path.dirname(fileURLToPath(mainEntryUrl));\n for (let depth = 0; depth < 6; depth++) {\n const pkg = readPackageJson(dir);\n if (pkg?.name === PI_PACKAGE_NAME) {\n const binRel = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.pi;\n if (binRel) {\n return { command: path.join(dir, binRel), source: 'package' };\n }\n break;\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n } catch (cause) {\n throw new Error(\n `Required ${PI_PACKAGE_NAME} could not be resolved; install @byok-sdk/client dependencies or set BYOK_PI_BIN to a Node 22.19+ pi sidecar`,\n { cause },\n );\n }\n throw new Error(\n `Required ${PI_PACKAGE_NAME} does not expose the pi CLI; reinstall the pinned dependency or set BYOK_PI_BIN to a Node 22.19+ pi sidecar`,\n );\n}\n","import type { PermissionPolicy } from '@byok-sdk/protocol';\n\nexport interface PiPermissionMapping {\n ok: boolean;\n /** CLI args to append to `pi --mode rpc ...`. Only meaningful when `ok` is true. */\n args: string[];\n /** Present when `ok` is false. */\n reason?: string;\n}\n\n/** pi's own read-only built-ins, per `pi --help`'s documented example: `pi --tools read,grep,find,ls -p ...`. */\nconst READONLY_TOOLS: readonly string[] = ['read', 'grep', 'find', 'ls'];\n\n/**\n * Map an effective {@link PermissionPolicy} to `pi --mode rpc` CLI args,\n * fail-closed against the exact pi 0.84.1 CLI contract:\n *\n * - `auto` / `readonly` are expressible via `--tools` / `--no-tools`.\n * - `confirm` and `plan` are NOT expressible: pi ships no built-in per-call\n * approval gate and explicitly \"skips ... plan mode\" (README); both exist\n * only as example third-party extensions (`examples/extensions/\n * permission-gate.ts`, `examples/extensions/plan-mode/`), which is\n * out-of-scope, speculative surface for M0.\n * - `network: false` is NOT expressible: pi has no network sandbox for its\n * bash tool (\"Pi does not include a built-in sandbox\" — docs/security.md).\n * `network: true` or unset proceeds, since nothing needs enforcing then.\n *\n * Workspace confinement is NOT a pi flag — the caller spawns pi with\n * `cwd: ctx.workspaceDir`, the daemon-created per-task directory.\n *\n * - `--tools` is an allowlist and `--exclude-tools` is a denylist. Passing\n * both lets pi remain the authority for its active tool registry instead\n * of duplicating pi's default tool list in this adapter.\n */\nexport function mapPermissionPolicyToPiArgs(policy: PermissionPolicy): PiPermissionMapping {\n if (policy.network === false) {\n return {\n ok: false,\n args: [],\n reason: 'policy requires network:false, which the pi adapter cannot enforce (pi has no network sandbox)',\n };\n }\n\n if (policy.mode === 'confirm' || policy.mode === 'plan') {\n return {\n ok: false,\n args: [],\n reason: `pi adapter cannot express permission mode \"${policy.mode}\" (no built-in per-call approval gate or plan-only mode without a custom extension)`,\n };\n }\n\n const denyTools = policy.denyTools ?? [];\n\n if (policy.mode === 'readonly') {\n const base = policy.allowTools ? policy.allowTools.filter((tool) => READONLY_TOOLS.includes(tool)) : [...READONLY_TOOLS];\n // Never fall through to an absent `--tools` flag here — that would run\n // pi's full default toolset, silently widening a readonly request.\n if (base.length === 0) return { ok: true, args: ['--no-tools'] };\n return {\n ok: true,\n args: ['--tools', base.join(','), ...(denyTools.length > 0 ? ['--exclude-tools', denyTools.join(',')] : [])],\n };\n }\n\n const args: string[] = [];\n if (policy.allowTools && policy.allowTools.length > 0) {\n args.push('--tools', policy.allowTools.join(','));\n }\n if (denyTools.length > 0) args.push('--exclude-tools', denyTools.join(','));\n\n return { ok: true, args };\n}\n","import type { AgentEvent } from '@byok-sdk/protocol';\nimport type { PiRpcMessage } from './rpc-client';\n\n/**\n * Map pi 0.84.1 RPC frames into BYOK's runtime-neutral event contract.\n *\n * `agent_settled` is the only whole-task completion authority. `agent_end`\n * ends one low-level agent run, but pi may still perform retry/compaction or\n * consume queued work before it emits `agent_settled`. Pi's own `turn_end`\n * is even narrower: a tool-using prompt can emit several of them. Mapping\n * either earlier boundary to BYOK `turn_end` would acknowledge the task\n * before the runtime is actually idle.\n *\n * `message_update` is delta-only in this contract. The mapper forwards text\n * deltas and never reads the removed cumulative `message`/`partial` fields.\n */\nexport function mapPiMessageToAgentEvent(msg: PiRpcMessage): AgentEvent | undefined {\n switch (msg.type) {\n case 'message_update': {\n const delta = msg.assistantMessageEvent as { type?: string; delta?: string } | undefined;\n if (delta?.type === 'text_delta' && typeof delta.delta === 'string') {\n return { type: 'progress', text: delta.delta };\n }\n return undefined;\n }\n\n case 'tool_execution_start': {\n if (typeof msg.toolName !== 'string') return undefined;\n return { type: 'tool_use', tool: msg.toolName, input: msg.args };\n }\n\n case 'tool_execution_end': {\n if (typeof msg.toolName !== 'string') return undefined;\n return {\n type: 'tool_result',\n tool: msg.toolName,\n output: { result: msg.result, isError: msg.isError === true },\n };\n }\n\n case 'agent_settled':\n return { type: 'turn_end' };\n\n /**\n * `artifact` is NOT a real pi RPC message — pi's own `write` tool only\n * ever surfaces as `tool_execution_start`/`tool_execution_end` (see\n * `docs/tools/write.js` in the installed package), and neither carries\n * enough on its own at the `_end` message (no `path`) for this stateless,\n * one-message-at-a-time mapper to correlate back to a written file\n * without introducing per-toolCallId state. This case exists purely so\n * the `fake-pi.mjs` test/e2e fixture (M1-4 blob-path acceptance run) has\n * a way to simulate \"the runtime wrote a file and is reporting it as an\n * artifact\" — real pi emits nothing today that reaches this branch, so\n * production traffic through this adapter never takes it. Revisit if a\n * future pi release adds a native artifact-producing message, or if this\n * needs correlating to a real `write` tool call.\n */\n case 'artifact': {\n if (typeof msg.name !== 'string' || typeof msg.contentType !== 'string') return undefined;\n return { type: 'artifact', name: msg.name, contentType: msg.contentType };\n }\n\n case 'extension_error':\n return { type: 'error', message: typeof msg.error === 'string' ? msg.error : 'pi extension error' };\n\n case 'auto_retry_end':\n if (msg.success === false) {\n return {\n type: 'error',\n message: typeof msg.finalError === 'string' ? msg.finalError : 'pi auto-retry exhausted',\n };\n }\n return undefined;\n\n // Routine pi session/turn/streaming bookkeeping with no `AgentEvent`\n // equivalent — see `ROUTINE_PI_EVENT_TYPES` below, which mirrors this\n // list so `PiSession`'s unmapped-frame accounting (rpc-client.ts's\n // `recordUnmappedFrame`) can tell \"known, expected, silently ignored\"\n // apart from \"genuinely never seen before\" (falls to `default` below).\n case 'agent_start':\n case 'agent_end': // one low-level run; `agent_settled` is BYOK completion\n case 'turn_start':\n case 'turn_end': // pi's own per-LLM-turn boundary, not ours\n case 'message_start':\n case 'message_end':\n case 'bash_execution_update':\n case 'tool_execution_update':\n case 'queue_update':\n case 'compaction_start':\n case 'compaction_end':\n case 'auto_retry_start':\n case 'summarization_retry_scheduled':\n case 'summarization_retry_attempt_start':\n case 'summarization_retry_finished':\n case 'session_info_changed':\n case 'thinking_level_changed':\n return undefined;\n\n default:\n return undefined;\n }\n}\n\n/**\n * Pi RPC message types that are routine, expected, and deliberately have no\n * `AgentEvent` equivalent (kept in sync with the switch cases above sharing\n * this comment). Used only for observability: `PiSession`'s event iterator\n * (pi-adapter.ts) calls `PiRpcClient.recordUnmappedFrame` for any message\n * type that maps to `undefined` AND isn't in this set — i.e. traffic nobody\n * has ever told this adapter to expect. That distinction is what makes a\n * regression like a changed completion event\n * self-diagnosing: a warning fires the first time the new/renamed settle\n * event shows up, instead of the daemon just quietly hanging. `default`-only\n * unknowns (a type not listed in the switch at all) are equally \"not\n * routine\" and get flagged the same way — this set exists so *routine*\n * traffic doesn't also trip that alarm on every single task.\n */\nexport const ROUTINE_PI_EVENT_TYPES: ReadonlySet<string> = new Set([\n 'agent_start',\n 'agent_end',\n 'turn_start',\n 'turn_end',\n 'message_start',\n 'message_end',\n 'bash_execution_update',\n 'tool_execution_update',\n 'queue_update',\n 'compaction_start',\n 'compaction_end',\n 'auto_retry_start',\n 'summarization_retry_scheduled',\n 'summarization_retry_attempt_start',\n 'summarization_retry_finished',\n 'session_info_changed',\n 'thinking_level_changed',\n]);\n","/**\n * A minimal push/pull async queue: producers call `push`/`end`, consumers\n * `for await` over the queue itself. Shared by the pi RPC client's event\n * stream, the claude/codex adapters' own event streams, and test fixtures\n * (stub sessions) so all of them get the same order-preserving semantics\n * without duplicating it.\n *\n * M4 hardening: this used to be genuinely back-pressure-free — an unbounded\n * internal `buffered` array, so a producer that outpaces its consumer (a\n * runaway/misbehaving runtime adapter, or simply nobody ever reading\n * `session.events`) could grow it without limit. Bounded now: `capacity`\n * (default {@link DEFAULT_ASYNC_QUEUE_CAPACITY}) caps how many items may sit\n * in `buffered` unconsumed. On overflow this fails fast rather than either of\n * the two worse alternatives — silently dropping the newest/oldest event (a\n * consumer would never know its view of the stream has a hole in it) or\n * blocking `push()` (every producer in this codebase calls `push()`\n * synchronously from an I/O callback; blocking there would stall reading the\n * underlying process/socket entirely). Instead, once `capacity` is exceeded,\n * the queue transitions into a permanent error state: every pending and\n * future consumer `next()` call rejects with an {@link AsyncQueueOverflowError}\n * naming the capacity, and all further `push()` calls are silently ignored\n * (mirrors the pre-existing `ended` no-op precedent — once terminal, always\n * terminal). The error state is checked before `ended`, so it wins even if\n * `end()` is called afterward — a caller must learn the stream broke, not\n * see a quiet, ordinary completion.\n */\nexport class AsyncQueueOverflowError extends Error {\n constructor(public readonly capacity: number) {\n super(\n `AsyncQueue exceeded its capacity of ${capacity} buffered item(s) — failing fast rather than growing unbounded, silently dropping events, or blocking push()`,\n );\n this.name = 'AsyncQueueOverflowError';\n }\n}\n\n/** Default `capacity` (see {@link AsyncQueue}'s own doc comment) when the constructor isn't given one — generous for every producer in this codebase (adapter event streams a consumer reads turn-by-turn), while still catching a genuinely runaway/unconsumed stream instead of growing forever. */\nexport const DEFAULT_ASYNC_QUEUE_CAPACITY = 10_000;\n\ninterface QueueWaiter<T> {\n resolve: (result: IteratorResult<T>) => void;\n reject: (err: Error) => void;\n}\n\nexport class AsyncQueue<T> implements AsyncIterable<T> {\n private readonly buffered: T[] = [];\n private readonly waiters: QueueWaiter<T>[] = [];\n private ended = false;\n private failure: Error | undefined;\n\n constructor(private readonly capacity: number = DEFAULT_ASYNC_QUEUE_CAPACITY) {}\n\n push(item: T): void {\n if (this.ended || this.failure) return;\n const waiter = this.waiters.shift();\n if (waiter) {\n // Handed directly to an already-waiting consumer — never touches\n // `buffered`, so this path can never itself cause an overflow.\n waiter.resolve({ value: item, done: false });\n return;\n }\n if (this.buffered.length >= this.capacity) {\n this.fail(new AsyncQueueOverflowError(this.capacity));\n return;\n }\n this.buffered.push(item);\n }\n\n end(): void {\n if (this.ended || this.failure) return;\n this.ended = true;\n for (const waiter of this.waiters.splice(0)) {\n waiter.resolve({ value: undefined as never, done: true });\n }\n }\n\n /** Transitions the queue into its permanent overflow state: rejects every currently-pending waiter and, from then on, every `next()` call (checked ahead of `ended` — see the class doc comment on why the error must win). */\n private fail(error: Error): void {\n if (this.ended || this.failure) return;\n this.failure = error;\n for (const waiter of this.waiters.splice(0)) {\n waiter.reject(error);\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.failure) {\n return Promise.reject(this.failure);\n }\n if (this.buffered.length > 0) {\n return Promise.resolve({ value: this.buffered.shift() as T, done: false });\n }\n if (this.ended) {\n return Promise.resolve({ value: undefined as never, done: true });\n }\n return new Promise((resolve, reject) => this.waiters.push({ resolve, reject }));\n },\n };\n }\n}\n","import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport { AsyncQueue } from '../../util/async-queue';\n\nexport type SpawnFn = typeof spawn;\n\n/**\n * A pi RPC-mode message: either a `{type:\"response\", id, success, ...}` reply\n * to a command we sent, or an unsolicited event/extension-UI-request. Field\n * shapes vary by `type` (see docs.md / this task's live probes), so this\n * stays a loose bag rather than a full discriminated union — M0 only needs\n * a handful of fields off of each.\n */\nexport interface PiRpcMessage {\n type: string;\n id?: string;\n [key: string]: unknown;\n}\n\nexport interface PiRpcClientOptions {\n command: string;\n args: string[];\n cwd: string;\n env: NodeJS.ProcessEnv;\n spawnFn?: SpawnFn;\n}\n\n/** Bound on retained stderr lines (see `onStderr`/`buildExitError`) — enough to show a real crash reason without an unbounded footprint for a long-lived process that's merely chatty on stderr. */\nconst STDERR_RING_CAPACITY = 20;\n\n/**\n * pi's 4 blocking \"dialog\" extension-UI methods (rpc.md's \"Extension UI\n * Protocol\": `select`/`confirm`/`input`/`editor` emit an\n * `extension_ui_request` and BLOCK pi's whole run until a matching\n * `extension_ui_response` arrives). The other 5 methods\n * (`notify`/`setStatus`/`setWidget`/`setTitle`/`set_editor_text`) are\n * fire-and-forget and must never get a reply (rpc.md: \"Responses are sent\n * for dialog methods only\") — see `respondToExtensionUiRequest`.\n */\nconst DIALOG_UI_METHODS = new Set(['select', 'confirm', 'input', 'editor']);\n\n/**\n * JSONL request/response + event-stream client for `pi --mode rpc`.\n *\n * Framing per pi's own docs/rpc.md: strict JSONL, LF (`\\n`) as the only\n * record delimiter (a trailing `\\r` is stripped; this deliberately does NOT\n * use `node:readline`, which pi's docs call out as non-compliant because it\n * also splits on U+2028/U+2029 — valid inside JSON strings).\n *\n * Responses are correlated by `id`, never by arrival order: empirically,\n * pi's responses do not preserve request order (an immediate parse-failure\n * response can overtake a slower in-flight command's response).\n */\nexport class PiRpcClient {\n private readonly child: ChildProcessWithoutNullStreams;\n private buffer = '';\n private nextId = 1;\n private readonly pending = new Map<\n string,\n { resolve: (msg: PiRpcMessage) => void; reject: (err: Error) => void }\n >();\n private readonly eventQueue = new AsyncQueue<PiRpcMessage>();\n private closed = false;\n private exitError: Error | undefined;\n /** Bounded tail of recent stderr lines — pi discarded this entirely before (nothing ever read `child.stderr`), which is exactly why finding #1 (`Error: Unknown option: --session-id`, exit 1) had to be root-caused by hand instead of reading it off a thrown error. See `buildExitError`. */\n private readonly stderrRing: string[] = [];\n /** Count of pi RPC message types `PiSession` (pi-adapter.ts) has told us have no `AgentEvent` mapping and aren't routine bookkeeping — see `recordUnmappedFrame`. */\n private readonly unmappedFrameCounts = new Map<string, number>();\n\n constructor(options: PiRpcClientOptions) {\n const spawnFn = options.spawnFn ?? spawn;\n this.child = spawnFn(options.command, options.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: ['pipe', 'pipe', 'pipe'],\n }) as ChildProcessWithoutNullStreams;\n\n this.child.stdout.setEncoding('utf8');\n this.child.stdout.on('data', (chunk: string) => this.onData(chunk));\n\n this.child.stderr.setEncoding('utf8');\n this.child.stderr.on('data', (chunk: string) => this.onStderr(chunk));\n\n // `close` (not `exit`): `exit` can fire before stdio streams finish\n // flushing, which matters a lot for finding #1's \"instant exit\" shape —\n // `close` is Node's guarantee that every stdout/stderr byte the process\n // ever wrote has already been delivered to our listeners, so the error\n // built below always has the complete stderr tail, not a truncated one.\n this.child.on('close', (code, signal) => {\n this.onClosed(this.buildExitError(code, signal));\n });\n this.child.on('error', (err) => {\n this.onClosed(err);\n });\n }\n\n /** Send a command, resolved with its correlated `response` message. */\n send(command: Record<string, unknown> & { type: string; id?: string }): Promise<PiRpcMessage> {\n if (this.closed) {\n return Promise.reject(this.exitError ?? new Error('pi process is closed'));\n }\n const id = command.id ?? `req-${this.nextId++}`;\n const full = { ...command, id };\n return new Promise((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n this.child.stdin.write(`${JSON.stringify(full)}\\n`, (err) => {\n if (err) {\n this.pending.delete(id);\n reject(err);\n }\n });\n });\n }\n\n /** Every non-response, non-`extension_ui_request` line — the latter is answered directly by this client (see `respondToExtensionUiRequest`) and never enqueued. */\n get events(): AsyncIterable<PiRpcMessage> {\n return this.eventQueue;\n }\n\n /**\n * Record a pi RPC message `type` that `PiSession` (pi-adapter.ts) decided\n * has no `AgentEvent` mapping and isn't routine bookkeeping (see\n * `events.ts`'s `ROUTINE_PI_EVENT_TYPES`) — i.e. genuinely unexpected\n * traffic. Logs once per distinct type (not per occurrence, so a\n * repeating unmapped type can't spam stdout); the running tally is also\n * folded into this client's exit-time error message (`buildExitError`) so\n * a post-mortem on a failed/hung task has it without separate log scraping.\n */\n recordUnmappedFrame(type: string): void {\n const next = (this.unmappedFrameCounts.get(type) ?? 0) + 1;\n this.unmappedFrameCounts.set(type, next);\n if (next === 1) {\n console.warn(\n `[byok/pi-adapter] pi emitted a frame type with no AgentEvent mapping: \"${type}\" (further occurrences of this type won't be logged individually)`,\n );\n }\n }\n\n /** Best-effort teardown. SIGTERM on POSIX; `taskkill /T /F` on Windows to also reap child processes pi itself spawned (e.g. bash). */\n kill(): void {\n if (this.closed) return;\n const pid = this.child.pid;\n if (process.platform === 'win32' && pid !== undefined) {\n spawnSync('taskkill', ['/pid', String(pid), '/T', '/F']);\n } else {\n this.child.kill('SIGTERM');\n }\n }\n\n private onData(chunk: string): void {\n this.buffer += chunk;\n let newlineIndex = this.buffer.indexOf('\\n');\n while (newlineIndex !== -1) {\n let line = this.buffer.slice(0, newlineIndex);\n this.buffer = this.buffer.slice(newlineIndex + 1);\n if (line.endsWith('\\r')) line = line.slice(0, -1);\n if (line.length > 0) this.onLine(line);\n newlineIndex = this.buffer.indexOf('\\n');\n }\n }\n\n private onLine(line: string): void {\n let msg: PiRpcMessage;\n try {\n msg = JSON.parse(line) as PiRpcMessage;\n } catch {\n return; // a stray non-JSON line is not this client's concern\n }\n if (msg.type === 'response' && typeof msg.id === 'string' && this.pending.has(msg.id)) {\n const waiter = this.pending.get(msg.id);\n this.pending.delete(msg.id);\n waiter?.resolve(msg);\n return;\n }\n if (msg.type === 'extension_ui_request' && typeof msg.id === 'string' && typeof msg.method === 'string') {\n // Answered here, not enqueued — see `respondToExtensionUiRequest`. If\n // left unanswered this blocks pi's entire run (process alive, waiting\n // on stdin forever), so every blocking dialog is handled here.\n this.respondToExtensionUiRequest(msg.id, msg.method);\n return;\n }\n this.eventQueue.push(msg);\n }\n\n /**\n * Answer pi's extension-UI blocking protocol headlessly (rpc.md's\n * \"Extension UI Protocol\"). Fail-closed policy, stated explicitly because\n * it's a security-relevant default, not an incidental one: this NEVER\n * approves or picks a value on the caller's behalf — every dialog method\n * (`select`/`confirm`/`input`/`editor`) gets `{cancelled: true}`, the one\n * response shape rpc.md documents as valid for all four uniformly\n * (\"Dismiss any dialog method... the extension receives `undefined` (for\n * select/input/editor) or `false` (for confirm)\"). An extension asking\n * e.g. `confirm(\"Delete everything?\")` gets a firm decline, never a\n * guessed approval — this adapter has no human in the loop to ask, and\n * silently approving would defeat any extension that uses these dialogs\n * specifically as a permission gate. Fire-and-forget methods\n * (`notify`/`setStatus`/`setWidget`/`setTitle`/`set_editor_text`) get no\n * reply at all — sending one would itself violate rpc.md (\"Responses are\n * sent for dialog methods only\").\n */\n private respondToExtensionUiRequest(id: string, method: string): void {\n if (!DIALOG_UI_METHODS.has(method)) return; // fire-and-forget — no response expected or sent\n this.child.stdin.write(`${JSON.stringify({ type: 'extension_ui_response', id, cancelled: true })}\\n`);\n }\n\n private onStderr(chunk: string): void {\n for (const rawLine of chunk.split('\\n')) {\n const line = rawLine.trim();\n if (line.length === 0) continue;\n this.stderrRing.push(line);\n if (this.stderrRing.length > STDERR_RING_CAPACITY) this.stderrRing.shift();\n }\n }\n\n /**\n * finding #1 (\"bad flag → instant exit\", e.g. the `--session-id`/\n * `--exclude-tools` bugs this task fixes): the daemon used to report only\n * `pi process exited (code=1, signal=null)` — accurate but useless for\n * diagnosing *why* without re-running pi by hand with a raw JSONL logger,\n * exactly as this task's own root-cause investigation had to. Folding in\n * the stderr tail and any unmapped-frame tally makes that self-diagnosing\n * from the thrown error alone.\n */\n private buildExitError(code: number | null, signal: NodeJS.Signals | null): Error {\n const parts = [`pi process exited (code=${code}, signal=${signal})`];\n if (this.stderrRing.length > 0) {\n parts.push(`stderr: ${this.stderrRing.join(' | ')}`);\n }\n if (this.unmappedFrameCounts.size > 0) {\n const summary = [...this.unmappedFrameCounts.entries()].map(([type, count]) => `${type}×${count}`).join(', ');\n parts.push(`unmapped frame types seen: ${summary}`);\n }\n return new Error(parts.join('; '));\n }\n\n private onClosed(err: Error): void {\n if (this.closed) return;\n this.closed = true;\n this.exitError = err;\n for (const [, waiter] of this.pending) waiter.reject(err);\n this.pending.clear();\n this.eventQueue.end();\n }\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport type { AgentEvent, TaskOfferPayload } from '@byok-sdk/protocol';\nimport {\n PolicyUnsupportedError,\n type RuntimeAdapter,\n type RuntimeCapabilities,\n type RuntimeDetectResult,\n type RuntimeEnvironmentRequirements,\n type Session,\n type TaskContext,\n} from '../../types';\nimport { resolvePiBin, type ResolvedBin } from './resolve-bin';\nimport { mapPermissionPolicyToPiArgs } from './permission-mapping';\nimport { mapPiMessageToAgentEvent, ROUTINE_PI_EVENT_TYPES } from './events';\nimport { PiRpcClient, type PiRpcMessage, type SpawnFn } from './rpc-client';\n\nconst execFileAsync = promisify(execFile);\nconst DETECT_TIMEOUT_MS = 5_000;\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Known provider credential env var *names* (never values) — see the\n * credential-isolation rule on `RuntimeAdapter`. `detect()` only checks\n * whether one of these names is set; it never reads pi's own auth storage\n * (`~/.pi/...`) or any file contents. Not exhaustive (pi supports ~30\n * providers); covers the common ones for a useful `authPresent` signal.\n */\nconst KNOWN_PROVIDER_ENV_VARS = [\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_OAUTH_TOKEN',\n 'OPENAI_API_KEY',\n 'GEMINI_API_KEY',\n 'AZURE_OPENAI_API_KEY',\n 'DEEPSEEK_API_KEY',\n 'GROQ_API_KEY',\n 'MISTRAL_API_KEY',\n 'OPENROUTER_API_KEY',\n 'XAI_API_KEY',\n // Confirmed against the installed pi's own docs/providers.md (\"ZAI |\n // `ZAI_API_KEY` | `zai`\") and exercised live against real GLM traffic\n // during this task's acceptance run — omitting it made `authPresent`\n // silently false for a perfectly valid, working z.ai/GLM setup.\n 'ZAI_API_KEY',\n] as const;\n\nexport interface PiAdapterOptions {\n /** Override bin resolution — tests substitute the fake-pi fixture script. */\n resolveBin?: () => ResolvedBin;\n /** Override process spawning — tests substitute a fake spawn. */\n spawnFn?: SpawnFn;\n}\n\nexport class PiAdapter implements RuntimeAdapter {\n readonly id = 'pi';\n\n constructor(private readonly options: PiAdapterOptions = {}) {}\n\n async detect(): Promise<RuntimeDetectResult> {\n try {\n const bin = this.resolveBin();\n // Empirically, pi 0.84.1 prints `--version` to stdout. Check stderr too\n // so detection remains accurate if a future pinned release moves it.\n const { stdout, stderr } = await execFileAsync(bin.command, ['--version'], { timeout: DETECT_TIMEOUT_MS });\n const version = stdout.trim() || stderr.trim();\n const authPresent = KNOWN_PROVIDER_ENV_VARS.some((name) => process.env[name] !== undefined);\n return { present: true, version, authPresent };\n } catch {\n return { present: false };\n }\n }\n\n capabilities(): RuntimeCapabilities {\n // `approvalInteractive: false` — `PiSession.resolveApproval` throws\n // unconditionally: pi has no `needs_approval` notion to resume from.\n return { steer: true, resume: true, approvalInteractive: false, permissionModes: ['auto', 'readonly'] };\n }\n\n /**\n * M5: pi authenticates to its ~30 supported providers via env-var API\n * keys — `detect()`'s own `authPresent` probe above checks this identical\n * list — so these MUST keep flowing into pi's spawned process or pi auth\n * breaks entirely. `KNOWN_PROVIDER_ENV_VARS` above is the single source\n * of truth, reused here rather than duplicated. No `baseNames`: nothing\n * in this adapter or `rpc-client.ts` reads a pi-specific config-discovery\n * variable beyond the platform baseline (`daemon/environment.ts`).\n */\n environmentRequirements(): RuntimeEnvironmentRequirements {\n return { credentialNames: KNOWN_PROVIDER_ENV_VARS };\n }\n\n async start(task: TaskOfferPayload, ctx: TaskContext): Promise<Session> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('pi adapter only supports string instructions in M0 (no blob-ref fetch yet)');\n }\n\n const mapping = mapPermissionPolicyToPiArgs(ctx.policy);\n if (!mapping.ok) {\n throw new PolicyUnsupportedError(mapping.reason ?? 'policy rejected by pi adapter');\n }\n\n const bin = this.resolveBin();\n // Session/workspace continuity:\n // `task.sessionRef` is only ever non-empty here when `task-runner.ts`\n // has (a) found a recorded workspace for this exact sessionRef in its\n // `SessionWorkspaceStore` and (b) spawned this adapter with\n // `ctx.workspaceDir` set to that SAME directory — see task-runner.ts's\n // `handleOffer`. That matters because pi's real `--session <id>` resume\n // is scoped to the cwd/project a session was created under: resuming\n // from a *different* cwd prompts an interactive \"Session found in\n // different project: ... Fork this session into current directory?\n // [y/N]\" pi cannot answer headlessly, and resuming an id pi never\n // minted fails outright (`No session found matching '<id>'`, exit 1 —\n // empirically confirmed against real pi, both live-probed during this\n // task). An absent `sessionRef` always means \"start fresh\" — pi mints\n // its own session id, which this adapter reads back via `get_state`\n // below and reports as `Session.sessionRef` so a *future* follow-up can\n // resume it. `TaskOfferPayload.workspaceHint` remains unimplemented (no\n // caller populates `DispatchInput` with it yet, and its intended\n // semantics — e.g. does it override or merely suggest a workspace\n // relative to the sessionRef mapping? — are still undesigned; see\n // docs/protocol.md §2's note on this field for the explicit\n // reserved/ignored status); a real implementation is a genuine\n // follow-on design task, not a mechanical fix, and is intentionally\n // left alone here. pi 0.84.1 also offers `--session-id`, but that flag\n // creates a missing session. BYOK deliberately uses `--session` so a\n // lost/unknown authoritative sessionRef fails closed instead of silently\n // starting a new history under the requested id.\n const resumeSessionId = task.sessionRef;\n const args = ['--mode', 'rpc', ...(resumeSessionId ? ['--session', resumeSessionId] : []), ...mapping.args];\n\n const rpc = new PiRpcClient({\n command: bin.command,\n args,\n cwd: ctx.workspaceDir,\n env: ctx.env,\n spawnFn: this.options.spawnFn,\n });\n\n const response = await rpc.send({ type: 'prompt', message: task.instruction });\n if (response.success === false) {\n rpc.kill();\n throw new Error(typeof response.error === 'string' ? response.error : 'pi rejected the initial prompt');\n }\n\n let sessionRef: string;\n if (resumeSessionId) {\n sessionRef = resumeSessionId;\n } else {\n try {\n sessionRef = await resolveFreshSessionId(rpc);\n } catch (err) {\n // Finding F8: fail closed, not a fabricated id — and don't leak the\n // process this adapter just spawned (the `response.success===false`\n // branch above already kills it on ITS failure path; this mirrors\n // that for get_state's).\n rpc.kill();\n throw err;\n }\n }\n return new PiSession(sessionRef, rpc);\n }\n\n private resolveBin(): ResolvedBin {\n return (this.options.resolveBin ?? resolvePiBin)();\n }\n}\n\n/**\n * Learn pi's own real session id for a freshly-started (non-resume) run, so\n * `Session.sessionRef` reports something a *future* follow-up can actually\n * resume via `--session <id>`. `get_state.data.sessionId` is populated from\n * the moment pi's RPC process boots (confirmed live: present even before\n * any prompt is sent, with `messageCount: 0`), so this is safe to call\n * right after the initial prompt is accepted.\n *\n * Finding F8 (fabricated sessionRef): this used to fall back to\n * `crypto.randomUUID()` whenever `get_state` failed, timed out, or omitted\n * `sessionId` — minting an id pi itself never knew about, which could never\n * actually be resumed and silently looked like a legitimate, resumable\n * session to every caller (`TaskRunner`'s `SessionWorkspaceStore`, a future\n * follow-up's `task.offer.sessionRef`, etc). Fail closed instead: if pi\n * doesn't hand back an authoritative session id, `start()` itself fails\n * with the real underlying error (stderr context is already folded in when\n * the rejection comes from the process exiting — see\n * `PiRpcClient.buildExitError`), exactly like any other adapter start()\n * failure `task-runner.ts` already knows how to report as `task.fail`.\n */\nasync function resolveFreshSessionId(rpc: PiRpcClient): Promise<string> {\n let state: PiRpcMessage;\n try {\n state = await rpc.send({ type: 'get_state' });\n } catch (err) {\n throw new Error(`pi did not yield an authoritative session id (get_state failed): ${errorMessage(err)}`, {\n cause: err,\n });\n }\n\n if (state.success === false) {\n const reason = typeof state.error === 'string' ? state.error : 'get_state reported failure';\n throw new Error(`pi did not yield an authoritative session id (get_state failed): ${reason}`);\n }\n\n const data = state.data as { sessionId?: unknown } | undefined;\n if (typeof data?.sessionId === 'string' && data.sessionId.length > 0) {\n return data.sessionId;\n }\n\n throw new Error(\n 'pi did not yield an authoritative session id (get_state succeeded but reported no sessionId) — cannot mint a resumable session',\n );\n}\n\nclass PiSession implements Session {\n constructor(\n public readonly sessionRef: string,\n private readonly rpc: PiRpcClient,\n ) {}\n\n get events(): AsyncIterable<AgentEvent> {\n const rpc = this.rpc;\n return {\n [Symbol.asyncIterator](): AsyncIterator<AgentEvent> {\n const inner = rpc.events[Symbol.asyncIterator]();\n return {\n async next(): Promise<IteratorResult<AgentEvent>> {\n for (;;) {\n const { value, done } = await inner.next();\n if (done) return { value: undefined as never, done: true };\n const mapped = mapPiMessageToAgentEvent(value);\n if (mapped) return { value: mapped, done: false };\n // Unmapped pi message: routine bookkeeping (compaction/retry/\n // session events — see ROUTINE_PI_EVENT_TYPES) is silently\n // ignored, same as before; anything else is genuinely\n // unexpected traffic worth flagging (see recordUnmappedFrame's\n // doc comment) — keep pulling either way, never surfaced.\n if (!ROUTINE_PI_EVENT_TYPES.has(value.type)) {\n rpc.recordUnmappedFrame(value.type);\n }\n }\n },\n };\n },\n };\n }\n\n async steer(text: string): Promise<void> {\n await this.rpc.send({ type: 'steer', message: text });\n }\n\n async followUp(task: TaskOfferPayload): Promise<void> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('pi adapter only supports string instructions in M0 (no blob-ref fetch yet)');\n }\n await this.rpc.send({ type: 'prompt', message: task.instruction, streamingBehavior: 'followUp' });\n }\n\n async interrupt(): Promise<void> {\n await this.rpc.send({ type: 'abort' });\n }\n\n async close(): Promise<void> {\n this.rpc.kill();\n }\n\n async resolveApproval(): Promise<void> {\n // pi has no built-in per-call approval gate (see permission-mapping.ts)\n // and never emits `needs_approval` in M0/M1, so this should be\n // unreachable in practice. Kept as an explicit, descriptive failure\n // rather than a silent no-op so a future caller (or a misbehaving\n // server) gets a clear error instead of a hang.\n throw new Error('pi adapter does not support approval resume: pi never emits needs_approval in M0/M1');\n }\n}\n","export interface ResolvedBin {\n command: string;\n source: 'env' | 'path';\n}\n\n/**\n * Resolve the `claude` (Claude Code) CLI executable.\n *\n * Unlike pi (`../pi/resolve-bin.ts`), this package does NOT bundle a\n * matched `claude` build as a dependency. Claude Code is the end\n * user's own globally-installed, individually-authenticated CLI (`claude\n * auth login`, tied to their Anthropic/claude.ai account) — there is\n * nothing useful to vendor: a bundled copy could never carry the user's own\n * login state, and the credential-isolation rule (see `../../types.ts`'s\n * `RuntimeAdapter` doc comment — this adapter must never read, proxy, or\n * forward `~/.claude`'s own auth storage) means this adapter has no\n * business managing a claude install at all, only spawning whatever `claude`\n * the user already has authenticated on their PATH.\n *\n * Resolution is therefore deliberately two-tier, not three like pi's:\n * `BYOK_CLAUDE_BIN` overrides everything when set (the injectable seam for\n * in-process tests — mirrors `BYOK_PI_BIN` and substitutes the\n * `fake-claude.mjs` fixture ahead of a real claude install, exactly as pi's\n * own override does), otherwise this falls back to the literal command name\n * `claude`, resolved via the child process's own PATH lookup — there is no\n * package-resolution tier in between.\n */\nexport function resolveClaudeBin(): ResolvedBin {\n const override = process.env.BYOK_CLAUDE_BIN;\n if (override) {\n return { command: override, source: 'env' };\n }\n return { command: 'claude', source: 'path' };\n}\n","import { fileURLToPath } from 'node:url';\nimport path from 'node:path';\n\nexport interface ResolvedApprovalMcpBin {\n command: string;\n args: string[];\n source: 'env' | 'dist';\n}\n\n/**\n * Resolve `byok-approval-mcp` — the small stdio MCP server\n * (`bin/byok-approval-mcp.ts`) `claude`'s own `--permission-prompt-tool`\n * spawns as ITS child process (see that file's doc comment, and\n * `permission-mapping.ts`'s `confirm`-mode doc comment, for the full design).\n *\n * Unlike `resolveClaudeBin` (the end user's own separately-installed,\n * separately-authenticated CLI, resolved via bare-name PATH lookup),\n * `byok-approval-mcp` is a script THIS SAME `@byok-sdk/client` package ships —\n * bare-name PATH lookup is NOT safe for it: `@byok-sdk/client` is typically a\n * project-local dependency, so its `node_modules/.bin/byok-approval-mcp`\n * symlink is only on PATH for processes that inherit THAT project's own\n * shell/PATH, not reliably for a background OS service (launchd/systemd\n * often run with a stripped-down PATH that omits project-local\n * `node_modules/.bin` entirely — see `templates/service/**`). Resolving an\n * ABSOLUTE path to this package's own compiled bin avoids depending on PATH\n * at all.\n *\n * `BYOK_APPROVAL_MCP_BIN` overrides everything when set — the injectable\n * seam for tests (mirrors `BYOK_CLAUDE_BIN`/`BYOK_PI_BIN`), letting a test\n * substitute a fixture script instead of computing any real path. The\n * override is a single command string with no separate args (tests don't\n * need to invoke it any differently than `node <script>`); the real default\n * below is `node <absolute-path-to-the-built-bin>`.\n *\n * The default computation is deliberately anchored to THIS module's own\n * `import.meta.url`, resolved once at the real production entry point: when\n * `@byok-sdk/client` is built (`tsup.config.ts`), this file's code ends up\n * bundled into `dist/index.js` at the package root, with `dist/bin/\n * byok-approval-mcp.js` as its direct sibling (same layout `byok-agent.js`\n * already uses) — `path.join(path.dirname(fileURLToPath(import.meta.url)),\n * 'bin', 'byok-approval-mcp.js')` is therefore correct for that one real\n * shape. It is NOT correct for this file's own unbundled TypeScript source\n * location (`src/adapters/claude/` is two directories deeper than `src/`),\n * but nothing in this codebase ever reaches this fallback unbundled — every\n * test that exercises `confirm` mode sets `BYOK_APPROVAL_MCP_BIN` explicitly\n * (see `claude-adapter.test.ts`), exactly like `BYOK_CLAUDE_BIN` already\n * does for the real `claude` binary.\n */\nexport function resolveApprovalMcpBin(): ResolvedApprovalMcpBin {\n const override = process.env.BYOK_APPROVAL_MCP_BIN;\n if (override) {\n return { command: override, args: [], source: 'env' };\n }\n const distBin = path.join(path.dirname(fileURLToPath(import.meta.url)), 'bin', 'byok-approval-mcp.js');\n return { command: process.execPath, args: [distBin], source: 'dist' };\n}\n","import type { PermissionPolicy } from '@byok-sdk/protocol';\n\nexport interface ClaudePermissionMapping {\n ok: boolean;\n /** CLI args to append to `claude -p ...`. Only meaningful when `ok` is true. */\n args: string[];\n /** Present when `ok` is false. */\n reason?: string;\n /**\n * M4 Phase 3: set only for `policy.mode === 'confirm'` — tells\n * `claude-adapter.ts`'s `start()` to additionally spin up the out-of-band\n * approval channel (write the temp `--mcp-config` file pointing at\n * `bin/byok-approval-mcp.ts`, then append `--permission-prompt-tool`) on\n * top of the base `args` returned here. Kept out of `args` itself because\n * generating that config is a real filesystem side effect (a temp\n * directory + file write) that has no business happening inside this\n * otherwise-pure, I/O-free mapping function — see `claude-adapter.ts`'s\n * `start()` for where it actually happens.\n */\n needsApprovalMcp?: boolean;\n}\n\n/**\n * Claude Code's real, capitalized built-in read-only tool names. `Read` is\n * fully empirically confirmed on the installed 2.1.212 binary two ways: (a)\n * under `--permission-mode default` (deny-by-default) with `--allowedTools\n * Read`, a Write attempt is cleanly denied (`permission_denials` populated,\n * no hang); (b) more importantly, under `--tools Read` specifically (the\n * *replacive* flag — see the module doc comment below on why this, not\n * `--allowedTools`, is what this mapper actually uses), the model's own\n * reported tool list (`system/init`'s `tools` array) shrinks to exactly\n * `['Read', ...any always-on MCP tools]` and it cannot even attempt Write\n * (no permission prompt needed — the tool simply isn't offered to it).\n * `Glob`/`Grep` are included by strong, well-documented Claude Code naming\n * convention (identical capitalization pattern to the four tool names\n * empirically confirmed here: Read/Write/Edit/Bash) and were confirmed as\n * syntactically-accepted `--tools`/`--allowedTools` argument values on this\n * real installed binary (no \"unknown tool\" rejection) — but this\n * particular dev machine runs a heavily customized/plugin-extended Claude\n * Code build whose own active tool surface does not include Glob or Grep at\n * all (its `system/init.tools` array is a bespoke orchestration-tool set,\n * not vanilla Claude Code's), so their exact allow-through behavior could\n * not be independently exercised end-to-end here the way Read's was. Flagged\n * for the M2-c freeze decision, not silently assumed.\n */\nconst READONLY_TOOLS: readonly string[] = ['Read', 'Glob', 'Grep'];\n\n/**\n * Map an effective {@link PermissionPolicy} to `claude -p ...` CLI args,\n * fail-closed. Empirically grounded against the real installed `claude`\n * 2.1.212 binary (see the M2-a report) — every claim below was reproduced\n * live, not inferred from `--help` text or training-data recall (`--help`\n * text alone was actively misleading in at least one case: `--allowedTools`\n * reads like a hard allowlist but is NOT one — see the finding below).\n *\n * ## Two DIFFERENT, easily-confused tool-control flags (the central finding)\n *\n * `claude --help` documents two separate flag families that look\n * interchangeable but are not:\n *\n * - `--allowedTools` / `--disallowedTools`: a PERMISSION pre-grant/deny\n * list. Empirically, this only affects whether a tool call needs an\n * interactive prompt — it does NOT reliably restrict what the model can\n * do once a broadly-permissive `--permission-mode` (acceptEdits,\n * bypassPermissions, dontAsk) is also in effect. Confirmed two ways: (a)\n * `--permission-mode acceptEdits --allowedTools Read` still let a Write\n * call succeed (zero `permission_denials`) — the allowlist was silently\n * ignored once the broad grant was in effect; (b) `--permission-mode\n * acceptEdits --disallowedTools Write` correctly blocked the *named*\n * `Write` tool call (`<tool_use_error>Error: No such tool available:\n * Write...`), but the model then simply used `Bash` instead (still\n * enabled) to write the same file — `(Bash completed with no output)`,\n * file created anyway. A single denied tool name is not a security\n * boundary when a general-purpose Bash tool remains available.\n * - `--tools`: REPLACES the entire active built-in tool set (pi's own\n * `--tools` flag works the same way — this is the one place claude and pi\n * share an identical convention). Confirmed: `--tools Read` shrinks\n * `system/init`'s reported `tools` array to just `['Read', ...MCP\n * tools]`; the model then cannot even attempt Write (no tool definition\n * to call), regardless of `--permission-mode`. `--tools \"\"` disables\n * every built-in tool outright (confirmed: only the always-on MCP tool\n * remained). This is the ONLY mechanism this mapper trusts to actually\n * restrict the model's capability surface — never `--allowedTools`/\n * `--disallowedTools` for that purpose.\n *\n * ## Mode mapping\n *\n * - `auto` (no `allowTools`): `--permission-mode acceptEdits`. Empirically\n * confirmed to auto-accept BOTH file edits (Write) and Bash execution\n * with zero `permission_denials` — broader than the interactive-mode\n * folklore that acceptEdits only covers file edits. Verified for a\n * benign `echo ... > file` Bash command specifically; not exhaustively\n * verified across every possible Bash command shape.\n * - `auto` with `allowTools` (no `denyTools`): adds `--tools\n * <allowTools.join(',')>` (the replacive flag) on top of `acceptEdits`,\n * mirroring pi's own `allowTools`-only branch.\n * - `readonly`: ALWAYS `--permission-mode default` (never acceptEdits/\n * bypassPermissions/dontAsk — see the finding above: a permissive mode\n * defeats any restriction) plus `--tools\n * <intersect(allowTools ?? READONLY_TOOLS, READONLY_TOOLS) - denyTools>`.\n * An empty resulting set emits `--tools \"\"` explicitly — never an absent\n * `--tools` flag, which would default to claude's full active set and\n * silently widen a readonly request (the exact class of bug pi's own\n * `--no-tools` fallback exists to prevent).\n * - `plan`: `--permission-mode plan`. Empirically confirmed to never\n * execute the requested mutating tool call against its real target — the\n * model instead writes a plan document and stops. **Caveat, flagged for\n * the M2-c freeze decision, not silently hidden**: it writes that plan\n * file to `~/.claude/plans/<slug>.md` — the real user's home directory,\n * OUTSIDE `ctx.workspaceDir` — unconditionally, regardless of cwd. This\n * is a genuine, confirmed workspace-confinement gap specific to plan\n * mode's own bookkeeping (the path is fixed/product-owned by Claude Code\n * itself, not attacker/model-directed, and no destructive action runs\n * against the actual task target) — mapped as supported rather than\n * failed-closed because refusing would make an entire policy mode whose\n * name and semantics match this protocol's own `plan` mode 1:1\n * completely unusable over a relatively minor, fixed-path side effect,\n * but this is a judgment call for a human to weigh in on, not a fact.\n * - `confirm`: SUPPORTED as of M4 Phase 3 — `--permission-mode default`\n * (deny-by-default baseline, same as `readonly`'s own choice above) plus\n * `allowTools`/`denyTools` mapped exactly like `auto` does (finding F2,\n * fixed same session as the confirm-mode support itself first shipped —\n * see the function body below): an explicit `--tools <allowTools>` when\n * only `allowTools` is set, and a fail-closed refusal whenever `denyTools`\n * is non-empty, since confirm — like `auto`/`plan` and unlike\n * `readonly` — has no bounded, known-safe base tool list to subtract\n * from; the only trustworthy restriction mechanism (`--tools`) is\n * replacive, not subtractive. This composes with, rather than replaces,\n * `--permission-prompt-tool` pointed at a small bundled MCP server\n * (`bin/byok-approval-mcp.ts`) that forwards the pending permission\n * decision to this device's own daemon over its control socket and blocks\n * until a human (or the daemon's own timeout) resolves it. This directly\n * supersedes the M2-a finding that `confirm` was inexpressible: that\n * finding was empirically true for `--permission-mode` ALONE (every\n * decision resolved synchronously, no pause) — `--permission-prompt-tool`\n * is a DIFFERENT flag, undocumented in `claude --help`'s output on the\n * installed 2.1.216 binary (confirmed accepted anyway — an unrecognized\n * flag is rejected outright with `error: unknown option`, this one is\n * not), that genuinely pauses the turn on a real MCP round-trip while\n * claude waits for an answer (M4 Phase 3 STEP 0: live-verified allow,\n * deny, and multi-second-delayed-then-allow, against the real installed\n * binary — see `../../bin/byok-approval-mcp.ts`'s own doc comment for the\n * full empirical writeup, including the one caveat found: a permission-\n * prompt-tool call that NEVER answers at all triggers claude's own\n * internal abandonment of the turn after roughly 1.5s — never actually hit\n * by this design, since `byok-approval-mcp` always eventually answers\n * within its own configured ceiling, but disclosed rather than silently\n * assumed away).\n * - `denyTools` non-empty under `auto`: FAILS CLOSED. Given the\n * `--allowedTools`/`--disallowedTools`-under-a-permissive-mode escape\n * hatch above, the only mechanism this mapper trusts (`--tools`) is\n * REPLACIVE, not subtractive — pi can resolve `denyTools` to an\n * equivalent allowlist because pi's own default active tool set is\n * fixed and known from its installed source; claude's active tool\n * surface is NOT reliably known ahead of time (empirically, this exact\n * dev machine's own installed build exposes a bespoke, non-vanilla tool\n * set — see `READONLY_TOOLS`'s doc comment), so there is no reliable\n * \"default set minus these\" this mapper can construct. Refusing is the\n * fail-closed choice over guessing a set that might not match reality.\n *\n * `network: false` fails closed for the same reason as pi: no verified\n * network sandbox exists for claude's Bash tool either (`claude --help`\n * exposes no network/sandbox flag at all) — this was not independently\n * re-verified against real network traffic the way the tool-restriction\n * findings above were (doing so would require an actual network probe this\n * task didn't run), but is the same conservative, precedent-consistent\n * default pi already applies for an unverifiable constraint.\n */\nexport function mapPermissionPolicyToClaudeArgs(policy: PermissionPolicy): ClaudePermissionMapping {\n if (policy.network === false) {\n return {\n ok: false,\n args: [],\n reason: 'policy requires network:false, which the claude adapter cannot enforce (claude has no network sandbox for its Bash tool)',\n };\n }\n\n const denyTools = policy.denyTools ?? [];\n\n if (policy.mode === 'confirm') {\n // Deny-by-default baseline (never a permissive mode — see readonly's own\n // reasoning above): `--permission-prompt-tool` only intercepts a\n // decision that would otherwise need ONE, so a permissive mode here\n // would defeat confirm's whole point by auto-granting first. The actual\n // `--permission-prompt-tool`/`--mcp-config` flags are appended by\n // `claude-adapter.ts`'s `start()` (see `needsApprovalMcp` above), not\n // here, since generating the mcp-config file is a real I/O side effect.\n //\n // Finding F2 (cross-model adversarial review): this branch used to\n // return unconditionally right here, silently discarding\n // `allowTools`/`denyTools` entirely — a `{mode:'confirm',\n // denyTools:['Bash']}` policy would still let Bash through (subject\n // only to a per-call human prompt), never actually enforcing the\n // caller's denial; that's a silent widening of the requested policy,\n // exactly what this mapper fails closed for everywhere else. Confirm\n // has no bounded, known-safe base tool list the way `readonly` does\n // (`READONLY_TOOLS`) — its whole point is to allow the FULL active\n // tool surface, gated by a human decision per call, not a fixed small\n // set — so its `denyTools` problem is the SAME shape as `auto`/`plan`'s\n // below: the only mechanism this mapper trusts (`--tools`) is\n // REPLACIVE, not subtractive, and there is no reliable \"full active set\n // minus denied\" to compute. Mirrors `auto`'s handling exactly: fail\n // closed whenever `denyTools` is non-empty (composed with `allowTools`\n // or not — an inexpressible constraint is inexpressible either way),\n // otherwise an explicit `--tools <allowTools>` when `allowTools` alone\n // is set.\n if (denyTools.length > 0) {\n return {\n ok: false,\n args: [],\n reason: `claude adapter cannot reliably enforce denyTools ([${denyTools.join(', ')}]) under confirm mode: its only trustworthy tool-restriction mechanism (--tools) replaces the whole active set rather than subtracting from it, and this adapter has no reliable way to learn a target installation's full default tool set to compute \"default minus denied\" (this dev machine's own installed build exposes a non-vanilla, bespoke tool list) — refusing rather than risking a denial that silently doesn't hold`,\n };\n }\n const confirmArgs = ['--permission-mode', 'default'];\n if (policy.allowTools && policy.allowTools.length > 0) {\n confirmArgs.push('--tools', policy.allowTools.join(','));\n }\n return { ok: true, args: confirmArgs, needsApprovalMcp: true };\n }\n\n if (policy.mode === 'readonly') {\n const base = policy.allowTools ? policy.allowTools.filter((tool) => READONLY_TOOLS.includes(tool)) : [...READONLY_TOOLS];\n const effective = subtractDenied(base, denyTools);\n // Never fall through to an absent `--tools` flag here — that would run\n // claude's full active toolset, silently widening a readonly request.\n return { ok: true, args: ['--permission-mode', 'default', '--tools', effective.join(',')] };\n }\n\n // `auto` and `plan` share the same tool-restriction logic below — plan\n // mode never executes a mutating call regardless (see the doc comment\n // above), so an `allowTools`/`denyTools` restriction only shapes what the\n // model is even offered to plan around, not a security boundary for plan\n // mode specifically. Kept as one branch for both, rather than duplicating\n // the denyTools fail-closed reasoning twice.\n if (denyTools.length > 0) {\n return {\n ok: false,\n args: [],\n reason: `claude adapter cannot reliably enforce denyTools ([${denyTools.join(', ')}]): its only trustworthy tool-restriction mechanism (--tools) replaces the whole active set rather than subtracting from it, and this adapter has no reliable way to learn a target installation's full default tool set to compute \"default minus denied\" (this dev machine's own installed build exposes a non-vanilla, bespoke tool list) — refusing rather than risking a denial that silently doesn't hold`,\n };\n }\n\n const permissionMode = policy.mode === 'plan' ? 'plan' : 'acceptEdits';\n const args = ['--permission-mode', permissionMode];\n if (policy.allowTools && policy.allowTools.length > 0) {\n args.push('--tools', policy.allowTools.join(','));\n }\n return { ok: true, args };\n}\n\nfunction subtractDenied(tools: readonly string[], denyTools: readonly string[]): string[] {\n const denied = new Set(denyTools);\n return tools.filter((tool) => !denied.has(tool));\n}\n","import { realpathSync } from 'node:fs';\nimport path from 'node:path';\nimport type { AgentEvent } from '@byok-sdk/protocol';\n\n/**\n * A raw parsed line from `claude --output-format stream-json`. Shapes vary\n * a lot by `type` (and, for `system`, by `subtype`) — see the doc comments\n * on the individual mapping functions below for the concrete shapes this\n * was empirically captured against. Kept as a loose bag rather than a full\n * discriminated union for the same reason pi's `PiRpcMessage` is: this\n * module only needs a handful of fields off of each frame.\n */\nexport interface ClaudeStreamMessage {\n type: string;\n [key: string]: unknown;\n}\n\n/**\n * Cross-message correlation state a single {@link ClaudeSession} (in\n * `../claude-adapter.ts`) owns for its whole lifetime.\n *\n * Unlike pi's `tool_execution_start`/`tool_execution_end` frames (which\n * both carry `toolName` directly, so `../pi/events.ts` can stay a pure,\n * stateless function), claude's Messages-API-shaped transcript splits a\n * tool call across two DIFFERENT frame types: the `assistant` frame's\n * `tool_use` content block carries `{id, name, input}`, but the later\n * `user` frame's `tool_result` content block carries only `{tool_use_id,\n * content, is_error}` — no tool name at all. Since this protocol's own\n * `AgentEvent` schema requires `tool_result.tool: string`, this mapper has\n * no choice but to remember `tool_use_id -> name` from the `tool_use` block\n * and look it up when the matching `tool_result` arrives later. This is a\n * genuine, disclosed structural difference from pi, not an arbitrary\n * design choice — see the M2-a report for the full reasoning.\n */\nexport interface ToolUseCorrelation {\n readonly toolNameByUseId: Map<string, string>;\n}\n\nexport function createToolUseCorrelation(): ToolUseCorrelation {\n return { toolNameByUseId: new Map() };\n}\n\nexport interface MapClaudeMessageOptions {\n /** `ctx.workspaceDir` for the task — used only to compute a workspace-relative `name` for a possible `artifact` AgentEvent (see `tryBuildArtifactEvent`'s doc comment). */\n workspaceDir: string;\n}\n\nexport interface MapClaudeMessageResult {\n events: AgentEvent[];\n /**\n * Set when this exact frame (or, for `assistant`/`user` frames, one\n * content block inside it) was genuinely unrecognized — a frame/subtype/\n * block-type this adapter has never been told to expect — as opposed to\n * routine bookkeeping this mapper deliberately ignores (see\n * `ROUTINE_CLAUDE_SYSTEM_SUBTYPES` and the `thinking`/`redacted_thinking`\n * cases below). The caller (`ClaudeSession`'s event iterator) is\n * responsible for actually recording it (via\n * `ClaudeProcessClient.recordUnmappedFrame`) — mirrors pi's\n * `ROUTINE_PI_EVENT_TYPES` check living in `PiSession`'s iterator rather\n * than inside `mapPiMessageToAgentEvent` itself. At most one label per\n * call even if a frame has several unmapped things in it — sufficient\n * for the \"did this regress\" self-diagnosing purpose this exists for,\n * without needing a list.\n */\n unmappedLabel?: string;\n}\n\n/**\n * `system` frame subtypes empirically observed on real stream-json output\n * from the installed claude 2.1.212 binary that carry no `AgentEvent`\n * equivalent — routine bookkeeping, deliberately ignored:\n *\n * - `init`: session/turn start. Carries `session_id`, `tools`, `cwd`,\n * `permissionMode`, etc. `session_id` specifically is NOT read here —\n * `ClaudeProcessClient.waitForInit()` (`process-client.ts`) captures it\n * directly off the raw line as part of this adapter's own\n * `start()`/sessionRef bookkeeping, since it's needed before any\n * `AgentEvent` mapping is even relevant.\n * - `hook_started` / `hook_response`: fired when the user's own Claude Code\n * installation has configured lifecycle hooks (e.g. `SessionStart`) —\n * machine/config-specific, not part of this protocol's surface at all.\n * - `thinking_tokens`: periodic token-count-estimate bookkeeping emitted\n * while the model is reasoning; no user-visible content.\n *\n * A `system` frame whose `subtype` is NOT in this set is treated as\n * genuinely unmapped (see `mapClaudeMessageToAgentEvents`'s `system` case)\n * rather than silently folded into \"system frames are always routine\" —\n * this is deliberately finer-grained than lumping the whole `system` type\n * together, so a future/unobserved subtype (e.g. something compaction- or\n * budget-related) shows up as a one-time warning instead of disappearing\n * the way the pi adapter's own root-cause hang (a real settle event with no\n * mapping, silently swallowed) did before that bug was found.\n */\nexport const ROUTINE_CLAUDE_SYSTEM_SUBTYPES: ReadonlySet<string> = new Set([\n 'init',\n 'hook_started',\n 'hook_response',\n 'thinking_tokens',\n]);\n\n/** Claude tool names whose successful `tool_result` may correspond to a file written into the task workspace — see `tryBuildArtifactEvent`. `Write` is fully empirically confirmed (real `tool_use_result.filePath`/`.type:\"create\"` shape captured live); `Edit`/`NotebookEdit` are included by the same reasoning/convention as `permission-mapping.ts`'s `Glob`/`Grep` note — not independently re-verified frame-by-frame here. */\nconst FILE_WRITING_TOOLS: ReadonlySet<string> = new Set(['Write', 'Edit', 'NotebookEdit']);\n\nconst EXTENSION_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.json': 'application/json',\n '.js': 'text/javascript',\n '.mjs': 'text/javascript',\n '.cjs': 'text/javascript',\n '.ts': 'text/plain',\n '.tsx': 'text/plain',\n '.jsx': 'text/plain',\n '.html': 'text/html',\n '.css': 'text/css',\n '.csv': 'text/csv',\n '.py': 'text/x-python',\n '.yaml': 'application/yaml',\n '.yml': 'application/yaml',\n};\n\n/** Best-effort, extension-based content-type guess — deliberately not a full MIME sniffer (out of scope; `AgentEventSchema.artifact.contentType` just needs a non-empty string). */\nfunction guessContentType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase();\n return EXTENSION_CONTENT_TYPES[ext] ?? 'application/octet-stream';\n}\n\n/**\n * `assistant` frames carry ONE new Messages-API content block per frame in\n * every live capture this adapter's empirical probes observed (never\n * multiple) — this still iterates the whole `content` array defensively\n * rather than assuming array length 1, since nothing in claude's own\n * documented wire contract guarantees that stays true.\n */\nfunction mapAssistant(msg: ClaudeStreamMessage, correlation: ToolUseCorrelation): MapClaudeMessageResult {\n const message = msg.message as { content?: unknown } | undefined;\n const content = message?.content;\n if (!Array.isArray(content)) return { events: [] };\n\n const events: AgentEvent[] = [];\n let unmappedLabel: string | undefined;\n\n for (const raw of content) {\n const block = raw as { type?: unknown; [key: string]: unknown };\n switch (block.type) {\n case 'text':\n if (typeof block.text === 'string') {\n events.push({ type: 'progress', text: block.text });\n }\n break;\n\n case 'tool_use':\n if (typeof block.id === 'string' && typeof block.name === 'string') {\n correlation.toolNameByUseId.set(block.id, block.name);\n events.push({ type: 'tool_use', tool: block.name, input: block.input });\n }\n break;\n\n // Deliberately NOT mapped to `progress` — mirrors pi's own choice to\n // ignore `thinking_delta` sub-events (see `../pi/events.ts`). Two\n // independent reasons: (1) the daemon's `task-runner.ts` folds every\n // `progress` event's text into `task.complete.summary` verbatim —\n // surfacing raw model reasoning there would leak internal\n // chain-of-thought to whatever SaaS embeds this SDK; (2) on several\n // current-generation models this content is empty by design anyway\n // (`display: \"omitted\"` is the default — see the claude-api skill's\n // \"Thinking & Effort\" reference) even though it was NOT empty in this\n // task's own live captures against `claude-haiku-4-5`. `redacted_thinking`\n // is a documented Anthropic Messages API block type never observed\n // live here; treated identically for the same reasoning.\n case 'thinking':\n case 'redacted_thinking':\n break;\n\n default:\n unmappedLabel = unmappedLabel ?? `assistant-block:${String(block.type)}`;\n }\n }\n\n return { events, unmappedLabel };\n}\n\n/**\n * `user` frames in stream-json output are NOT a human sending input — they\n * are claude echoing a `tool_result` back into the transcript (the same\n * \"user\" role the underlying Messages API uses for tool results). Two\n * concrete verbatim shapes captured live:\n *\n * Denied (no permission granted, headless — see `../claude-adapter.ts`'s\n * doc comment for the full approval-model finding):\n * ```json\n * {\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\n * \"content\":\"Claude requested permissions to write to <path>, but you\n * haven't granted it yet.\",\"is_error\":true,\"tool_use_id\":\"toolu_...\"}]},\n * \"tool_use_result\":\"Error: Claude requested permissions to write to\n * <path>, but you haven't granted it yet.\"}\n * ```\n *\n * Successful Write (the shape `tryBuildArtifactEvent` reads):\n * ```json\n * {\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_...\",\n * \"type\":\"tool_result\",\"content\":\"File created successfully at: <path> ...\"}]},\n * \"tool_use_result\":{\"type\":\"create\",\"filePath\":\"<absolute path>\",\n * \"content\":\"...\",\"structuredPatch\":[],\"originalFile\":null,\"userModified\":false}}\n * ```\n *\n * `content` on the `tool_result` block was only ever observed as a plain\n * string in this task's captures (never the content-block-array shape the\n * general Messages API allows) — `output.content` below is passed through\n * as `unknown` regardless, so a future array shape still round-trips\n * correctly without a mapping change.\n */\nfunction mapUser(msg: ClaudeStreamMessage, correlation: ToolUseCorrelation, options: MapClaudeMessageOptions): MapClaudeMessageResult {\n const message = msg.message as { content?: unknown } | undefined;\n const content = message?.content;\n if (!Array.isArray(content)) return { events: [] };\n\n const events: AgentEvent[] = [];\n let unmappedLabel: string | undefined;\n\n for (const raw of content) {\n const block = raw as { type?: unknown; tool_use_id?: unknown; content?: unknown; is_error?: unknown };\n if (block.type !== 'tool_result') {\n unmappedLabel = unmappedLabel ?? `user-block:${String(block.type)}`;\n continue;\n }\n\n const toolUseId = typeof block.tool_use_id === 'string' ? block.tool_use_id : undefined;\n // Falls back to 'unknown' rather than dropping the event: the\n // AgentEventSchema requires a non-empty `tool` string, and a\n // correlation miss here would mean this adapter's own\n // tool_use<->tool_result bookkeeping has a bug — worth surfacing to the\n // caller as a (still-delivered) event with an honest sentinel name,\n // never silently swallowing real tool-result data.\n const tool = (toolUseId && correlation.toolNameByUseId.get(toolUseId)) || 'unknown';\n const isError = block.is_error === true;\n events.push({ type: 'tool_result', tool, output: { content: block.content, isError } });\n\n if (!isError && FILE_WRITING_TOOLS.has(tool)) {\n const artifact = tryBuildArtifactEvent(msg, options.workspaceDir);\n if (artifact) events.push(artifact);\n }\n }\n\n return { events, unmappedLabel };\n}\n\n/**\n * Real claude has no dedicated \"artifact\" wire message the way pi's fixture\n * fakes one (see `../pi/events.ts`'s doc comment on that case) — but for a\n * successful `Write` (confirmed shape above), the SAME `user` frame's\n * top-level `tool_use_result.filePath` sibling field gives this adapter\n * something pi never had: a real, live-observed, absolute path to the file\n * claude just wrote. This turns it into a workspace-relative `name` for the\n * protocol's `artifact` `AgentEvent`.\n *\n * A written file OUTSIDE `workspaceDir` never produces an artifact event —\n * this is not just defensive coding, it is the concrete fix for a real,\n * confirmed case: `--permission-mode plan` writes its own plan document to\n * `~/.claude/plans/<slug>.md`, the user's actual home directory, regardless\n * of cwd (see `permission-mapping.ts`'s doc comment on that finding). That\n * path always resolves outside `workspaceDir` and must never be reported to\n * the daemon as this task's artifact.\n */\nfunction tryBuildArtifactEvent(msg: ClaudeStreamMessage, workspaceDir: string): AgentEvent | undefined {\n const toolUseResult = msg.tool_use_result as { filePath?: unknown } | undefined;\n const filePath = toolUseResult && typeof toolUseResult.filePath === 'string' ? toolUseResult.filePath : undefined;\n if (!filePath) return undefined;\n\n // `workspaceDir` (`ctx.workspaceDir`) and the absolute `filePath` claude\n // reports can each be expressed through a different symlink alias of the\n // same real location — hit empirically on macOS, where `os.tmpdir()`\n // itself is a symlink (`/var/folders/... -> /private/var/folders/...`)\n // and a spawned child process's own `process.cwd()` reports the\n // REALPATH-resolved form regardless of which alias `cwd` was set to when\n // spawning it. Comparing the raw strings directly misclassifies a file\n // that genuinely IS inside the workspace as outside it purely because of\n // which alias each side happened to use — confirmed live: this silently\n // dropped every artifact event for a workspace created under this\n // machine's own `os.tmpdir()` before this fix. Resolve both through\n // `realpath` before comparing — mirrors `task-runner.ts`'s own\n // `openArtifact()`, which resolves the exact same alias-mismatch class\n // for its downstream containment check.\n //\n // Realpath is applied to the *directory*, not the file itself: the file\n // is expected to exist by the time this runs against real claude output\n // (the tool_result frame only arrives after the write actually\n // happened), but resolving the directory rather than the leaf path keeps\n // this correct even when it doesn't (a stale/removed artifact, or a pure\n // unit test constructing a `tool_use_result` without writing real bytes)\n // — falls back to the raw directory on a realpath failure (e.g. it too\n // doesn't exist), matching `openArtifact`'s own defensive fallback.\n const realWorkspaceDir = tryRealpath(workspaceDir) ?? workspaceDir;\n const fileDir = path.dirname(filePath);\n const realFileDir = tryRealpath(fileDir) ?? fileDir;\n const realFilePath = path.join(realFileDir, path.basename(filePath));\n\n const relative = path.relative(realWorkspaceDir, realFilePath);\n if (relative === '' || relative.startsWith('..') || path.isAbsolute(relative)) {\n return undefined;\n }\n\n return { type: 'artifact', name: relative, contentType: guessContentType(filePath) };\n}\n\nfunction tryRealpath(candidate: string): string | undefined {\n try {\n return realpathSync(candidate);\n } catch {\n return undefined;\n }\n}\n\n/**\n * `result` is claude's real \"whole run settled\" signal (the direct\n * counterpart to pi's `agent_settled` — see `../pi/events.ts`'s doc comment on\n * why that distinction mattered there). Confirmed live: it is always the\n * LAST frame of a turn, whether the turn succeeded (`is_error:false`,\n * `subtype:\"success\"`) or failed before ever reaching the model (e.g. an\n * unresolvable `--resume` target: `subtype:\"error_during_execution\"`,\n * `is_error:true`, `errors:[\"No conversation found with session ID: ...\"]`,\n * process exit code 1 — but STILL a clean, parseable stream-json frame,\n * unlike pi's equivalent bad-invocation case which produces no structured\n * output at all and forces the adapter to fall back to a raw stderr tail).\n * `is_error` (not `subtype`) is the field this switches on, since it is a\n * plain boolean rather than a string this adapter would otherwise need to\n * pattern-match against every observed/unobserved subtype spelling.\n *\n * `usage` (pre-freeze protocol addition — see `extractClaudeUsageEvent`'s\n * doc comment) is extracted on BOTH branches, not just success: a real\n * mid-generation API error could still carry partial usage, and an empty/\n * absent `usage` (the confirmed shape on the pre-flight \"no conversation\n * found\" failure — no model call was ever made) just yields no event, same\n * as the success path. Ordering is load-bearing: `usage`, when present, is\n * placed BEFORE `turn_end`/`error` in the returned array — `ClaudeSession`'s\n * event iterator (`../claude-adapter.ts`) buffers this array and drains it\n * in order, and `task-runner.ts`'s `pump()` returns the instant it sees\n * `turn_end`, so anything queued after it would never be read. `error` does\n * not end the read loop the same way, so the ordering there is for\n * consistency rather than a delivery requirement.\n */\nfunction mapResult(msg: ClaudeStreamMessage): MapClaudeMessageResult {\n const usageEvent = extractClaudeUsageEvent(msg.usage);\n // Cross-model review finding: success must require `is_error === false`\n // EXPLICITLY — the previous `!== true` check treated a MISSING or\n // non-boolean `is_error` (a malformed/future `result` frame) as success\n // too, which would have reported task.complete for a turn that never\n // actually said it succeeded. `result` is the wire's own terminal signal\n // (see this function's own doc comment above) — never inferred.\n if (msg.is_error === false) {\n const events: AgentEvent[] = usageEvent ? [usageEvent, { type: 'turn_end' }] : [{ type: 'turn_end' }];\n return { events };\n }\n const errors = Array.isArray(msg.errors) ? msg.errors.filter((e): e is string => typeof e === 'string') : [];\n // Shared between both failure branches below: the SAME \"errors array wins,\n // result string is the fallback\" extraction `is_error === true` has always\n // used. `undefined` means the frame carried neither.\n const diagnostic =\n errors.length > 0 ? errors.join('; ') : typeof msg.result === 'string' && msg.result.length > 0 ? msg.result : undefined;\n const message =\n msg.is_error === true\n ? (diagnostic ?? 'claude reported an error result')\n : // M4 hardening: a missing/invalid `is_error` is already fail-closed\n // (treated as failure, never inferred as success — see above), but\n // this branch used to report ONLY the generic fallback string, even\n // when the frame actually carried a perfectly usable `result`/\n // `errors` payload (real diagnostic content is only read above when\n // `is_error === true` EXACTLY, so a malformed-but-otherwise-populated\n // frame silently lost it). Appending `diagnostic` here (when\n // present) preserves the fail-closed verdict — this is still always\n // an `error` AgentEvent — while no longer discarding whatever the\n // frame actually said. Truncated defensively: this is an\n // unexpected/malformed frame shape, not the normal error path, so\n // the content could in principle be arbitrarily large.\n diagnostic\n ? `claude result frame had a missing/invalid is_error flag (got ${JSON.stringify(msg.is_error)}) — treating as failure, fail-closed; diagnostic content on the frame: ${truncateResultDiagnostic(diagnostic)}`\n : `claude result frame had a missing/invalid is_error flag (got ${JSON.stringify(msg.is_error)}) — treating as failure, fail-closed`;\n const events: AgentEvent[] = usageEvent ? [usageEvent, { type: 'error', message }] : [{ type: 'error', message }];\n return { events };\n}\n\n/** Sane upper bound on how much of a malformed `result` frame's own diagnostic content (`errors`/`result`) gets folded into the fail-closed message above — no existing truncation helper in this file to reuse (the codex adapter's equivalent, `codex-adapter.ts`'s `JSON.stringify(evt).slice(0, 200)`, lives in a different module entirely), so this is its own small, local bound rather than a shared one. */\nconst RESULT_DIAGNOSTIC_MAX_CHARS = 2000;\n\nfunction truncateResultDiagnostic(text: string): string {\n return text.length > RESULT_DIAGNOSTIC_MAX_CHARS ? `${text.slice(0, RESULT_DIAGNOSTIC_MAX_CHARS)}… [truncated]` : text;\n}\n\n/**\n * Maps the `result` frame's `usage` object to a `usage` `AgentEvent`, or\n * `undefined` when nothing usable is present. Field names and shape\n * confirmed via a live minimal probe against the installed claude 2.1.212\n * binary (same version this adapter's other captures were made against —\n * see `claude-adapter.ts`'s class doc comment): a real `result.usage` looks\n * like `{\"input_tokens\":2,\"cache_creation_input_tokens\":35649,\n * \"cache_read_input_tokens\":0,\"output_tokens\":24,\"server_tool_use\":{...},\n * \"service_tier\":\"standard\",...}` — the standard Anthropic Messages API\n * usage shape, carried through verbatim by the Claude Code CLI.\n *\n * Mapping choices:\n * - `inputTokens` <- `input_tokens` verbatim.\n * - `cachedInputTokens` <- `cache_read_input_tokens` (tokens actually SERVED\n * from a prior cache — the same \"reused, cheaper\" semantic as codex's\n * `cached_input_tokens`). Deliberately NOT `cache_creation_input_tokens`\n * (the cost of WRITING a new cache entry — a distinct, more-expensive-not-\n * cheaper concept the wire schema has no separate slot for; dropped\n * rather than conflated).\n * - `outputTokens` <- `output_tokens` verbatim.\n * - `reasoningTokens` / `totalTokens`: never populated for claude — neither\n * a separate reasoning-token count nor a combined total appears anywhere\n * in real claude output (thinking-block content is folded into\n * `output_tokens`, not counted separately), so these stay absent rather\n * than fabricated.\n */\nfunction extractClaudeUsageEvent(rawUsage: unknown): AgentEvent | undefined {\n if (!rawUsage || typeof rawUsage !== 'object') return undefined;\n const usage = rawUsage as Record<string, unknown>;\n const inputTokens = toNonNegativeInt(usage.input_tokens);\n const cachedInputTokens = toNonNegativeInt(usage.cache_read_input_tokens);\n const outputTokens = toNonNegativeInt(usage.output_tokens);\n if (inputTokens === undefined && cachedInputTokens === undefined && outputTokens === undefined) {\n return undefined;\n }\n const event: Extract<AgentEvent, { type: 'usage' }> = { type: 'usage' };\n if (inputTokens !== undefined) event.inputTokens = inputTokens;\n if (cachedInputTokens !== undefined) event.cachedInputTokens = cachedInputTokens;\n if (outputTokens !== undefined) event.outputTokens = outputTokens;\n return event;\n}\n\n/** Matches `AgentEventSchema`'s `usage` fields (`z.number().int().nonnegative().optional()`) — anything else (missing, a string, a float, negative) is treated as not-reported rather than coerced. */\nfunction toNonNegativeInt(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 ? value : undefined;\n}\n\n/**\n * Map one raw claude stream-json line to zero or more normalized\n * `AgentEvent`s (a single `user` frame can produce two: `tool_result` plus\n * a derived `artifact`). See the per-`type` mapping functions above for the\n * concrete, empirically-captured shapes each branch handles.\n */\nexport function mapClaudeMessageToAgentEvents(\n msg: ClaudeStreamMessage,\n correlation: ToolUseCorrelation,\n options: MapClaudeMessageOptions,\n): MapClaudeMessageResult {\n switch (msg.type) {\n case 'assistant':\n return mapAssistant(msg, correlation);\n case 'user':\n return mapUser(msg, correlation, options);\n case 'result':\n return mapResult(msg);\n case 'system': {\n const subtype = typeof msg.subtype === 'string' ? msg.subtype : undefined;\n if (subtype && ROUTINE_CLAUDE_SYSTEM_SUBTYPES.has(subtype)) return { events: [] };\n return { events: [], unmappedLabel: `system:${subtype ?? 'unknown'}` };\n }\n // Per-turn rate-limit-window bookkeeping, unconditionally emitted\n // alongside every result — no `AgentEvent` equivalent, routine.\n case 'rate_limit_event':\n return { events: [] };\n default:\n return { events: [], unmappedLabel: `top-level:${String(msg.type)}` };\n }\n}\n","import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport { AsyncQueue } from '../../util/async-queue';\nimport type { ClaudeStreamMessage } from './events';\n\nexport type SpawnFn = typeof spawn;\n\nexport interface ClaudeProcessClientOptions {\n command: string;\n args: string[];\n cwd: string;\n env: NodeJS.ProcessEnv;\n spawnFn?: SpawnFn;\n}\n\n/** Bound on retained stderr lines, mirroring pi's identical constant/rationale in `../pi/rpc-client.ts`. */\nconst STDERR_RING_CAPACITY = 20;\n\n/**\n * NDJSON process transport for `claude -p --input-format stream-json\n * --output-format stream-json`.\n *\n * Structurally simpler than pi's `PiRpcClient` in one real way, and\n * different (not simpler) in another:\n *\n * - No request/response correlation. pi's RPC mode replies to each command\n * with a `{type:\"response\", id, success, ...}` — claude's stream-json has\n * no such acknowledgement at all; writing a `{\"type\":\"user\",...}` line\n * just starts (or queues) a turn, and the ONLY confirmation is the\n * ordinary event stream itself (starting with a `system/init` frame).\n * There is therefore no `pending` id->resolver map here.\n * - `waitForInit()` exists specifically to compensate for that missing\n * ack: pi's `start()` fails fast because a bad flag/auth error rejects\n * the pending `send()` promise for the first command. Claude's own\n * `AsyncQueue.end()` (used for the `events` stream) is a CLEAN,\n * non-throwing end — a process that crashes before ever emitting a line\n * would otherwise look, from the async-iteration protocol alone, exactly\n * like a session that legitimately produced zero events, silently\n * swallowing the real failure. `waitForInit()` is a dedicated promise\n * that resolves with the real `session_id` once claude's own\n * `system/init` frame arrives, or rejects with the same enriched\n * exit-error `events` would otherwise swallow — this is what lets\n * `ClaudeAdapter.start()` fail loudly and immediately for a bad\n * `--resume` target etc., mirroring pi's own fail-fast contract with a\n * mechanism suited to claude's ack-less protocol instead of copying pi's\n * request/response one verbatim.\n *\n * Framing, stderr-ring, and unmapped-frame-tally-in-exit-error all mirror\n * `../pi/rpc-client.ts`'s already-proven design directly (LF-delimited\n * JSONL, `node:readline` avoided for the same U+2028/U+2029 reason pi's\n * doc comment explains, `close` not `exit` for the same complete-stderr\n * guarantee) — these are generic, sound patterns, not pi-specific logic,\n * so re-implementing them independently here (rather than importing from\n * `../pi/`) keeps this adapter fully self-contained, matching this repo's\n * existing per-adapter isolation.\n */\nexport class ClaudeProcessClient {\n private readonly child: ChildProcessWithoutNullStreams;\n private buffer = '';\n private readonly eventQueue = new AsyncQueue<ClaudeStreamMessage>();\n private closed = false;\n private exitError: Error | undefined;\n private readonly stderrRing: string[] = [];\n private readonly unmappedFrameCounts = new Map<string, number>();\n\n private sessionId: string | undefined;\n private initWaiter: { resolve: (sessionId: string) => void; reject: (err: Error) => void } | undefined;\n\n constructor(options: ClaudeProcessClientOptions) {\n const spawnFn = options.spawnFn ?? spawn;\n this.child = spawnFn(options.command, options.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: ['pipe', 'pipe', 'pipe'],\n }) as ChildProcessWithoutNullStreams;\n\n this.child.stdout.setEncoding('utf8');\n this.child.stdout.on('data', (chunk: string) => this.onData(chunk));\n\n this.child.stderr.setEncoding('utf8');\n this.child.stderr.on('data', (chunk: string) => this.onStderr(chunk));\n\n // `close` (not `exit`) — see pi's rpc-client.ts doc comment for why\n // this matters: it's Node's guarantee every stdout/stderr byte the\n // process wrote has already been delivered, so a bad-flag/auth-failure\n // exit error always carries the complete stderr tail.\n this.child.on('close', (code, signal) => {\n this.onClosed(this.buildExitError(code, signal));\n });\n this.child.on('error', (err) => {\n this.onClosed(err);\n });\n }\n\n /**\n * Write a new user turn onto stdin (`--input-format stream-json`'s wire\n * shape: `{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"type\":\n * \"text\",\"text\":...}]}}`). Used identically for the very first turn\n * (`ClaudeAdapter.start()`) and any later same-session turn\n * (`ClaudeSession.followUp()`) — empirically confirmed live that claude\n * keeps a `--input-format stream-json` process alive across multiple\n * sequential turns on ONE persistent process/session (same `session_id`\n * reported on each turn's own `system/init` and `result` frames), only\n * exiting when stdin is closed or the process is killed. This is the\n * mechanism `followUp()` relies on instead of spawning a fresh\n * `--resume`'d process per follow-up.\n */\n writeUserMessage(text: string): void {\n if (this.closed) {\n throw this.exitError ?? new Error('claude process is closed');\n }\n const line = `${JSON.stringify({ type: 'user', message: { role: 'user', content: [{ type: 'text', text }] } })}\\n`;\n this.child.stdin.write(line, (err) => {\n if (err) {\n console.error(`[byok/claude-adapter] failed to write to claude stdin: ${err.message}`);\n }\n });\n }\n\n /**\n * Resolves with claude's own `session_id` once its `system/init` frame\n * arrives (see this class's doc comment for why this exists at all).\n * Idempotent: once resolved, further calls resolve immediately with the\n * same id; if the process already closed before init ever arrived,\n * every call rejects with that same exit error.\n */\n waitForInit(): Promise<string> {\n if (this.sessionId !== undefined) return Promise.resolve(this.sessionId);\n if (this.closed) return Promise.reject(this.exitError ?? new Error('claude process is closed'));\n return new Promise((resolve, reject) => {\n this.initWaiter = { resolve, reject };\n });\n }\n\n /** Every parsed stream-json line — `system/init` is consumed internally (see `waitForInit`) but is also forwarded here like any other frame, so routine-frame accounting in `ClaudeSession`'s mapper stays uniform. */\n get events(): AsyncIterable<ClaudeStreamMessage> {\n return this.eventQueue;\n }\n\n /**\n * Record a claude stream-json frame/subtype/content-block label that\n * `ClaudeSession`'s event iterator (`../claude-adapter.ts`) decided has\n * no `AgentEvent` mapping and isn't routine bookkeeping (see\n * `events.ts`'s `MapClaudeMessageResult.unmappedLabel` doc comment) —\n * i.e. genuinely unexpected traffic. Mirrors pi's\n * `PiRpcClient.recordUnmappedFrame` exactly: logs once per distinct\n * label, folds the running tally into a later exit error for a\n * post-mortem without separate log scraping.\n */\n recordUnmappedFrame(label: string): void {\n const next = (this.unmappedFrameCounts.get(label) ?? 0) + 1;\n this.unmappedFrameCounts.set(label, next);\n if (next === 1) {\n console.warn(\n `[byok/claude-adapter] claude emitted a frame with no AgentEvent mapping: \"${label}\" (further occurrences of this label won't be logged individually)`,\n );\n }\n }\n\n /** Best-effort teardown. SIGTERM on POSIX; `taskkill /T /F` on Windows to also reap child processes claude itself spawned (e.g. Bash) — mirrors pi's cross-platform `kill()` exactly. Empirically confirmed on this (POSIX) machine: a running claude process exits cleanly within ~1s of SIGTERM (observed exit code 143 = 128+SIGTERM, i.e. claude catches and handles the signal itself rather than needing a harder kill). */\n kill(): void {\n if (this.closed) return;\n const pid = this.child.pid;\n if (process.platform === 'win32' && pid !== undefined) {\n spawnSync('taskkill', ['/pid', String(pid), '/T', '/F']);\n } else {\n this.child.kill('SIGTERM');\n }\n }\n\n private onData(chunk: string): void {\n this.buffer += chunk;\n let newlineIndex = this.buffer.indexOf('\\n');\n while (newlineIndex !== -1) {\n let line = this.buffer.slice(0, newlineIndex);\n this.buffer = this.buffer.slice(newlineIndex + 1);\n if (line.endsWith('\\r')) line = line.slice(0, -1);\n if (line.length > 0) this.onLine(line);\n newlineIndex = this.buffer.indexOf('\\n');\n }\n }\n\n private onLine(line: string): void {\n let msg: ClaudeStreamMessage;\n try {\n msg = JSON.parse(line) as ClaudeStreamMessage;\n } catch {\n return; // a stray non-JSON line is not this client's concern\n }\n\n if (\n this.sessionId === undefined &&\n msg.type === 'system' &&\n msg.subtype === 'init' &&\n typeof msg.session_id === 'string' &&\n msg.session_id.length > 0\n ) {\n this.sessionId = msg.session_id;\n this.initWaiter?.resolve(this.sessionId);\n this.initWaiter = undefined;\n }\n\n this.eventQueue.push(msg);\n }\n\n private onStderr(chunk: string): void {\n for (const rawLine of chunk.split('\\n')) {\n const line = rawLine.trim();\n if (line.length === 0) continue;\n this.stderrRing.push(line);\n if (this.stderrRing.length > STDERR_RING_CAPACITY) this.stderrRing.shift();\n }\n }\n\n /** Mirrors pi's `buildExitError` exactly — stderr tail + unmapped-frame tally folded into one self-diagnosing message. */\n private buildExitError(code: number | null, signal: NodeJS.Signals | null): Error {\n const parts = [`claude process exited (code=${code}, signal=${signal})`];\n if (this.stderrRing.length > 0) {\n parts.push(`stderr: ${this.stderrRing.join(' | ')}`);\n }\n if (this.unmappedFrameCounts.size > 0) {\n const summary = [...this.unmappedFrameCounts.entries()].map(([label, count]) => `${label}×${count}`).join(', ');\n parts.push(`unmapped frame labels seen: ${summary}`);\n }\n return new Error(parts.join('; '));\n }\n\n private onClosed(err: Error): void {\n if (this.closed) return;\n this.closed = true;\n this.exitError = err;\n this.initWaiter?.reject(err);\n this.initWaiter = undefined;\n this.eventQueue.end();\n }\n}\n","import { createInterface } from 'node:readline';\n\n/**\n * M4 Phase 3: the testable core of `byok-approval-mcp` (`byok-approval-mcp.ts`\n * is the thin stdio-wiring entry point — mirrors this repo's existing\n * `bin/commands/*.ts` split: real logic lives in a plain module, the bin\n * script itself is glue no test ever imports directly).\n *\n * `byok-approval-mcp` is the MCP stdio server `claude`'s own\n * `--permission-prompt-tool` spawns AS ITS OWN CHILD PROCESS when the claude\n * adapter runs a task under `PermissionPolicy.mode: 'confirm'` (see\n * `../adapters/claude/permission-mapping.ts`'s `confirm`-mode doc comment).\n * It implements just enough of the MCP stdio transport (JSON-RPC 2.0,\n * newline-delimited, per the spec) to expose ONE tool — empirically\n * confirmed end-to-end against the real installed claude 2.1.216 binary\n * (M4 Phase 3 STEP 0): `initialize` -> `notifications/initialized` ->\n * `tools/list` -> `tools/call`, with the tool's arguments shaped exactly\n * `{tool_name, input, tool_use_id}` (claude's own real wire shape,\n * live-captured) and its expected response shaped\n * `{content:[{type:'text', text: JSON.stringify({behavior:'allow',\n * updatedInput} | {behavior:'deny', message})}]}` — the SAME shape the\n * Claude Agent SDK's in-process `canUseTool` callback returns (see\n * platform.claude.com/docs/en/agent-sdk/user-input), just crossing a\n * process boundary via MCP instead of an in-process function call.\n */\n\nexport const APPROVAL_TOOL_NAME = 'approval_prompt';\n\n/** Bound on how much of a tool call's `input` gets folded into the wire `task.await_approval.summary` — mirrors `events.ts`'s `RESULT_DIAGNOSTIC_MAX_CHARS`/`truncateResultDiagnostic` convention: a human-facing summary, not a full audit record. */\nexport const APPROVAL_SUMMARY_MAX_CHARS = 500;\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/** `${toolName}: ${input}`, bounded — the human-readable description carried into `task.await_approval.summary` and (via the daemon/CLI's own rendering) whatever a real approver actually reads before deciding. */\nexport function summarizeToolCall(toolName: string, input: unknown): string {\n let inputStr: string;\n try {\n inputStr = JSON.stringify(input);\n } catch (err) {\n inputStr = `<unserializable input: ${errorMessage(err)}>`;\n }\n const bounded =\n inputStr.length > APPROVAL_SUMMARY_MAX_CHARS ? `${inputStr.slice(0, APPROVAL_SUMMARY_MAX_CHARS)}… [truncated]` : inputStr;\n return `${toolName}: ${bounded}`;\n}\n\nexport interface ApprovalOutcome {\n approved: boolean;\n reason?: string;\n}\n\n/** What `byok-approval-mcp.ts` (the real entry point) injects — the one real dependency this module has on the outside world. */\nexport interface ApprovalMcpDeps {\n /** Requests a decision from the daemon this task is running on. Any rejection/throw here is treated as fail-closed (deny) by `handleMcpRequest` — never surfaced to claude as a raw protocol error. */\n requestApproval(taskId: string, summary: string): Promise<ApprovalOutcome>;\n}\n\ninterface JsonRpcRequestLike {\n jsonrpc?: unknown;\n id?: unknown;\n method?: unknown;\n params?: unknown;\n}\n\n/**\n * Handles exactly one already-parsed JSON-RPC request object and returns the\n * exact response object to write back (`undefined` for a notification that\n * expects no reply, e.g. `notifications/initialized`) — no stdio/process\n * concerns at all, so tests call this directly with a stub {@link\n * ApprovalMcpDeps} instead of spawning a real process or a real control\n * socket. `serveApprovalMcpOverStdio` below is the only caller in production.\n */\nexport async function handleMcpRequest(\n req: JsonRpcRequestLike,\n deps: ApprovalMcpDeps,\n taskId: string,\n): Promise<Record<string, unknown> | undefined> {\n const id = req.id;\n\n if (req.method === 'initialize') {\n const params = (req.params ?? {}) as { protocolVersion?: unknown };\n return {\n jsonrpc: '2.0',\n id,\n result: {\n protocolVersion: typeof params.protocolVersion === 'string' ? params.protocolVersion : '2024-11-05',\n capabilities: { tools: {} },\n serverInfo: { name: 'byok-approval-mcp', version: '0.0.1' },\n },\n };\n }\n\n if (req.method === 'notifications/initialized') {\n return undefined; // notification — no response\n }\n\n if (req.method === 'tools/list') {\n return {\n jsonrpc: '2.0',\n id,\n result: {\n tools: [\n {\n name: APPROVAL_TOOL_NAME,\n description:\n 'Requests operator approval for a pending tool call. Blocks until a human (or this device\\'s daemon) decides, or the configured timeout elapses (fail-closed deny on timeout).',\n inputSchema: {\n type: 'object',\n properties: {\n tool_name: { type: 'string' },\n input: { type: 'object' },\n },\n },\n },\n ],\n },\n };\n }\n\n if (req.method === 'tools/call') {\n const params = (req.params ?? {}) as { name?: unknown; arguments?: unknown };\n if (params.name !== APPROVAL_TOOL_NAME) {\n return { jsonrpc: '2.0', id, error: { code: -32602, message: `unknown tool \"${String(params.name)}\"` } };\n }\n const args = (params.arguments ?? {}) as { tool_name?: unknown; input?: unknown };\n const toolName = typeof args.tool_name === 'string' ? args.tool_name : 'unknown tool';\n const input = args.input ?? {};\n const summary = summarizeToolCall(toolName, input);\n\n let outcome: ApprovalOutcome;\n try {\n outcome = await deps.requestApproval(taskId, summary);\n } catch (err) {\n // Fail-closed (task's own mandate): a daemon that's unreachable, a\n // control request that times out, or any other failure reaching the\n // approving device must never leave claude's own MCP call unanswered\n // — that risks claude abandoning the whole turn on its own (M4 Phase 3\n // STEP 0 found claude gives up on a permission-prompt-tool call that\n // never answers at all, ~1.5s in) rather than cleanly denying just\n // this one tool call and letting the conversation continue.\n outcome = { approved: false, reason: `could not reach the approving device: ${errorMessage(err)}` };\n }\n\n const payload = outcome.approved\n ? { behavior: 'allow' as const, updatedInput: input }\n : { behavior: 'deny' as const, message: outcome.reason ?? 'denied' };\n return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: JSON.stringify(payload) }] } };\n }\n\n if (id !== undefined) {\n return { jsonrpc: '2.0', id, error: { code: -32601, message: `unknown method: ${String(req.method)}` } };\n }\n return undefined;\n}\n\nexport interface ServeApprovalMcpOptions {\n taskId: string;\n deps: ApprovalMcpDeps;\n /** Defaults to `process.stdin`/`process.stdout` — overridable so tests can drive this over in-memory streams. */\n input?: NodeJS.ReadableStream;\n output?: NodeJS.WritableStream;\n}\n\n/**\n * Wires {@link handleMcpRequest} to real NDJSON stdio (the MCP stdio\n * transport: one JSON-RPC message per line, both directions) — the only\n * genuinely process-shaped piece of this module. Each line is handled\n * independently and asynchronously (never serialized against the others),\n * since claude's own parallel-tool-use can legitimately fire more than one\n * concurrent `tools/call` over the same connection.\n */\nexport function serveApprovalMcpOverStdio(opts: ServeApprovalMcpOptions): void {\n const input = opts.input ?? process.stdin;\n const output = opts.output ?? process.stdout;\n const rl = createInterface({ input, terminal: false });\n\n rl.on('line', (line) => {\n const trimmed = line.trim();\n if (!trimmed) return;\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n return; // a stray non-JSON line is not this server's concern\n }\n void handleMcpRequest(parsed as JsonRpcRequestLike, opts.deps, opts.taskId).then((response) => {\n if (response !== undefined) output.write(`${JSON.stringify(response)}\\n`);\n });\n });\n}\n","import { execFile } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\nimport type { AgentEvent, TaskOfferPayload } from '@byok-sdk/protocol';\nimport {\n PolicyUnsupportedError,\n SteerUnsupportedError,\n type ApprovalChannel,\n type RuntimeAdapter,\n type RuntimeCapabilities,\n type RuntimeDetectResult,\n type RuntimeEnvironmentRequirements,\n type Session,\n type TaskContext,\n} from '../../types';\nimport { resolveClaudeBin, type ResolvedBin } from './resolve-bin';\nimport { resolveApprovalMcpBin, type ResolvedApprovalMcpBin } from './resolve-approval-mcp-bin';\nimport { mapPermissionPolicyToClaudeArgs } from './permission-mapping';\nimport { createToolUseCorrelation, mapClaudeMessageToAgentEvents, type ToolUseCorrelation } from './events';\nimport { ClaudeProcessClient, type SpawnFn } from './process-client';\nimport { APPROVAL_TOOL_NAME } from '../../bin/approval-mcp-server';\n\n/** The MCP server NAME this adapter registers `byok-approval-mcp` under in the generated `--mcp-config` (arbitrary, local to this file) — combined with {@link APPROVAL_TOOL_NAME} (imported, single-sourced from `bin/approval-mcp-server.ts` so the two can never independently drift) to form the `mcp__<server>__<tool>` identifier `--permission-prompt-tool` expects. */\nconst APPROVAL_MCP_SERVER_NAME = 'byokapproval';\n\nconst execFileAsync = promisify(execFile);\n\n/** Applied to both `detect()` probe calls (`--version`, `auth status --json`) — mirrors codex-adapter.ts's identical `DETECT_TIMEOUT_MS`/rationale exactly (cross-model review finding: these two claude probes previously had NO timeout at all, unlike codex's, so a hung claude CLI could block daemon startup and every `detect()` call indefinitely). `detect()` runs on every allowlist-narrowed task offer, so a small ceiling is cheap insurance against either probe ever unexpectedly hanging. */\nconst DETECT_TIMEOUT_MS = 5000;\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/** Best-effort teardown of the temp `--mcp-config` directory `start()` creates for `confirm` mode (see its own doc comment) — never throws, since a cleanup failure must never mask the real error/result it's cleaning up after. No-ops when `dir` is `undefined` (every mode other than `confirm`). */\nasync function cleanupApprovalMcpConfigDir(dir: string | undefined): Promise<void> {\n if (!dir) return;\n await fs.rm(dir, { recursive: true, force: true }).catch(() => {});\n}\n\nexport interface ClaudeAdapterOptions {\n /** Override bin resolution — tests substitute the fake-claude fixture script. */\n resolveBin?: () => ResolvedBin;\n /** Override process spawning — tests substitute a fake spawn. */\n spawnFn?: SpawnFn;\n /** M4 Phase 3: override `byok-approval-mcp` bin resolution — tests substitute a fixture script instead of computing a real dist path. Mirrors `resolveBin` above. */\n resolveApprovalMcpBin?: () => ResolvedApprovalMcpBin;\n}\n\n/**\n * Claude Code runtime adapter (`claude -p --input-format stream-json\n * --output-format stream-json`) — the M2-a counterpart to `../pi/pi-adapter.ts`.\n * Every behavioral claim in this file's own doc comments and its sibling\n * modules (`events.ts`, `permission-mapping.ts`, `process-client.ts`) was\n * empirically reproduced against the real installed `claude` 2.1.212 binary\n * on a logged-in machine (per this task's own \"do NOT trust docs over the\n * real binary\" mandate — `claude --help` was actively wrong/misleading for\n * `--allowedTools`, see `permission-mapping.ts`) — not inferred from\n * training-data recall or the Claude API/Agent-SDK docs, which describe a\n * DIFFERENT product surface (the Messages API, not this CLI's headless\n * wire format).\n *\n * ## The central finding: claude's headless approval model has no\n * `needs_approval` pause, at all\n *\n * This is the first real use of the `needs_approval` /\n * `Session.resolveApproval` seam any adapter in this codebase has\n * implemented (pi never emits `needs_approval` — see `PiSession\n * .resolveApproval`'s own doc comment) — so this finding directly informs\n * the M2-c protocol-freeze decision on that seam.\n *\n * Empirically (see the M2-a report for the full live-capture evidence):\n * spawning `claude -p` **non-interactively** with a tool call that would\n * normally prompt a human is resolved **synchronously, before the turn\n * continues** — there is no pause, no wait, no later resumption point:\n *\n * - Under `--permission-mode default` (or no flag at all — headless has no\n * TTY to interactively ask), an unapproved tool call is immediately\n * AUTO-DENIED with a synthesized `tool_result`\n * (`\"Claude requested permissions to write to <path>, but you haven't\n * granted it yet.\"`, `is_error:true`) and the run continues normally to\n * its own `result` frame — no hang, and nothing this adapter could ever\n * resume later even if it wanted to.\n * - Under a permissive `--permission-mode` (`acceptEdits`/`bypassPermissions`),\n * the call is auto-GRANTED, again synchronously, again with nothing to\n * pause on.\n *\n * There is consequently no claude stream-json frame this adapter could\n * ever map to the protocol's `needs_approval` `AgentEvent` — the decision\n * is always already made by the time any frame reaches this adapter at\n * all. `resolveApproval()` below throws a descriptive error rather than\n * silently no-op'ing, mirroring `PiSession.resolveApproval`'s own\n * documented reasoning exactly: a caller that ever receives\n * `task.approve`/`task.reject` for one of this adapter's tasks implies\n * something upstream expected approval support this adapter genuinely does\n * not have.\n *\n * `PermissionPolicy.mode: 'confirm'` — the policy mode whose whole point is\n * \"ask a human, then proceed\" — was therefore rejected outright at\n * `start()` through M2/M3 (fail-closed, see `permission-mapping.ts`), never\n * silently downgraded to auto-accept or auto-deny.\n *\n * ## M4 Phase 3 update: a genuine out-of-band pause DOES exist — it is\n * just invisible to everything written above\n *\n * `--permission-prompt-tool` (a DIFFERENT flag from `--permission-mode`,\n * undocumented in `claude --help`'s own output on the installed 2.1.216\n * binary but empirically confirmed accepted — an unrecognized flag is\n * rejected outright with `error: unknown option`, this one is not) makes\n * claude block a turn on a real MCP round-trip to a server it spawns\n * itself, waiting for that server to answer allow/deny before continuing —\n * genuinely pausing, for real wall-clock time (live-verified: an instant\n * allow/deny, AND a deliberate multi-second delayed answer, both worked\n * identically; only a permission-prompt-tool call that never answers AT\n * ALL was found to make claude abandon the turn on its own, after roughly\n * 1.5s — never actually reachable by this design, since the bundled\n * `bin/byok-approval-mcp.ts` always eventually answers within its own\n * configured ceiling).\n *\n * Everything above this section remains true and is NOT superseded by\n * this: claude's own stream-json output still emits nothing while this\n * pause is in progress — the gap between a `tool_use` frame and its\n * `tool_result` is indistinguishable from ordinary model latency on the\n * wire, and there is still no `needs_approval`-shaped frame this adapter's\n * event mapper could ever produce. The pause is real, but it is invisible\n * to `ClaudeSession.events` and to `task-runner.ts`'s `pump()` entirely —\n * it is only ever observable from OUTSIDE this adapter's own process, by\n * the separate MCP-server child process claude itself spawns. This is why\n * `confirm` mode's daemon-side wiring (`task-runner.ts`'s `requestApproval`,\n * `types.ts`'s `ApprovalChannel`) is driven from the control socket, not\n * from any `AgentEvent` — see those files' own doc comments for the full\n * design this finding drove. `confirm` is now SUPPORTED (see\n * `permission-mapping.ts` and `resolveApproval()` below), still fail-closed\n * whenever no approval channel was actually wired up for this session.\n *\n * ## Steering was also found unsupported (a second, related finding)\n *\n * Live-probed via a persistent `--input-format stream-json` process:\n * writing a second `{\"type\":\"user\",...}` message to stdin WHILE a turn is\n * still generating does NOT redirect that in-flight turn — it QUEUES as a\n * separate, subsequent turn, processed only after the first one reaches\n * its own `result`. This is genuinely useful for `followUp()` (a new turn\n * \"after [the session] has gone idle\" — exactly the queued-after-result\n * case), but it is not what `Session.steer`'s \"inject steering text into a\n * running turn (mid-stream)\" contract promises. `capabilities().steer` is\n * therefore `false`, and `steer()` throws rather than silently behaving\n * like a queued follow-up under a name that implies live redirection.\n */\nexport class ClaudeAdapter implements RuntimeAdapter {\n readonly id = 'claude';\n\n constructor(private readonly options: ClaudeAdapterOptions = {}) {}\n\n async detect(): Promise<RuntimeDetectResult> {\n const bin = this.resolveBin();\n try {\n // Empirically confirmed: claude 2.1.212 prints `--version` output to\n // STDOUT. Only stdout is read here because that is what was probed;\n // pi reads both streams instead because its own `--version` channel\n // has moved between pi releases (see ../pi/pi-adapter.ts).\n const { stdout } = await execFileAsync(bin.command, ['--version'], { timeout: DETECT_TIMEOUT_MS });\n const version = stdout.trim();\n const authPresent = await this.probeAuthPresent(bin.command);\n return { present: true, version, authPresent };\n } catch {\n return { present: false };\n }\n }\n\n capabilities(): RuntimeCapabilities {\n // M4 Phase 3: 'confirm' added — see permission-mapping.ts's confirm-mode\n // doc comment for the empirical basis (`--permission-prompt-tool`,\n // live-verified against the real installed binary).\n // `approvalInteractive: true` — the confirm path is genuinely wired end\n // to end: `--permission-prompt-tool` → the out-of-process approval MCP\n // server (`bin/byok-approval-mcp.ts`) → this daemon's control socket →\n // `ClaudeSession.resolveApproval` really resuming the paused turn.\n return { steer: false, resume: true, approvalInteractive: true, permissionModes: ['auto', 'readonly', 'plan', 'confirm'] };\n }\n\n /**\n * M5: deliberate product-boundary decision, not an oversight — byok's\n * current ToS posture for claude is login-state-only (`claude auth\n * login`'s own OAuth session — see `probeAuthPresent` below), so this\n * adapter declares NO credential env vars at all; env-based API-key\n * passthrough for claude is a separate, still-pending product decision.\n * A product that genuinely needs it can opt in locally per-device via\n * `DaemonConfig.runtimeEnvironment.claude.allow` (`create-daemon.ts`).\n * `baseNames` is empty too: nothing in this adapter reads a\n * claude-specific config-discovery variable (e.g. `CLAUDE_CONFIG_DIR`)\n * today — if a future version of this adapter starts reading one, it\n * belongs here, not left to rely on the platform baseline alone.\n */\n environmentRequirements(): RuntimeEnvironmentRequirements {\n return { credentialNames: [] };\n }\n\n async start(task: TaskOfferPayload, ctx: TaskContext): Promise<Session> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('claude adapter only supports string instructions in M2 (no blob-ref fetch yet)');\n }\n\n const mapping = mapPermissionPolicyToClaudeArgs(ctx.policy);\n if (!mapping.ok) {\n throw new PolicyUnsupportedError(mapping.reason ?? 'policy rejected by claude adapter');\n }\n\n // M4 Phase 3: `confirm` mode's approval channel. Generating the\n // temp --mcp-config file is a real filesystem side effect (see\n // permission-mapping.ts's `needsApprovalMcp` doc comment for why this\n // lives here, in start(), rather than in the pure mapping function) —\n // deliberately OUTSIDE ctx.workspaceDir (a fresh, 0700 os.tmpdir()\n // subdirectory instead) so it never shows up to the agent's own\n // workspace-scoped Read/Glob/Grep, even though its contents (a storeDir\n // path, productId, and this taskId — no secret/token material at all;\n // the actual control-socket auth token stays under storeDir, unrelated\n // to this file) would be low-value to an agent that found it anyway.\n let approvalMcpConfigDir: string | undefined;\n if (mapping.needsApprovalMcp) {\n if (!ctx.approvalChannel) {\n // Internal-consistency fail-closed: TaskRunner always populates this\n // (see task-runner.ts's handleOffer) — a missing one here means\n // something upstream is badly wired, not a normal policy rejection.\n throw new PolicyUnsupportedError(\n 'claude adapter requires policy.mode \"confirm\" to be started with an approval channel (TaskContext.approvalChannel) — none was provided',\n );\n }\n const approvalMcpBin = (this.options.resolveApprovalMcpBin ?? resolveApprovalMcpBin)();\n approvalMcpConfigDir = await fs.mkdtemp(path.join(os.tmpdir(), 'byok-approval-mcp-'));\n await fs.chmod(approvalMcpConfigDir, 0o700).catch(() => {});\n const mcpConfigPath = path.join(approvalMcpConfigDir, 'mcp-config.json');\n const mcpConfig = {\n mcpServers: {\n [APPROVAL_MCP_SERVER_NAME]: {\n command: approvalMcpBin.command,\n args: approvalMcpBin.args,\n env: {\n BYOK_STORE_DIR: ctx.approvalChannel.storeDir,\n BYOK_PRODUCT_ID: ctx.approvalChannel.productId,\n BYOK_TASK_ID: ctx.approvalChannel.taskId,\n BYOK_APPROVAL_TIMEOUT_MS: String(ctx.approvalChannel.timeoutMs),\n },\n },\n },\n };\n await fs.writeFile(mcpConfigPath, JSON.stringify(mcpConfig), { mode: 0o600 });\n mapping.args = [\n ...mapping.args,\n '--permission-prompt-tool',\n `mcp__${APPROVAL_MCP_SERVER_NAME}__${APPROVAL_TOOL_NAME}`,\n '--mcp-config',\n mcpConfigPath,\n // Never let this task's confirm-mode run pick up some OTHER MCP\n // server from ambient project/user config — the approval channel is\n // the only MCP server this invocation should ever see.\n '--strict-mcp-config',\n ];\n }\n\n const bin = this.resolveBin();\n const resumeSessionId = task.sessionRef;\n const args = [\n '-p',\n '--input-format',\n 'stream-json',\n '--output-format',\n 'stream-json',\n // REQUIRED alongside `--output-format stream-json` in `--print` mode —\n // empirically confirmed: omitting this exits 1 immediately with\n // \"Error: When using --print, --output-format=stream-json requires\n // --verbose\", before spawning any model call.\n '--verbose',\n ...(resumeSessionId ? ['--resume', resumeSessionId] : []),\n ...mapping.args,\n ];\n\n const client = new ClaudeProcessClient({\n command: bin.command,\n args,\n cwd: ctx.workspaceDir,\n env: ctx.env,\n spawnFn: this.options.spawnFn,\n });\n\n // `--input-format stream-json` expects the first turn's instruction on\n // stdin too, not as a positional CLI argument — empirically confirmed\n // live (this task's persistent-process multi-turn probes never passed\n // a positional prompt at all, relying entirely on this same write for\n // turn one). Using the identical mechanism for turn one and every\n // `followUp()` afterward (see `ClaudeSession.followUp` /\n // `ClaudeProcessClient.writeUserMessage`) avoids two different\n // send-a-prompt code paths.\n client.writeUserMessage(task.instruction);\n\n let sessionRef: string;\n try {\n // Resolves with claude's own real `session_id` off its `system/init`\n // frame — see `ClaudeProcessClient.waitForInit`'s doc comment for why\n // this is needed at all (claude's stream-json protocol has no\n // request/response ack the way pi's RPC mode does) and why, unlike\n // pi's `resolveFreshSessionId`, no separate follow-up round-trip is\n // needed: claude always surfaces `session_id` directly on the very\n // first frame of a successful run, resume or fresh alike (confirmed:\n // a `--resume <id>` run's own `system/init.session_id` always equals\n // the requested id). An unresolvable `--resume` target (or any other\n // immediate failure) never emits an `init` frame at all and instead\n // exits promptly — `waitForInit()` rejects with the enriched exit\n // error in that case (see `process-client.ts`), which is exactly\n // what should make `start()` fail here, fail-closed, never a\n // fabricated sessionRef.\n sessionRef = await client.waitForInit();\n } catch (err) {\n client.kill();\n await cleanupApprovalMcpConfigDir(approvalMcpConfigDir);\n throw err;\n }\n\n // Cross-model review finding: the doc comment above states this is\n // \"confirmed\" to always hold empirically — but nothing actually verified\n // it in code, so a future/unobserved claude behavior (or a bug) silently\n // resuming a DIFFERENT session than `task.sessionRef` asked for would\n // have gone completely unnoticed: this adapter would return a\n // `ClaudeSession` for whatever `sessionRef` claude happened to report,\n // running the task against the wrong workspace/history with no signal\n // to the caller at all. Fail closed instead of trusting the assumption.\n if (resumeSessionId !== undefined && sessionRef !== resumeSessionId) {\n client.kill();\n await cleanupApprovalMcpConfigDir(approvalMcpConfigDir);\n throw new Error(\n `claude --resume echoed a different session id than requested (requested ${resumeSessionId}, got ${sessionRef}) — refusing to continue in a possibly-wrong session (fail-closed)`,\n );\n }\n\n return new ClaudeSession(sessionRef, client, ctx.workspaceDir, ctx.approvalChannel, approvalMcpConfigDir);\n }\n\n /**\n * `claude auth status --json` is claude's OWN non-secret login-state\n * signal (see the credential-isolation rule on `RuntimeAdapter` in\n * `../../types.ts`) — empirically confirmed live on this logged-in\n * machine to report `{\"loggedIn\":true,\"authMethod\":\"claude.ai\",\n * \"apiProvider\":\"firstParty\",\"email\":\"...\",\"orgId\":\"...\",\"orgName\":\"...\",\n * \"subscriptionType\":\"max\"}`, with no token/key material anywhere in it.\n * This spawns the binary and parses ONLY its own reported status — it\n * never reads `~/.claude` or any credential file itself, matching pi's\n * `authPresent` computation being limited to environment-variable\n * *names* (`../pi/pi-adapter.ts`'s `KNOWN_PROVIDER_ENV_VARS`), just via\n * claude's own equivalent non-secret probe instead (claude's auth is\n * OAuth-session-based via `claude auth login`, not primarily an env var,\n * so pi's env-var-presence approach doesn't apply here the same way).\n * A failed/unparseable probe (binary present but not logged in, a future\n * claude release changing this output shape, etc.) fails closed to\n * `false` — this never affects `present`, which is solely about whether\n * `--version` itself succeeded.\n */\n private async probeAuthPresent(command: string): Promise<boolean> {\n try {\n const { stdout } = await execFileAsync(command, ['auth', 'status', '--json'], { timeout: DETECT_TIMEOUT_MS });\n const parsed = JSON.parse(stdout) as { loggedIn?: unknown };\n return parsed.loggedIn === true;\n } catch {\n return false;\n }\n }\n\n private resolveBin(): ResolvedBin {\n return (this.options.resolveBin ?? resolveClaudeBin)();\n }\n}\n\nclass ClaudeSession implements Session {\n private readonly correlation: ToolUseCorrelation = createToolUseCorrelation();\n\n constructor(\n public readonly sessionRef: string,\n private readonly client: ClaudeProcessClient,\n private readonly workspaceDir: string,\n /** M4 Phase 3: set only when this session was started under `policy.mode: 'confirm'` — see `resolveApproval()`. */\n private readonly approvalChannel?: ApprovalChannel,\n /** M4 Phase 3: the temp `--mcp-config` directory `start()` created for this session, if any — removed in `close()`. */\n private readonly approvalMcpConfigDir?: string,\n ) {}\n\n get events(): AsyncIterable<AgentEvent> {\n const client = this.client;\n const correlation = this.correlation;\n const workspaceDir = this.workspaceDir;\n return {\n [Symbol.asyncIterator](): AsyncIterator<AgentEvent> {\n const inner = client.events[Symbol.asyncIterator]();\n // A single raw claude frame can map to more than one AgentEvent\n // (e.g. a Write `tool_result` plus a derived `artifact` — see\n // `events.ts`'s `mapUser`) — buffered here and drained before\n // pulling the next raw line, so this iterator still yields exactly\n // one `AgentEvent` per `next()` call like every other adapter's\n // `Session.events`.\n let pending: AgentEvent[] = [];\n // Cross-model re-review finding (P1 regression, the \"claude-hang\n // class\"): set once THIS turn's own `result` frame has been read off\n // `inner` — `result` is claude's real \"whole run settled\" signal and\n // is always the LAST frame of a turn, success or failure alike (see\n // `events.ts`'s `mapResult` doc comment). On the SUCCESS path\n // (`turn_end`) this is inert: `task-runner.ts`'s `pump()` returns the\n // instant it sees `turn_end`, so no further `next()` call ever\n // happens. It matters on the FAILURE/malformed path: `mapResult`\n // maps a non-success result to a plain `error` AgentEvent with no\n // `turn_end` — and unlike codex (one process per turn, whose own\n // process-close watcher ends the queue — see codex-adapter.ts's\n // `runCodexTurn`), claude's process is PERSISTENT: it stays alive\n // after `result`, awaiting a possible `followUp()` write on stdin\n // (see process-client.ts's own doc comment). Nothing would otherwise\n // ever end this iterator, so `task-runner.ts`'s `pump()` would await\n // a raw line that never arrives — a real, confirmed hang, not just a\n // theoretical one. Once `turnSettled` is true and `pending` has been\n // fully drained (every event from the `result` frame delivered),\n // this iterator ends itself (`done:true`) WITHOUT pulling `inner`\n // again — ending only THIS TURN's own exposed event stream, never\n // the underlying session-lifetime `client.events` queue a future\n // `followUp()` still needs, and never killing the process itself\n // (that stays this session's `close()`/`interrupt()`'s job). This is\n // exactly what lets `pump()`'s existing \"iterable ended without\n // turn_end\" branch report the task as Failed instead of hanging.\n let turnSettled = false;\n return {\n async next(): Promise<IteratorResult<AgentEvent>> {\n for (;;) {\n const buffered = pending.shift();\n if (buffered) return { value: buffered, done: false };\n\n if (turnSettled) return { value: undefined as never, done: true };\n\n const { value, done } = await inner.next();\n if (done) return { value: undefined as never, done: true };\n\n if (value.type === 'result') turnSettled = true;\n\n const mapped = mapClaudeMessageToAgentEvents(value, correlation, { workspaceDir });\n if (mapped.unmappedLabel) {\n client.recordUnmappedFrame(mapped.unmappedLabel);\n }\n if (mapped.events.length > 0) {\n pending = mapped.events;\n }\n // Nothing to yield yet (routine frame, or an unmapped one) —\n // loop around and pull the next raw line.\n }\n },\n };\n },\n };\n }\n\n /**\n * Not supported — see the class-level doc comment on `ClaudeAdapter` for\n * the full empirical basis (a message written to stdin mid-turn was\n * proven to queue as a follow-up turn, not redirect the running one).\n * `capabilities().steer` reports `false` for exactly this reason; this\n * throws rather than silently behaving like `followUp()` under the\n * `steer()` name, which would promise live redirection it cannot deliver.\n */\n async steer(): Promise<void> {\n throw new SteerUnsupportedError(\n 'claude',\n 'claude adapter does not support mid-turn steering: writing to claude\\'s stdin while a turn is in flight queues as a separate subsequent turn rather than redirecting the running one (empirically confirmed) — see capabilities().steer',\n );\n }\n\n async followUp(task: TaskOfferPayload): Promise<void> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('claude adapter only supports string instructions in M2 (no blob-ref fetch yet)');\n }\n // Writes onto the SAME persistent process this session already has\n // open — empirically confirmed live that claude keeps a\n // `--input-format stream-json` process alive across sequential turns,\n // reusing the identical `session_id`, until stdin closes or the\n // process is killed (see `ClaudeProcessClient.writeUserMessage`'s doc\n // comment).\n this.client.writeUserMessage(task.instruction);\n }\n\n /**\n * Real claude has no distinct \"abort but stay alive\" primitive the way\n * pi's RPC mode does (`{type:'abort'}`, after which pi keeps running and\n * stays queryable) — nothing in `claude --help` exposes one, and this\n * task's probes found none. SIGTERM (via `ClaudeProcessClient.kill()`)\n * is the only verified way to stop an in-flight turn, so `interrupt()`\n * and `close()` both resolve to the same underlying action here. This is\n * consistent with how they are actually used together: `task-runner.ts`'s\n * `handleCancel` always calls `interrupt()` immediately followed by\n * `close()` on the same task, never `interrupt()` alone expecting the\n * session to remain usable afterward.\n */\n async interrupt(): Promise<void> {\n this.client.kill();\n }\n\n async close(): Promise<void> {\n this.client.kill();\n await cleanupApprovalMcpConfigDir(this.approvalMcpConfigDir);\n }\n\n /**\n * M4 Phase 3: routes into the out-of-band approval channel `start()`\n * threaded through from `TaskContext.approvalChannel` — see that type's\n * own doc comment (`../../types.ts`) for the full design, and\n * `permission-mapping.ts`'s `confirm`-mode doc comment for the empirical\n * basis. `approved`/`reason` map directly onto `ApprovalChannel.resolve`'s\n * own parameters, which in turn resolve the SAME `ApprovalRegistry` entry\n * `bin/byok-approval-mcp.ts`'s pending `approvals.request` control call is\n * awaiting — answering that call is what lets claude's own blocked\n * `tools/call` (and therefore the paused turn) proceed.\n *\n * Still throws when no channel is present — every adapter/session that\n * ISN'T running under `confirm` mode (the overwhelming majority) has\n * nothing to resolve, and a caller receiving `task.approve`/`task.reject`\n * for one of those implies something upstream expected approval support\n * that isn't there, exactly as this method's doc comment always said.\n * `ApprovalChannel.resolve` itself throws the equally-descriptive \"no\n * pending approval\" error for the narrower case (confirm mode, but nothing\n * currently pending) — this method doesn't need its own separate check for\n * that.\n */\n async resolveApproval(approved: boolean, reason?: string): Promise<void> {\n if (!this.approvalChannel) {\n throw new Error(\n 'claude adapter has no approval channel for this session (not running under policy.mode \"confirm\") — under every other mode, claude resolves every permission decision synchronously (auto-denied under a restrictive --permission-mode, auto-granted under a permissive one) before this adapter ever sees the corresponding frame, so there is nothing to resume later',\n );\n }\n await this.approvalChannel.resolve(approved, reason);\n }\n}\n","export interface ResolvedBin {\n command: string;\n source: 'env' | 'path';\n}\n\n/**\n * Resolve the codex CLI executable.\n *\n * Like pi and Claude Code, the real OpenAI Codex CLI (empirically `codex-cli\n * 0.144.5` on the machine this adapter was built/verified against, installed\n * at a plain PATH location — not inside this repo's `node_modules`) is not\n * published as an npm package this SDK could sensibly depend on: it's a\n * standalone global install (native installer / `npm i -g @openai/codex` /\n * homebrew, depending on platform and version). There is no package-relative\n * resolution to attempt: use an explicit override for tests, else a bare\n * PATH lookup.\n *\n * `BYOK_CODEX_BIN` overrides PATH lookup — substituting the fake-codex test\n * fixture, exactly like `BYOK_PI_BIN` does for pi. The `byok-agent` CLI bin\n * only ever constructs `new CodexAdapter()` with no options (mirroring\n * `createDaemon`'s pi wiring), so an out-of-process substitution (e.g. a\n * future e2e harness swapping in a fake binary ahead of a real codex install)\n * has no other seam to use.\n */\nexport function resolveCodexBin(): ResolvedBin {\n const override = process.env.BYOK_CODEX_BIN;\n if (override) {\n return { command: override, source: 'env' };\n }\n return { command: 'codex', source: 'path' };\n}\n","import type { PermissionPolicy } from '@byok-sdk/protocol';\n\nexport interface CodexPermissionMapping {\n ok: boolean;\n /** `-c key=value` args (as separate argv entries, e.g. `['-c', 'sandbox_mode=read-only']`) to append to `codex exec`/`codex exec resume`. Only meaningful when `ok` is true. */\n args: string[];\n /** Present when `ok` is false. */\n reason?: string;\n}\n\n/**\n * Map an effective {@link PermissionPolicy} to `codex exec`/`codex exec\n * resume` CLI args, fail-closed. Empirically grounded against the real\n * installed `codex exec --json` (`codex-cli 0.144.5`) — every claim below was\n * driven live in a scratch directory before being encoded here, exactly like\n * the pi adapter's own M0-3 findings (`../pi/permission-mapping.ts`), and the\n * same class of \"the CLI's own --help lies\" bug showed up again independently\n * (see the confirm/plan case below).\n *\n * - Uses `-c sandbox_mode=<value>` (a config-override key), NEVER the `-s`/\n * `--sandbox` CLI flag, and NEVER `-a`/`--ask-for-approval`:\n * - `-s`/`--sandbox` works on a fresh `codex exec` but is REJECTED\n * outright (\"error: unexpected argument '-s' found\") on `codex exec\n * resume` — confirmed against the real binary, not inferred from\n * --help (which lists `-s` for `exec` but, correctly for once, omits\n * it entirely from `resume`'s own --help).\n * - `-a`/`--ask-for-approval` is documented in `codex exec --help`\n * itself, yet REJECTED outright by `codex exec`'s real arg parser\n * (\"error: unexpected argument '-a' found\") — a second, independent\n * instance of the exact doc/binary mismatch class the pi adapter's own\n * `--session-id`/`--exclude-tools` findings already hit. Never used.\n * - `-c sandbox_mode=...` was confirmed empirically to produce the\n * IDENTICAL restriction as `-s` on a fresh `exec` (a read-only-sandbox\n * write attempt fails the same way either way), and to also work on\n * `resume` (where `-s` cannot be used at all) — so this one mechanism\n * is used everywhere, for both a fresh start and every later resume,\n * instead of switching mechanisms depending on call site.\n * - CRITICAL: `codex exec resume` does NOT auto-inherit the sandbox mode\n * the session was originally started with. Empirically confirmed: a\n * session started with `sandbox_mode=read-only` (which correctly\n * denied a write on its first turn) had that SAME write SUCCEED on a\n * later `codex exec resume` with no sandbox flag re-passed — silently\n * falling back to this machine's own ambient `~/.codex/config.toml`\n * default. This is exactly the \"silently widens\" failure this whole\n * mapping exists to prevent, so this adapter re-pins\n * `-c sandbox_mode=...` (and `-c approval_policy=never`, see below) on\n * *every* invocation — the initial `start()` AND every `followUp()` —\n * never relying on \"it was already set once.\"\n * - `auto` -> `sandbox_mode=workspace-write`. Empirically, this permits\n * writes inside the given cwd (the task workspace) AND the OS temp dir\n * (e.g. `/tmp`/`$TMPDIR`) — NOT strictly confined to the workspace, a\n * real, non-obvious scope worth knowing about (confirmed: a shell write to\n * `/tmp/...` succeeds even though it's outside `ctx.workspaceDir`; a write\n * to `$HOME` is correctly denied with a clean \"operation not permitted\",\n * not a hang or silent no-op).\n * - `readonly` -> `sandbox_mode=read-only`. Empirically denies every write —\n * both `apply_patch`-style file edits and shell-redirection writes — with\n * an immediate, clean, non-interactive denial (real stderr: \"writing is\n * blocked by read-only sandbox; rejected by user approval settings\").\n * Never a hang, never a silent bypass.\n * - `confirm` / `plan` are NOT expressible and fail closed. `codex exec` has\n * no interactive approval channel at all in this build — confirmed two\n * independent ways: (1) the flag that would request one (`-a`/\n * `--ask-for-approval`) is rejected outright by the real parser (see\n * above); (2) even reaching the underlying setting via the `-c\n * approval_policy=...` config-key path (which IS accepted), a\n * sandbox-denied action under a non-`never` policy resolves the \"ask\"\n * *internally* with no wire-visible pause and no way for an external\n * caller to answer it: it just auto-denies, and the model narrates the\n * denial as a normal `agent_message` — there is no `needs_approval`-\n * equivalent signal anywhere in the `--json` stream to map to (see\n * `../codex/events.ts`'s module doc comment). `plan` has no dedicated\n * codex equivalent either (no plan-only/no-exec CLI mode exists on this\n * build). Rather than pretend either is supported, both fail closed here —\n * the same posture as pi's own `confirm`/`plan` rejection.\n * - `network: true` is NOT expressible and fails closed. The one config key\n * that should enable network under `workspace-write`\n * (`sandbox_workspace_write.network_access=true`) was tested three times\n * against the real binary — including a pure `-c`-only invocation with no\n * `-s` flag mixed in, ruling out CLI-flag/config-table interference as the\n * cause — and never restored real network access: a `curl` to a live host\n * still failed every time, though its failure mode shifted from a\n * DNS-level block (exit 6, \"Could not resolve host\") to a data-level block\n * (exit 56, \"Failure receiving network data\"), hinting the override does\n * *something* internally but not enough to trust as a real grant. A\n * control run under `sandbox_mode=danger-full-access` confirmed network\n * genuinely works from this environment (HTTP 200), so the gap is in this\n * specific override on this codex build, not the test methodology. This\n * adapter never silently proceeds without the grant it was asked for, so\n * `network: true` is rejected rather than quietly running with no network\n * and no explanation. `network: false`/unset needs no special handling:\n * both sandbox modes this adapter ever selects have NO network by default\n * (confirmed empirically for both `read-only` and plain `workspace-write`)\n * — the safe case is also the default case, so nothing to enforce.\n * - `allowTools`/`denyTools` are NOT expressible and fail closed when\n * non-empty. `codex exec` has no verified per-tool allow/deny surface —\n * only the coarse `sandbox_mode` dial — unlike pi's real `--tools`/\n * `--no-tools`. Rather than silently drop a requested tool restriction,\n * this rejects it outright.\n * - `approval_policy=never` is pinned unconditionally on every `ok: true`\n * result, for the same \"never trust the ambient default\" reason as\n * `sandbox_mode` above: a fresh `codex exec` invoked with NO sandbox/\n * approval flags at all on the reference machine successfully ran a live\n * network `curl` with zero prompting — proof this machine's own\n * `~/.codex/config.toml` default is more permissive than anything this\n * adapter should ever grant implicitly. Every invocation pins both keys\n * explicitly so behavior never depends on the end user's own codex config.\n */\nexport function mapPermissionPolicyToCodexArgs(policy: PermissionPolicy): CodexPermissionMapping {\n if (policy.mode === 'confirm' || policy.mode === 'plan') {\n return {\n ok: false,\n args: [],\n reason: `codex adapter cannot express permission mode \"${policy.mode}\" (codex exec has no interactive approval channel — sandbox-denied actions resolve internally with no needs_approval-equivalent wire signal, and -a/--ask-for-approval is rejected outright by codex exec's real arg parser despite being documented in --help)`,\n };\n }\n\n if (policy.network === true) {\n return {\n ok: false,\n args: [],\n reason:\n 'codex adapter cannot guarantee network:true (empirically, -c sandbox_workspace_write.network_access=true did not restore real network access on the installed codex build — see this file\\'s module doc comment) — never silently proceeds without the requested grant',\n };\n }\n\n if ((policy.allowTools && policy.allowTools.length > 0) || (policy.denyTools && policy.denyTools.length > 0)) {\n return {\n ok: false,\n args: [],\n reason:\n 'codex adapter cannot express allowTools/denyTools (codex exec has no verified per-tool allow/deny surface, only the coarse sandbox_mode) — never silently ignores a requested tool restriction',\n };\n }\n\n const sandboxMode = policy.mode === 'readonly' ? 'read-only' : 'workspace-write';\n return { ok: true, args: ['-c', `sandbox_mode=${sandboxMode}`, '-c', 'approval_policy=never'] };\n}\n","import path from 'node:path';\nimport type { AgentEvent } from '@byok-sdk/protocol';\nimport type { CodexRawEvent } from './process-runner';\n\n/**\n * `codex exec --json` event catalog, empirically captured (raw JSONL frames,\n * not inferred from docs) driving the real installed `codex-cli 0.144.5`\n * across: plain chat, file writes (`apply_patch`-style), shell commands,\n * network attempts, sandbox denials, an invalid-model API error, and a\n * SIGTERM-killed-then-resumed thread. Two nesting levels: top-level frames\n * (`thread.started`, `turn.started`, `item.started`, `item.completed`,\n * `turn.completed`, `turn.failed`, and a top-level `error` distinct from any\n * item), and — inside `item.started`/`item.completed` — a nested `item.type`\n * (`agent_message`, `command_execution`, `file_change`, `error`).\n *\n * `thread.started` (always the first line codex ever prints, both for a\n * fresh `codex exec` and for `codex exec resume`) is deliberately NOT\n * handled here — it's consumed directly by `../codex-adapter.ts`'s\n * `runCodexTurn` to resolve `Session.sessionRef` before this mapper ever\n * sees a line, mirroring how pi's adapter resolves a fresh session id via\n * `get_state` before constructing its `Session` (`../pi/pi-adapter.ts`'s\n * `resolveFreshSessionId`). It's still listed in\n * {@link ROUTINE_CODEX_EVENT_TYPES} for defensiveness, in case it were ever\n * unexpectedly seen again mid-stream.\n *\n * No case here ever produces `{type: 'needs_approval'}` — deliberately, not\n * an oversight. `codex exec` (this build) has no wire-visible signal for it\n * at all: a sandbox-denied action under any approval policy resolves\n * internally with no pause an external caller could ever answer — confirmed\n * empirically (a `sandbox_mode=read-only` write attempt, and separately an\n * `approval_policy=untrusted` shell command outside the sandbox's own\n * trusted-command allowlist, both either just ran when the sandbox allowed\n * it or were silently auto-denied when it didn't, narrated only as a normal\n * `agent_message` — never a distinct event this mapper could hook). See\n * `../codex-adapter.ts`'s `resolveApproval`, which throws rather than\n * pretending to support a resume path that cannot exist on the wire.\n */\nexport function mapCodexEventToAgentEvents(evt: CodexRawEvent, workspaceDir: string): AgentEvent[] {\n switch (evt.type) {\n case 'turn.completed': {\n // `usage` (input/cached-input/output/reasoning-output token counts) —\n // pre-freeze protocol change (see `packages/protocol/src/agent-event.ts`)\n // added a `usage` AgentEvent variant specifically for this data, which\n // was real and present here but had no wire slot before that. Mapped\n // 1:1 from codex's own field names (`extractCodexUsageEvent` below);\n // no `total_tokens` field has ever been observed on real codex output,\n // so `totalTokens` is never populated here — never synthesized by\n // summing, per this adapter's \"forward only what's actually reported\"\n // convention.\n //\n // Ordering is load-bearing, not stylistic: `usage` is placed BEFORE\n // `turn_end` in the returned array. `codex-adapter.ts`'s `runCodexTurn`\n // pushes each element of this array onto the session's queue in order,\n // and `task-runner.ts`'s `pump()` returns immediately (ending the read\n // loop) the instant it sees `turn_end` — an event queued AFTER it would\n // never be drained. See `../claude/events.ts`'s `mapResult`, which has\n // the identical ordering constraint for the same reason.\n const usageEvent = extractCodexUsageEvent(evt.usage);\n return usageEvent ? [usageEvent, { type: 'turn_end' }] : [{ type: 'turn_end' }];\n }\n\n case 'turn.failed':\n // The real turn-level failure signal — NOT `item.completed{type:\n // 'error'}` (see below, which is informational/non-fatal and can\n // appear even on a fully successful turn). Deliberately does NOT also\n // synthesize a `turn_end`: task-runner.ts's `pump()` only treats an\n // explicit `turn_end` as success, and an events iterable that ends\n // without one already reports `task.fail` on its own (the codex\n // process exits non-zero right after this frame, ending the stream\n // naturally) — so this only needs to make the failure reason visible\n // via `task.progress` before that happens.\n return [{ type: 'error', message: extractErrorMessage(evt.error) ?? 'codex turn failed' }];\n\n case 'error':\n // Top-level (not nested in an item) — empirically seen paired\n // immediately before a `turn.failed`, carrying the same underlying API\n // error message (itself a JSON-encoded string from the provider\n // response) verbatim, deliberately not re-parsed — forwarding exactly\n // what codex reported is more honest than guessing at its structure.\n return [{ type: 'error', message: typeof evt.message === 'string' ? evt.message : 'codex reported an error' }];\n\n case 'item.started':\n return mapItem(evt.item, 'started', workspaceDir);\n\n case 'item.completed':\n return mapItem(evt.item, 'completed', workspaceDir);\n\n // `thread.started`/`turn.started` carry no AgentEvent-mappable content —\n // see the module doc comment above for why `thread.started` is handled\n // upstream instead. Both are also listed in ROUTINE_CODEX_EVENT_TYPES.\n case 'thread.started':\n case 'turn.started':\n return [];\n\n default:\n return []; // caller (codex-adapter.ts) records this as an unmapped frame unless isRoutineCodexEvent(evt)\n }\n}\n\n/**\n * Maps `turn.completed.usage` to a `usage` `AgentEvent`, or `undefined` when\n * nothing usable is present (e.g. `usage: {}`, empirically also seen on real\n * codex — never emit a content-free `usage` event just because the key\n * existed). Field names are verbatim from real codex output — confirmed live\n * against the installed `codex-cli` binary while building this mapping\n * (`turn.completed` -> `{\"usage\":{\"input_tokens\":24963,\"cached_input_tokens\":\n * 9984,\"output_tokens\":5,\"reasoning_output_tokens\":0}}`), matching the\n * shape this adapter's own fixture (`fake-codex.mjs`) and unit tests already\n * encoded from the ORIGINAL M2-b capture. `0` is a real, reportable value\n * (not absence) for every one of these fields — only a non-number/missing\n * value is treated as \"not reported\".\n */\nfunction extractCodexUsageEvent(rawUsage: unknown): AgentEvent | undefined {\n if (!rawUsage || typeof rawUsage !== 'object') return undefined;\n const usage = rawUsage as Record<string, unknown>;\n const inputTokens = toNonNegativeInt(usage.input_tokens);\n const cachedInputTokens = toNonNegativeInt(usage.cached_input_tokens);\n const outputTokens = toNonNegativeInt(usage.output_tokens);\n const reasoningTokens = toNonNegativeInt(usage.reasoning_output_tokens);\n if (\n inputTokens === undefined &&\n cachedInputTokens === undefined &&\n outputTokens === undefined &&\n reasoningTokens === undefined\n ) {\n return undefined;\n }\n const event: Extract<AgentEvent, { type: 'usage' }> = { type: 'usage' };\n if (inputTokens !== undefined) event.inputTokens = inputTokens;\n if (cachedInputTokens !== undefined) event.cachedInputTokens = cachedInputTokens;\n if (outputTokens !== undefined) event.outputTokens = outputTokens;\n if (reasoningTokens !== undefined) event.reasoningTokens = reasoningTokens;\n return event;\n}\n\n/** Matches `AgentEventSchema`'s `usage` fields (`z.number().int().nonnegative().optional()`) — anything else (missing, a string, a float, negative) is treated as not-reported rather than coerced. */\nfunction toNonNegativeInt(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 ? value : undefined;\n}\n\ntype ItemPhase = 'started' | 'completed';\n\nfunction mapItem(rawItem: unknown, phase: ItemPhase, workspaceDir: string): AgentEvent[] {\n if (!rawItem || typeof rawItem !== 'object') return [];\n const item = rawItem as Record<string, unknown>;\n const itemType = item.type;\n\n switch (itemType) {\n case 'agent_message': {\n // Empirically only ever observed as `item.completed` — codex delivers\n // the whole message atomically, not as streamed token deltas the way\n // pi's `text_delta` does (see `../pi/events.ts`). An `item.started`\n // for `agent_message` has never been seen; if it ever is, this falls\n // through to the empty-array default and gets flagged as unmapped\n // (see `isRoutineCodexEvent`/`unmappedFrameKey`) rather than silently\n // treated as equivalent to `.completed`.\n if (phase !== 'completed') return [];\n return typeof item.text === 'string' ? [{ type: 'progress', text: item.text }] : [];\n }\n\n case 'command_execution': {\n const command = typeof item.command === 'string' ? item.command : undefined;\n if (phase === 'started') {\n return command !== undefined ? [{ type: 'tool_use', tool: 'command_execution', input: { command } }] : [];\n }\n return [\n {\n type: 'tool_result',\n tool: 'command_execution',\n output: {\n command,\n aggregatedOutput: item.aggregated_output,\n exitCode: item.exit_code,\n status: item.status,\n },\n },\n ];\n }\n\n case 'file_change': {\n const changes = Array.isArray(item.changes) ? item.changes : [];\n if (phase === 'started') {\n return [{ type: 'tool_use', tool: 'file_change', input: { changes } }];\n }\n return [\n { type: 'tool_result', tool: 'file_change', output: { changes, status: item.status } },\n ...extractArtifactEvents(changes, workspaceDir),\n ];\n }\n\n case 'error': {\n // Informational/warning-level, NOT necessarily a task failure — e.g.\n // \"Exceeded skills context budget...\" (this environment's installed\n // skill count exceeds codex's own model-visible budget) appeared as\n // `item_0` on EVERY single empirical capture made while building this\n // adapter, success or failure alike, and \"Model metadata for `<bad\n // model>` not found. Defaulting to fallback metadata...\" appeared\n // ahead of a turn that otherwise still ran. Forwarded honestly as an\n // `AgentEvent` (task-runner.ts's `pump()` already tolerates `error`\n // AgentEvents as non-fatal progress — see its own `sendArtifact`\n // failure handling, which does the same) rather than pattern-matching\n // on message text to guess which `error` items \"really\" matter, which\n // would be fragile and machine-specific (this exact notice may not\n // even appear on a machine with fewer installed codex skills).\n return typeof item.message === 'string' ? [{ type: 'error', message: item.message }] : [];\n }\n\n default:\n return [];\n }\n}\n\n/**\n * `file_change` items report the changed file's ABSOLUTE path. Converting to\n * a workspace-relative name before emitting an `artifact` AgentEvent matters\n * for two independent reasons, not just style:\n *\n * 1. `task-runner.ts`'s `openArtifact(workspaceDir, name)` resolves `name`\n * via `path.resolve(realWorkspaceDir, name)` — and `path.resolve` returns\n * an absolute second argument VERBATIM, discarding `realWorkspaceDir`\n * entirely (unlike `path.join`). Handing it codex's raw absolute path\n * would only happen to pass its containment check by coincidence (when\n * that absolute path happens to already be textually prefixed by the\n * realpath'd workspace dir) rather than by the intended, robust\n * relative-path contract every other adapter's artifact name follows\n * (pi's fixture, e.g., always uses a simple relative filename).\n * 2. It lets this mapper itself fail closed — skip, don't forward — a\n * `file_change` that (for whatever reason) landed outside the given\n * `workspaceDir` (`path.relative` starting with `..`), rather than\n * silently handing task-runner.ts's own containment check a path it has\n * to reject on this adapter's behalf.\n *\n * `contentType` is a best-effort extension guess: `file_change` items report\n * only `path`/`kind`, never a content type (unlike pi's synthetic artifact\n * message, which carries one directly) — `application/octet-stream` for\n * anything not in the small known-extension table below is an honest \"don't\n * know\" default, not a wrong guess dressed up as a real one.\n */\nfunction extractArtifactEvents(changes: unknown[], workspaceDir: string): AgentEvent[] {\n const events: AgentEvent[] = [];\n for (const rawChange of changes) {\n if (!rawChange || typeof rawChange !== 'object') continue;\n const change = rawChange as Record<string, unknown>;\n const absolutePath = typeof change.path === 'string' ? change.path : undefined;\n const kind = typeof change.kind === 'string' ? change.kind : undefined;\n if (!absolutePath || kind === 'delete') continue; // nothing to upload for a deletion\n\n const relative = path.relative(workspaceDir, absolutePath);\n if (relative.length === 0 || relative.startsWith('..') || path.isAbsolute(relative)) continue; // outside the workspace — never trust/forward (see doc comment above)\n\n events.push({ type: 'artifact', name: relative, contentType: guessContentType(relative) });\n }\n return events;\n}\n\nconst CONTENT_TYPE_BY_EXTENSION: Readonly<Record<string, string>> = {\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.json': 'application/json',\n '.js': 'text/javascript',\n '.mjs': 'text/javascript',\n '.cjs': 'text/javascript',\n '.ts': 'text/plain',\n '.html': 'text/html',\n '.css': 'text/css',\n '.py': 'text/x-python',\n '.yml': 'application/yaml',\n '.yaml': 'application/yaml',\n '.csv': 'text/csv',\n};\n\nfunction guessContentType(relativePath: string): string {\n return CONTENT_TYPE_BY_EXTENSION[path.extname(relativePath).toLowerCase()] ?? 'application/octet-stream';\n}\n\nfunction extractErrorMessage(rawError: unknown): string | undefined {\n if (typeof rawError === 'string') return rawError;\n if (!rawError || typeof rawError !== 'object') return undefined;\n const message = (rawError as Record<string, unknown>).message;\n return typeof message === 'string' ? message : undefined;\n}\n\n/** Top-level codex event types with no `AgentEvent` equivalent, by design (not \"not yet handled\") — kept in sync with the switch cases above sharing this comment. Used only for observability, mirroring pi's `ROUTINE_PI_EVENT_TYPES`: `codex-adapter.ts`'s per-line pump only records a frame as genuinely unmapped (`unmappedFrameKey` + a one-time console.warn) when it maps to an empty array AND its type isn't in this set. */\nexport const ROUTINE_CODEX_EVENT_TYPES: ReadonlySet<string> = new Set(['thread.started', 'turn.started']);\n\nexport function isRoutineCodexEvent(evt: CodexRawEvent): boolean {\n return ROUTINE_CODEX_EVENT_TYPES.has(evt.type);\n}\n\n/** Diagnostic key for unmapped-frame accounting: for `item.*` frames, folds in the nested `item.type` (e.g. `\"item.completed:reasoning\"`) so a never-seen item shape is distinguishable from a never-seen top-level type. */\nexport function unmappedFrameKey(evt: CodexRawEvent): string {\n if (evt.type === 'item.started' || evt.type === 'item.completed') {\n const item = evt.item;\n const itemType = item && typeof item === 'object' && typeof (item as Record<string, unknown>).type === 'string' ? (item as Record<string, unknown>).type : 'unknown';\n return `${evt.type}:${String(itemType)}`;\n }\n return evt.type;\n}\n","import { spawn, spawnSync, type ChildProcessByStdio } from 'node:child_process';\nimport type { Readable } from 'node:stream';\n\nexport type SpawnFn = typeof spawn;\n\n/** stdin is `null` (never a Writable) — this process never pipes stdin to the child; see the module doc comment below for why. */\ntype CodexChildProcess = ChildProcessByStdio<null, Readable, Readable>;\n\n/**\n * One parsed line of `codex exec --json` / `codex exec resume --json`\n * output. Field shapes vary by `type` (see `./events.ts`'s module doc\n * comment for the empirically-captured catalog), so this stays a loose bag\n * rather than a full discriminated union, mirroring `PiRpcMessage` in\n * `../pi/rpc-client.ts`.\n */\nexport interface CodexRawEvent {\n type: string;\n [key: string]: unknown;\n}\n\nexport interface CodexProcessOptions {\n command: string;\n args: string[];\n cwd: string;\n env: NodeJS.ProcessEnv;\n spawnFn?: SpawnFn;\n /** Called once per parsed JSONL line, in arrival order. */\n onEvent: (evt: CodexRawEvent) => void;\n}\n\n/** Bound on retained stderr lines (see `onStderr`/`buildExitError`) — mirrors `STDERR_RING_CAPACITY` in `../pi/rpc-client.ts`. */\nconst STDERR_RING_CAPACITY = 20;\n\n/**\n * Spawns and streams ONE `codex exec` / `codex exec resume` invocation — i.e.\n * exactly one turn.\n *\n * Unlike pi (a single long-lived RPC server process for a whole session's\n * lifetime — see `../pi/rpc-client.ts`), `codex exec` is a one-shot batch\n * process per turn with no persistent request/response channel: it takes its\n * prompt as an argv positional, streams JSONL to stdout for the one turn\n * it's running, and exits. `../codex-adapter.ts`'s `CodexSession` constructs\n * a fresh `CodexProcessRunner` for every turn (the initial `start()` and\n * every later `followUp()`), forwarding each one's lines into the same\n * long-lived event queue.\n *\n * stdin is deliberately never piped to the child (`stdio: ['ignore', 'pipe',\n * 'pipe']`): `codex exec --help` documents that a piped, non-TTY stdin is\n * read and appended to the prompt as a `<stdin>` block even when a prompt was\n * ALSO given as an argv positional, and empirically every single real\n * invocation made while building this adapter logged \"Reading additional\n * input from stdin...\" on stderr regardless of whether a prompt argument was\n * given. Leaving `stdio: ['pipe', ...]` open for stdin and never closing it\n * risks codex blocking on that read forever — exactly the hang class this\n * task was built to avoid (the pi adapter's own `agent_end`/`agent_settled`\n * mismatch left a task stuck `Running` forever in the M0/M1 GLM run).\n * `'ignore'` presents immediate EOF instead, which was verified live with a\n * dedicated Node `child_process` probe before this was written: no hang,\n * clean completion at normal model latency. This adapter never needs to\n * SEND codex anything over stdin — there is no in-band steer/approval\n * protocol (see `../codex-adapter.ts`'s `steer`/`resolveApproval`).\n */\nexport class CodexProcessRunner {\n private readonly child: CodexChildProcess;\n private readonly onEvent: (evt: CodexRawEvent) => void;\n private buffer = '';\n private readonly stderrRing: string[] = [];\n private closed = false;\n private exitCode: number | null = null;\n private exitSignal: NodeJS.Signals | null = null;\n private readonly closedPromise: Promise<void>;\n private resolveClosed!: () => void;\n\n constructor(options: CodexProcessOptions) {\n this.onEvent = options.onEvent;\n const spawnFn = options.spawnFn ?? spawn;\n this.child = spawnFn(options.command, options.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n this.closedPromise = new Promise((resolve) => {\n this.resolveClosed = resolve;\n });\n\n this.child.stdout.setEncoding('utf8');\n this.child.stdout.on('data', (chunk: string) => this.onData(chunk));\n\n this.child.stderr.setEncoding('utf8');\n this.child.stderr.on('data', (chunk: string) => this.onStderr(chunk));\n\n // `close` (not `exit`), mirroring rpc-client.ts's own reasoning: `close`\n // is Node's guarantee every stdout/stderr byte the process wrote has\n // already reached our listeners, so a post-mortem error built afterward\n // always has the complete stderr tail.\n this.child.on('close', (code, signal) => {\n this.exitCode = code;\n this.exitSignal = signal;\n this.finishClosing();\n });\n this.child.on('error', () => {\n // e.g. ENOENT for a missing binary — buildExitError's stderr tail will\n // be empty in this case, but exitCode/exitSignal staying null is\n // itself informative (never claims a fabricated exit code).\n this.finishClosing();\n });\n }\n\n private finishClosing(): void {\n if (this.closed) return;\n this.closed = true;\n this.resolveClosed();\n }\n\n /** Resolves once the child process has fully exited (both exit and stdio-flush guaranteed — see the `close` listener above). Never rejects. */\n waitClosed(): Promise<void> {\n return this.closedPromise;\n }\n\n get isClosed(): boolean {\n return this.closed;\n }\n\n /**\n * Best-effort teardown. SIGTERM on POSIX: SIGINT was empirically confirmed\n * to be silently ignored by `codex exec` (a real, direct test — a 60s\n * shell `sleep` ran to full, unaffected completion despite SIGINT sent at\n * t=4s) — a genuine, evidence-based correction to this task's own initial\n * assumption (\"interrupt: SIGINT — POSIX here\"). SIGTERM was separately\n * confirmed to terminate the process immediately (exit code 143) with no\n * orphaned child processes left behind (the shell command it was running\n * died with it), and — critically — the underlying codex thread remained\n * cleanly resumable afterward via `codex exec resume` (no corruption from\n * killing mid-turn). `taskkill /T /F` on Windows, mirroring\n * `../pi/rpc-client.ts`'s own cross-platform convention.\n */\n kill(): void {\n if (this.closed) return;\n const pid = this.child.pid;\n if (process.platform === 'win32' && pid !== undefined) {\n spawnSync('taskkill', ['/pid', String(pid), '/T', '/F']);\n } else {\n this.child.kill('SIGTERM');\n }\n }\n\n /** Builds a descriptive error folding in the exit code/signal and the stderr tail — mirrors `PiRpcClient.buildExitError`'s reasoning: a post-mortem on a failed start/resume should never need separately re-running codex by hand with a raw JSONL logger to learn why. */\n buildExitError(context: string): Error {\n const parts = [`${context} (exit code=${this.exitCode}, signal=${this.exitSignal})`];\n if (this.stderrRing.length > 0) {\n parts.push(`stderr: ${this.stderrRing.join(' | ')}`);\n }\n return new Error(parts.join('; '));\n }\n\n private onData(chunk: string): void {\n this.buffer += chunk;\n let newlineIndex = this.buffer.indexOf('\\n');\n while (newlineIndex !== -1) {\n let line = this.buffer.slice(0, newlineIndex);\n this.buffer = this.buffer.slice(newlineIndex + 1);\n if (line.endsWith('\\r')) line = line.slice(0, -1);\n if (line.length > 0) this.parseLine(line);\n newlineIndex = this.buffer.indexOf('\\n');\n }\n }\n\n private parseLine(line: string): void {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n return; // a stray non-JSON line is not this runner's concern\n }\n if (!parsed || typeof parsed !== 'object' || typeof (parsed as { type?: unknown }).type !== 'string') {\n return;\n }\n this.onEvent(parsed as CodexRawEvent);\n }\n\n private onStderr(chunk: string): void {\n for (const rawLine of chunk.split('\\n')) {\n const line = rawLine.trim();\n if (line.length === 0) continue;\n this.stderrRing.push(line);\n if (this.stderrRing.length > STDERR_RING_CAPACITY) this.stderrRing.shift();\n }\n }\n}\n","import { execFile } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\nimport { promisify } from 'node:util';\nimport type { AgentEvent, TaskOfferPayload } from '@byok-sdk/protocol';\nimport {\n PolicyUnsupportedError,\n SteerUnsupportedError,\n type RuntimeAdapter,\n type RuntimeCapabilities,\n type RuntimeDetectResult,\n type RuntimeEnvironmentRequirements,\n type Session,\n type TaskContext,\n} from '../../types';\nimport { AsyncQueue } from '../../util/async-queue';\nimport { resolveCodexBin, type ResolvedBin } from './resolve-bin';\nimport { mapPermissionPolicyToCodexArgs } from './permission-mapping';\nimport { isRoutineCodexEvent, mapCodexEventToAgentEvents, unmappedFrameKey } from './events';\nimport { CodexProcessRunner, type CodexRawEvent, type SpawnFn } from './process-runner';\n\nconst execFileAsync = promisify(execFile);\n\n/** Applied to both `detect()` probe calls — both are local-only and empirically fast (~50-80ms each), but detect() runs on every allowlist-narrowed task offer (see task-runner.ts's `pickAdapter`), so a small ceiling is cheap insurance against either ever unexpectedly hanging. */\nconst DETECT_TIMEOUT_MS = 5000;\n\nexport interface CodexAdapterOptions {\n /** Override bin resolution — tests substitute the fake-codex fixture script. */\n resolveBin?: () => ResolvedBin;\n /** Override process spawning — tests substitute a fake spawn. */\n spawnFn?: SpawnFn;\n}\n\n/**\n * `RuntimeAdapter` for the OpenAI Codex CLI (`codex exec --json`), the M2-b\n * counterpart to `../pi/pi-adapter.ts`. Every empirical claim in this file\n * and its sibling modules (`events.ts`, `permission-mapping.ts`,\n * `process-runner.ts`) was driven live against the real installed `codex-cli\n * 0.144.5` in a scratch directory before being encoded — repeating the pi\n * adapter's own M0-3 discipline (\"docs lied and shipped a nonexistent flag\")\n * independently found the exact same bug class on codex:\n *\n * - `codex exec --help` documents `-a`/`--ask-for-approval`; the real\n * parser rejects it outright on `codex exec` (\"unexpected argument\").\n * - `-s`/`--sandbox` works on a fresh `codex exec` but is rejected outright\n * on `codex exec resume` (whose own --help correctly omits it).\n * - `codex exec resume` does NOT auto-inherit the sandbox mode a session\n * was originally started with — a read-only-started session's write\n * SUCCEEDED on a bare resume with no sandbox override re-passed,\n * silently falling back to this machine's own ambient config default.\n * - This task's own brief assumed SIGINT for `interrupt()`; empirically,\n * `codex exec` ignores SIGINT entirely (a 60s `sleep` ran to completion\n * despite SIGINT at t=4s) — SIGTERM is used instead (confirmed to work:\n * immediate exit, no orphaned children, thread stays resumable after).\n *\n * See `./permission-mapping.ts` and `./process-runner.ts` for the full\n * per-finding writeups (sandbox scope, network, approval model, resume\n * mechanics, stdin handling).\n *\n * Architecture, and how it differs from pi: pi is one long-lived `pi --mode\n * rpc` process for a whole session's lifetime, driven by a bidirectional\n * JSONL request/response protocol (`../pi/rpc-client.ts`). `codex exec` has\n * no such thing — it's a one-shot batch process per turn, prompt in via\n * argv, JSONL out via stdout, process exits. `CodexSession` here instead\n * spawns a fresh `CodexProcessRunner` for every turn (the initial `start()`\n * and every later `followUp()`), and forwards each one's mapped events into\n * one shared, session-lifetime `AsyncQueue` — the thing `Session.events`\n * actually exposes. `sessionRef` is codex's own `thread_id`, learned from\n * `thread.started`, which is reliably the first JSONL line codex ever prints\n * (confirmed across every empirical capture, fresh starts and resumes\n * alike) — `runCodexTurn` below awaits specifically for that line before\n * resolving, mirroring pi's own \"resolve a real session id before\n * constructing the Session, fail closed if you can't\" discipline\n * (`../pi/pi-adapter.ts`'s `resolveFreshSessionId`, finding F8).\n */\nexport class CodexAdapter implements RuntimeAdapter {\n readonly id = 'codex';\n\n constructor(private readonly options: CodexAdapterOptions = {}) {}\n\n async detect(): Promise<RuntimeDetectResult> {\n const bin = this.resolveBin();\n try {\n const versionResult = await execFileAsync(bin.command, ['--version'], { timeout: DETECT_TIMEOUT_MS });\n const version = versionResult.stdout.trim() || versionResult.stderr.trim();\n const authPresent = await this.probeAuthPresent(bin);\n return { present: true, version, authPresent };\n } catch {\n return { present: false };\n }\n }\n\n /**\n * `authPresent` without ever reading `~/.codex/auth.json` (credential-\n * isolation rule, `../../types.ts`): spawns codex's OWN `login status`\n * subcommand and interprets its human-readable report — the exact\n * \"non-secret signal\" this adapter is required to use, and cleaner than\n * pi's env-var-name check since codex's real credential model (on the\n * reference machine) is a ChatGPT OAuth session, not an env var.\n *\n * Two independently-verified channel gotchas apply here, the \"pi lesson\"\n * yet again:\n * - `codex login status`'s human-readable \"Logged in using ChatGPT\"\n * message prints on STDERR, not stdout — both streams are checked\n * here for exactly that reason. pi's `--version` is the same class of\n * hazard from the other direction: its channel has moved between pi\n * releases (see ../pi/pi-adapter.ts), so neither stream is assumed.\n * - The NOT-logged-in message/exit-code shape was deliberately never\n * empirically tested: this machine has a real, live ChatGPT login, and\n * running `codex logout` to observe the negative case would have\n * broken that login for the rest of this session/machine. The match\n * below is intentionally conservative (`/logged in (using|with)/i`,\n * not a bare `\"logged in\"` substring) specifically because a bare\n * substring check would false-positive on a plausible negative message\n * like \"Not logged in\" (itself containing the substring \"logged in\").\n * This is a documented, known gap — flagged for M2-c / a follow-up\n * empirical pass on a logged-out machine, not asserted as verified.\n */\n private async probeAuthPresent(bin: ResolvedBin): Promise<boolean> {\n try {\n const result = await execFileAsync(bin.command, ['login', 'status'], { timeout: DETECT_TIMEOUT_MS });\n return /logged in (using|with)/i.test(`${result.stdout}\\n${result.stderr}`);\n } catch (err) {\n const withStreams = err as { stdout?: string; stderr?: string };\n return /logged in (using|with)/i.test(`${withStreams.stdout ?? ''}\\n${withStreams.stderr ?? ''}`);\n }\n }\n\n capabilities(): RuntimeCapabilities {\n // `approvalInteractive: false` — `codex exec` never emits a\n // `needs_approval`-equivalent event, so there is nothing to resume and\n // `CodexSession.resolveApproval` throws unconditionally.\n return { steer: false, resume: true, approvalInteractive: false, permissionModes: ['auto', 'readonly'] };\n }\n\n /**\n * M5: same deliberate posture as the claude adapter (see its own doc\n * comment) — codex authenticates via its own `codex login`-managed\n * ChatGPT OAuth session (`probeAuthPresent` above), not an env var, so\n * there is no credential env var this adapter needs forwarded; env-based\n * API-key passthrough remains a separate, pending product decision. No\n * `baseNames` either: nothing in this adapter reads a codex-specific\n * config-discovery variable (e.g. `CODEX_HOME`) today.\n */\n environmentRequirements(): RuntimeEnvironmentRequirements {\n return { credentialNames: [] };\n }\n\n async start(task: TaskOfferPayload, ctx: TaskContext): Promise<Session> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('codex adapter only supports string instructions in M2 (no blob-ref fetch yet)');\n }\n\n const mapping = mapPermissionPolicyToCodexArgs(ctx.policy);\n if (!mapping.ok) {\n throw new PolicyUnsupportedError(mapping.reason ?? 'policy rejected by codex adapter');\n }\n\n const bin = this.resolveBin();\n const queue = new AsyncQueue<AgentEvent>();\n const recordUnmapped = makeUnmappedFrameRecorder(new Map<string, number>());\n\n // Realpath'd once, up front — see `resolveRealWorkspaceDir`'s doc\n // comment for why this matters even on a single-machine, non-adversarial\n // path (unlike task-runner.ts's own realpath use, which is defending\n // against symlink swaps): `file_change` items report codex's own\n // absolute paths, built from the CHILD PROCESS's `process.cwd()`, which\n // Node/the OS resolve through any symlink in `ctx.workspaceDir` — a\n // stock `os.tmpdir()`-based workspace on macOS (`/var/folders/...`,\n // itself a symlink to `/private/var/folders/...`) hits this on literally\n // every run, not just a contrived edge case.\n const workspaceDir = await resolveRealWorkspaceDir(ctx.workspaceDir);\n\n const { sessionRef, runner } = await runCodexTurn({\n command: bin.command,\n resumeRef: task.sessionRef,\n instruction: task.instruction,\n policyArgs: mapping.args,\n cwd: ctx.workspaceDir,\n env: ctx.env,\n spawnFn: this.options.spawnFn,\n workspaceDir,\n queue,\n recordUnmapped,\n expectedSessionRef: task.sessionRef,\n preparedGit: ctx.gitWorkspace !== undefined,\n });\n\n return new CodexSession({\n sessionRef,\n command: bin.command,\n workspaceDir,\n env: ctx.env,\n spawnFn: this.options.spawnFn,\n queue,\n recordUnmapped,\n initialRunner: runner,\n preparedGit: ctx.gitWorkspace !== undefined,\n });\n }\n\n private resolveBin(): ResolvedBin {\n return (this.options.resolveBin ?? resolveCodexBin)();\n }\n}\n\n/**\n * Self-discovered finding while building this adapter's own test suite\n * (caught by a real timeout, not inspection): `fs.mkdtemp(path.join(os.\n * tmpdir(), ...))` on macOS returns a path through `/var/folders/...`, which\n * is itself a symlink to `/private/var/folders/...` — confirmed with a\n * direct probe (`fs.realpathSync(os.tmpdir())`). A child process spawned\n * with `cwd` set to the *symlinked* form still reports the *realpath'd* form\n * from its own `process.cwd()` (confirmed with a dedicated spawn probe) —\n * and `file_change` items build their absolute `path` from exactly that.\n * Without this, `events.ts`'s `extractArtifactEvents` would compute a\n * `path.relative(workspaceDir, absolutePath)` full of `../` segments for a\n * file genuinely inside the workspace, trip its own outside-the-workspace\n * safety check, and silently drop the artifact event — not a contrived edge\n * case, but the literal default shape of any workspace root built from\n * `os.tmpdir()` on macOS. Mirrors task-runner.ts's own `openArtifact`, which\n * realpaths its workspace root for the identical reason (there, defending\n * against a symlink swap; here, just matching what the child process's own\n * OS-resolved cwd will report) — same fallback too: a realpath failure\n * (workspace deleted out from under it, etc.) falls through to the original\n * path unchanged rather than failing the whole turn over it.\n */\nasync function resolveRealWorkspaceDir(workspaceDir: string): Promise<string> {\n return fs.realpath(workspaceDir).catch(() => workspaceDir);\n}\n\n/** Count of codex frame \"keys\" (see `unmappedFrameKey`) this session has told us have no `AgentEvent` mapping and aren't routine bookkeeping — i.e. genuinely unexpected traffic. Logs once per distinct key, mirroring `PiRpcClient.recordUnmappedFrame`'s exact reasoning: this is the mechanism that turns a future regression (like this task's own root-cause \"read `thread.started` on every line, not just the first\" class of bug would have been) into an immediate, self-diagnosing warning instead of a silent drop. */\nfunction makeUnmappedFrameRecorder(counts: Map<string, number>): (key: string) => void {\n return (key: string): void => {\n const next = (counts.get(key) ?? 0) + 1;\n counts.set(key, next);\n if (next === 1) {\n console.warn(\n `[byok/codex-adapter] codex emitted a frame with no AgentEvent mapping: \"${key}\" (further occurrences of this type won't be logged individually)`,\n );\n }\n };\n}\n\ninterface RunTurnParams {\n command: string;\n resumeRef: string | undefined;\n instruction: string;\n policyArgs: string[];\n cwd: string;\n env: NodeJS.ProcessEnv;\n spawnFn: SpawnFn | undefined;\n workspaceDir: string;\n queue: AsyncQueue<AgentEvent>;\n recordUnmapped: (key: string) => void;\n /**\n * When resuming, the sessionRef this call expects codex to echo back in\n * `thread.started` — required for BOTH `start()`'s externally-supplied\n * `task.sessionRef` and `followUp()`'s own previously-recorded\n * `sessionRef` (see `CodexSession.followUp`'s doc comment for why the\n * latter also passes this now). Fail-closed on a mismatch: the\n * already-spawned runner for this attempt is killed and the call throws\n * rather than silently adopting whatever id codex actually reported —\n * this is a real check, not just a diagnostic log.\n */\n expectedSessionRef?: string | undefined;\n preparedGit?: boolean;\n}\n\ninterface RunTurnResult {\n sessionRef: string;\n runner: CodexProcessRunner;\n}\n\n/**\n * `--skip-git-repo-check` is used for plain workspaces, not derived from policy:\n * real `codex exec` (confirmed live, via this adapter's own e2e run against a\n * plain non-git scratch workspace) refuses to run at all outside a Git\n * repository — \"Not inside a trusted directory and --skip-git-repo-check\n * was not specified\", exit 1, no JSONL emitted — unless this flag is passed.\n * A successfully prepared Git workspace already satisfies that precondition,\n * so the flag is omitted there. `task-runner.ts`'s `resolveWorkspaceDir`\n * creates plain directories (`fs.mkdir(dir, {recursive:true})`, never a git\n * repo), so the plain-workspace path must retain the flag. It carries no\n * sandbox/approval semantics of its own (purely a \"do you want the git-repo\n * safety net\" gate) — confirmed via `codex exec --help`, which documents it\n * separately from every sandbox/approval flag.\n */\nfunction buildArgv(resumeRef: string | undefined, policyArgs: string[], instruction: string, preparedGit = false): string[] {\n const base = resumeRef !== undefined ? ['exec', 'resume', resumeRef] : ['exec'];\n return [...base, '--json', ...(preparedGit ? [] : ['--skip-git-repo-check']), ...policyArgs, instruction];\n}\n\n/**\n * Spawn one `codex exec`/`codex exec resume` turn, await its first line\n * (must be `thread.started` — fail closed otherwise, never fabricate a\n * `sessionRef`, mirroring `../pi/pi-adapter.ts`'s finding F8), then keep\n * pumping the rest of that process's output into `params.queue` in the\n * background for as long as it runs. Shared by both `CodexAdapter.start()`\n * (a fresh turn, or a resume-via-`task.sessionRef`) and\n * `CodexSession.followUp()` (always a resume).\n */\nasync function runCodexTurn(params: RunTurnParams): Promise<RunTurnResult> {\n const argv = buildArgv(params.resumeRef, params.policyArgs, params.instruction, params.preparedGit);\n\n let firstLineSettled = false;\n let resolveFirstLine!: (ref: string) => void;\n let rejectFirstLine!: (err: Error) => void;\n const firstLine = new Promise<string>((resolve, reject) => {\n resolveFirstLine = resolve;\n rejectFirstLine = reject;\n });\n\n // Set once THIS turn's own mapped events include an explicit `turn_end` —\n // see the `waitClosed()` watcher below (cross-model review finding, the\n // \"pi-hang class\") for why this matters: `turn_end` already told\n // task-runner.ts's `pump()` this specific turn is done, so the shared\n // session-lifetime queue must stay open afterward (a later `followUp()`\n // reuses this exact same queue — see `CodexSession` below).\n let turnEnded = false;\n\n const runner = new CodexProcessRunner({\n command: params.command,\n args: argv,\n cwd: params.cwd,\n env: params.env,\n spawnFn: params.spawnFn,\n onEvent: (evt: CodexRawEvent) => {\n if (!firstLineSettled) {\n firstLineSettled = true;\n if (evt.type === 'thread.started' && typeof evt.thread_id === 'string' && evt.thread_id.length > 0) {\n resolveFirstLine(evt.thread_id);\n } else {\n rejectFirstLine(\n new Error(`codex did not yield thread.started as its first event (got ${JSON.stringify(evt).slice(0, 200)})`),\n );\n }\n return;\n }\n const mapped = mapCodexEventToAgentEvents(evt, params.workspaceDir);\n for (const agentEvent of mapped) {\n if (agentEvent.type === 'turn_end') turnEnded = true;\n params.queue.push(agentEvent);\n }\n if (mapped.length === 0 && !isRoutineCodexEvent(evt)) {\n params.recordUnmapped(unmappedFrameKey(evt));\n }\n },\n });\n\n /**\n * Cross-model review finding (the \"pi-hang class\" — a task that never\n * completes): `codex exec` is one-shot PER TURN (see this module's own\n * doc comment) — a failed turn (`turn.failed`, mapped to an `error`\n * AgentEvent, deliberately NOT a `turn_end`) or any other unexpected exit\n * (crash, an `interrupt()`-triggered SIGTERM) ends the child process\n * without ever producing the one signal task-runner.ts's `pump()` treats\n * as terminal. Nothing else in this file ever called `params.queue.end()`\n * for that case — confirmed reproducible: the events async-iterable's\n * `next()` stays pending forever, 250ms+ after the process has already\n * exited. pi's own `PiRpcClient.onClosed` (`../pi/rpc-client.ts`)\n * unconditionally ends its eventQueue on process close — correct THERE\n * because pi is one long-lived process for a WHOLE session (closing means\n * the whole session is over). Codex is architecturally different: a fresh\n * process per turn, sharing ONE session-lifetime queue across turns (see\n * `CodexSession.followUp()`) — unconditionally ending it here would wrongly\n * terminate a session a later `followUp()` is about to keep using. So:\n * only when THIS turn never reached `turn_end` do we surface a terminal\n * `error` AgentEvent (exit code/signal + stderr ring tail via\n * `buildExitError` — the real reason, never fabricated) and end the queue.\n * `waitClosed()` never rejects (see process-runner.ts), so no `.catch()`\n * is needed; pushing/ending an already-ended queue (e.g. `close()` beat\n * this watcher to it) is a harmless no-op (`AsyncQueue` is idempotent).\n */\n void runner.waitClosed().then(() => {\n if (turnEnded) return;\n params.queue.push({ type: 'error', message: runner.buildExitError('codex exited without completing the turn').message });\n params.queue.end();\n });\n\n // Race the first line against the process closing before ever producing\n // one (bad flag, ENOENT, an unknown resume id — \"Error: thread/resume:\n // ... no rollout found for thread id ...\" — all empirically confirmed to\n // exit fast with no JSONL at all, never a hang). Built as an explicit\n // executor rather than `Promise.race` so the \"losing\" side can never\n // become an unhandled rejection: `runner.waitClosed()` itself only ever\n // resolves (see process-runner.ts), and `firstLine` always has its\n // resolve/reject consumed below regardless of which settles first.\n let sessionRef: string;\n try {\n sessionRef = await new Promise<string>((resolve, reject) => {\n let settled = false;\n firstLine.then(\n (ref) => {\n if (!settled) {\n settled = true;\n resolve(ref);\n }\n },\n (err: unknown) => {\n if (!settled) {\n settled = true;\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n },\n );\n void runner.waitClosed().then(() => {\n if (!settled) {\n settled = true;\n reject(runner.buildExitError('codex exited before yielding an authoritative thread id'));\n }\n });\n });\n } catch (err) {\n runner.kill();\n throw err;\n }\n\n // Cross-model review finding: this used to only `console.warn` and\n // continue on a mismatch — \"a runtime silently falling back to a\n // different session runs the task in the WRONG context.\" `expectedSessionRef`\n // here is always an EXTERNALLY supplied ask (`CodexAdapter.start()`'s\n // `task.sessionRef`, from the server/daemon — never `CodexSession\n // .followUp()`, which intentionally omits it; see that method's own doc\n // comment for why its own internal resume is handled differently). A\n // mismatch against an external expectation means this adapter can no\n // longer trust that codex resumed the workspace/history the caller\n // actually meant — fail closed rather than quietly proceeding.\n if (params.expectedSessionRef !== undefined && sessionRef !== params.expectedSessionRef) {\n runner.kill();\n throw new Error(\n `codex exec resume echoed a different thread id than requested (requested ${params.expectedSessionRef}, got ${sessionRef}) — refusing to continue in a possibly-wrong session (fail-closed)`,\n );\n }\n\n return { sessionRef, runner };\n}\n\ninterface CodexSessionOptions {\n sessionRef: string;\n command: string;\n workspaceDir: string;\n env: NodeJS.ProcessEnv;\n spawnFn: SpawnFn | undefined;\n queue: AsyncQueue<AgentEvent>;\n recordUnmapped: (key: string) => void;\n initialRunner: CodexProcessRunner;\n preparedGit: boolean;\n}\n\nclass CodexSession implements Session {\n /**\n * NOT `readonly` (cross-model review finding): `followUp()` below\n * re-assigns this from the runtime's own CONFIRMED reflected id on each\n * resume — see its own doc comment. Silently keeping the value captured at\n * construction time was the original bug (the NEW id `runCodexTurn`\n * returns was discarded, so every later `followUp()` kept resuming the\n * OLD, potentially stale, id); that stays fixed here. A LATER cross-model\n * re-review found the fix above was itself incomplete: `followUp()` now\n * also verifies the reflected id matches what it asked to resume BEFORE\n * this field is touched — a mismatch throws (fail-closed) instead of ever\n * reaching this assignment, so `sessionRef` only ever advances to an id\n * codex has proven it actually resumed, never one it silently swapped in.\n */\n public sessionRef: string;\n private readonly command: string;\n private readonly workspaceDir: string;\n private readonly env: NodeJS.ProcessEnv;\n private readonly spawnFn: SpawnFn | undefined;\n private readonly queue: AsyncQueue<AgentEvent>;\n private readonly recordUnmapped: (key: string) => void;\n private readonly preparedGit: boolean;\n private currentRunner: CodexProcessRunner | undefined;\n private closed = false;\n\n constructor(options: CodexSessionOptions) {\n this.sessionRef = options.sessionRef;\n this.command = options.command;\n this.workspaceDir = options.workspaceDir;\n this.env = options.env;\n this.spawnFn = options.spawnFn;\n this.queue = options.queue;\n this.recordUnmapped = options.recordUnmapped;\n this.preparedGit = options.preparedGit;\n this.currentRunner = options.initialRunner;\n void this.forgetRunnerOnceClosed(options.initialRunner);\n }\n\n get events(): AsyncIterable<AgentEvent> {\n return this.queue;\n }\n\n private async forgetRunnerOnceClosed(runner: CodexProcessRunner): Promise<void> {\n await runner.waitClosed();\n if (this.currentRunner === runner) this.currentRunner = undefined;\n }\n\n /**\n * New turn, same session, via the real resume mechanism (`codex exec\n * resume <sessionRef> ...`). Maps `task.policy` FRESH on every call — a\n * deliberate, evidence-based divergence from pi's own `followUp()`\n * (`../pi/pi-adapter.ts`), which ignores `task.policy` entirely and relies\n * on argv baked in once at `start()`. That's safe for pi only because pi\n * reuses one already-running RPC process for its whole session lifetime,\n * so there is nothing to re-apply. codex has no such invariant: a resume\n * spawns a BRAND NEW process, and empirically that new process does NOT\n * inherit the sandbox mode the session originally started with (see this\n * file's module doc comment) — omitting a fresh, explicit mapping here\n * would silently run the follow-up turn under this machine's ambient codex\n * config default instead of the policy this specific follow-up was\n * offered under, exactly the silent-widen failure mode this adapter exists\n * to prevent.\n *\n * Cross-model RE-review finding (this corrects the previous wave's own\n * reasoning, quoted below for context): `expectedSessionRef` IS now passed\n * to `runCodexTurn`, set to the exact id this call asked to resume\n * (`resumeRef`, captured up front before the call). The previous version\n * of this method deliberately omitted it — \"this resume targets OUR OWN\n * previously-recorded `sessionRef`, not an externally supplied server\n * expectation crossing a trust boundary... whatever id codex reports back\n * for THIS resume is unambiguously the current, authoritative id for this\n * session\" — but that reasoning let a resume-A/reports-B mismatch silently\n * MIGRATE this session's identity instead of failing closed: codex has no\n * documented contract for re-keying a thread on resume, so a mismatch here\n * must be treated as an error, exactly like `start()`'s own\n * `task.sessionRef` comparison (see `runCodexTurn`'s doc comment on that\n * check) — never silently adopted. `runCodexTurn` kills the (failed) new\n * runner and throws on a mismatch, BEFORE `sessionRef`/`currentRunner`\n * below are ever touched — so a thrown mismatch leaves this session in its\n * previous, still-good state rather than partially migrated. The\n * `sessionRef` return value is still captured and re-assigned below (the\n * ORIGINAL bug this fixes, one wave further back: the return value used to\n * be discarded entirely, so every later `followUp()` kept resuming a\n * stale id even after codex had moved on) — it just can now only ever be\n * the SAME id this call asked to resume, never a silently-different one.\n */\n async followUp(task: TaskOfferPayload): Promise<void> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('codex adapter only supports string instructions in M2 (no blob-ref fetch yet)');\n }\n if (this.closed) {\n throw new Error('cannot follow up on a closed codex session');\n }\n\n const mapping = mapPermissionPolicyToCodexArgs(task.policy);\n if (!mapping.ok) {\n throw new PolicyUnsupportedError(mapping.reason ?? 'policy rejected by codex adapter');\n }\n\n const resumeRef = this.sessionRef;\n let sessionRef: string;\n let runner: CodexProcessRunner;\n try {\n ({ sessionRef, runner } = await runCodexTurn({\n command: this.command,\n resumeRef,\n instruction: task.instruction,\n policyArgs: mapping.args,\n cwd: this.workspaceDir,\n env: this.env,\n spawnFn: this.spawnFn,\n workspaceDir: this.workspaceDir,\n queue: this.queue,\n recordUnmapped: this.recordUnmapped,\n expectedSessionRef: resumeRef,\n preparedGit: this.preparedGit,\n }));\n } catch (err) {\n // Fix (queue-leak-on-failure): confirmed empirically with a diagnostic\n // unbounded `for await` over `session.events` — the SAME shared queue\n // this method's own `runCodexTurn` call already writes into is exposed\n // to the caller from before this call even started (`this.queue` ===\n // `session.events`, session-lifetime). `runCodexTurn` already kills the\n // FAILED attempt's own runner before throwing in both its failure\n // branches (a bad/missing first frame, and this exact session-identity\n // mismatch — see its own doc comment), but never touches `this.queue`.\n // The identity-mismatch case in particular routinely reaches\n // `turn.completed` (mapped to `turn_end`) on the wire BEFORE the\n // mismatch is even detected — this turn's own `onEvent` callback keeps\n // draining the child's stdout independently of when `runCodexTurn`'s\n // own promise settles, and a synchronous fixture's whole output can\n // arrive in one 'data' chunk, processed before the \"await sessionRef\"\n // continuation ever runs. That means `runCodexTurn`'s own \"this turn\n // never reached turn_end\" background watcher sees `turnEnded === true`\n // and correctly declines to end the queue itself (see that watcher's\n // doc comment: a later `followUp()` reusing this same queue is the\n // normal case it's protecting) — leaving NOTHING that will ever end\n // it, so a consumer that reads past everything already buffered gets a\n // `next()` that never resolves. Ending it here closes that gap,\n // mirroring `close()`'s own `this.queue.end()` — deliberately NOT\n // setting `this.closed`, since a failed followUp() must not corrode\n // the SESSION itself (a later `followUp()` with a matching id still\n // succeeds — see the cross-model re-review test above); only this\n // queue's usefulness ends, since nothing can safely keep sharing it\n // once a turn's own result has been this explicitly distrusted.\n this.queue.end();\n throw err;\n }\n this.sessionRef = sessionRef;\n this.currentRunner = runner;\n void this.forgetRunnerOnceClosed(runner);\n }\n\n /** Best-effort abort of the current turn. SIGTERM's the currently-running child, if any — see `process-runner.ts`'s `kill()` doc comment for why SIGTERM (not SIGINT) and why this is safe: the underlying codex thread survives and stays resumable, confirmed empirically. A no-op when no turn is currently in flight. */\n async interrupt(): Promise<void> {\n this.currentRunner?.kill();\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.currentRunner?.kill();\n this.queue.end();\n }\n\n /**\n * `codex exec` has no in-band channel to inject text into an already-\n * running turn — confirmed empirically, not assumed: there is no stdin\n * protocol (stdin is never even piped to the child — see\n * `process-runner.ts`'s module doc comment), SIGINT (a plausible\n * interrupt-and-redirect signal) is silently ignored outright, and `codex\n * exec resume` only ever starts a brand NEW turn strictly after the\n * current one has fully finished — it cannot inject into one still in\n * flight. Throws honestly rather than silently no-op-ing, matching this\n * task's explicit instruction and `capabilities().steer === false` above.\n */\n async steer(): Promise<void> {\n throw new SteerUnsupportedError(\n 'codex',\n 'codex adapter does not support steer: codex exec has no in-band channel to inject text into a running turn (no stdin protocol, SIGINT is ignored, and resume only starts a new turn after the current one finishes)',\n );\n }\n\n /**\n * `codex exec` never emits a `needs_approval`-equivalent event on the wire\n * — confirmed empirically (see `events.ts`'s module doc comment): a\n * sandbox-denied action resolves the approval decision internally with no\n * pause an external caller could ever observe or answer, regardless of\n * `approval_policy`. Since this session can therefore never have emitted a\n * `needs_approval` `AgentEvent` in the first place, a caller reaching this\n * method implies something upstream expected approval support that isn't\n * there — thrown as a descriptive error rather than a silent no-op,\n * mirroring `../pi/pi-adapter.ts`'s identical `resolveApproval`.\n */\n async resolveApproval(): Promise<void> {\n throw new Error(\n 'codex adapter does not support approval resume: codex exec never emits a needs_approval-equivalent event (sandbox-denied actions resolve internally with no wire-visible pause)',\n );\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/types.ts","../../src/adapters/pi/resolve-bin.ts","../../src/adapters/pi/permission-mapping.ts","../../src/adapters/pi/events.ts","../../src/util/async-queue.ts","../../src/adapters/pi/rpc-client.ts","../../src/adapters/provider-credential-environment.ts","../../src/adapters/pi/pi-adapter.ts","../../src/adapters/claude/resolve-bin.ts","../../src/adapters/claude/resolve-approval-mcp-bin.ts","../../src/adapters/claude/permission-mapping.ts","../../src/adapters/claude/events.ts","../../src/adapters/claude/process-client.ts","../../src/bin/approval-mcp-server.ts","../../src/adapters/claude/claude-adapter.ts","../../src/adapters/codex/resolve-bin.ts","../../src/adapters/codex/permission-mapping.ts","../../src/adapters/codex/events.ts","../../src/adapters/codex/process-runner.ts","../../src/adapters/codex/codex-adapter.ts"],"names":["path","fileURLToPath","READONLY_TOOLS","events","STDERR_RING_CAPACITY","spawn","spawnSync","execFileAsync","promisify","execFile","DETECT_TIMEOUT_MS","fs","toNonNegativeInt","guessContentType","subscriptionModel"],"mappings":"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","file":"index.js","sourcesContent":["import type { AgentEvent, PermissionPolicy, TaskOfferPayload } from '@byok-sdk/protocol';\nimport type { RuntimeEnvironmentRequirements } from './daemon/environment';\n\nexport type { RuntimeEnvironmentRequirements } from './daemon/environment';\n\nexport interface GitWorkspaceConfig {\n mode: 'local-checkpoints';\n}\n\n/**\n * Result of probing whether a runtime is usable on this machine. `authPresent`\n * is computed without ever reading the runtime's own credential storage (see\n * the credential-isolation rule on {@link RuntimeAdapter}) — it only reflects\n * whether a recognized environment variable name is set.\n */\nexport interface RuntimeDetectResult {\n present: boolean;\n version?: string;\n authPresent?: boolean;\n}\n\n/** What a runtime adapter can do, advertised so the daemon can pick/validate adapters. */\nexport interface RuntimeCapabilities {\n steer: boolean;\n resume: boolean;\n /**\n * Whether this adapter can project task-scoped, locally configured MCP\n * servers into the runtime without accepting executable definitions from\n * the remote task. Omission is fail-closed and means unsupported.\n */\n mcpToolsets?: boolean;\n /**\n * Whether this adapter can genuinely pause a running session on\n * `needs_approval` and resume it from an out-of-band decision — i.e.\n * whether {@link Session.resolveApproval} really resolves rather than\n * throwing. This is the ONLY source of truth for the wire's\n * `RuntimeInfo.capabilities.approvalInteractive` (`daemon/\n * create-daemon.ts`'s `toRuntimeInfoCapabilities`); the daemon no longer\n * hardcodes a value.\n *\n * Required, deliberately: a new adapter (or a test fake) that forgets to\n * declare it fails to compile rather than silently defaulting to a claim\n * it cannot back.\n */\n approvalInteractive: boolean;\n /** Subset of {@link PermissionPolicy}'s `mode` values this adapter can express without widening. */\n permissionModes: string[];\n}\n\n/** One local stdio MCP server definition. Remote task payloads can never supply this shape. */\nexport interface McpStdioServerConfig {\n command: string;\n args?: readonly string[];\n}\n\n/** A logical group of local MCP servers selectable by a wire-level toolset id. */\nexport interface McpToolsetConfig {\n mcpServers: Readonly<Record<string, McpStdioServerConfig>>;\n}\n\n/**\n * M4 Phase 3: the out-of-band approval channel `TaskRunner` (`daemon/\n * task-runner.ts`) hands to an adapter's `start()` via `TaskContext\n * .approvalChannel`, for a runtime whose approval mechanism genuinely needs\n * to reach back into the daemon from OUTSIDE the adapter's own process — the\n * claude adapter's concrete case: `claude`'s `--permission-prompt-tool`\n * resolves a pending permission entirely inside a SEPARATE MCP-server child\n * process claude itself spawns (see `bin/byok-approval-mcp.ts`), which has\n * no in-process handle to this task's `Session` at all and must instead call\n * back into the SAME daemon over its control socket. `storeDir`/`productId`\n * are exactly what that out-of-process helper needs to find and authenticate\n * against this daemon's control socket (`daemon/control-protocol.ts`\n * `controlEndpointPath`/`controlTokenPath`); `taskId` is how its request gets\n * correlated back to THIS task once it arrives. `resolve()` is the\n * daemon-side counterpart: it resolves the single most-recently-registered\n * pending approval for this task (via `TaskRunner.requestApproval`'s own\n * `ApprovalRegistry` entry — see `daemon/approvals.ts`), and rejects if none\n * is currently pending, mirroring `Session.resolveApproval`'s own\n * no-notion-of-approval-pending fail-closed contract one level up.\n *\n * Optional and adapter-agnostic on purpose: only an adapter whose runtime\n * genuinely supports an out-of-band pause (claude, today) ever reads this;\n * every other adapter (pi, codex) ignores it exactly as before this field\n * existed.\n */\nexport interface ApprovalChannel {\n taskId: string;\n storeDir: string;\n productId: string;\n /** Default wait (ms) before the daemon force-resolves an unanswered approval request as a fail-closed rejection — see `TaskRunner.requestApproval`. */\n timeoutMs: number;\n /** Resolve the single currently-pending out-of-band approval for this task. Rejects if none is pending right now. */\n resolve(approved: boolean, reason?: string): Promise<void>;\n}\n\n/**\n * Per-task execution context handed to {@link RuntimeAdapter.start}. `policy`\n * is the already fail-closed-checked *effective* policy (offer policy merged\n * against the daemon's configured ceiling) — the adapter must obey this, not\n * whatever the raw task offer's own `policy` field said.\n */\nexport interface TaskContext {\n workspaceDir: string;\n policy: PermissionPolicy;\n env: NodeJS.ProcessEnv;\n /**\n * MCP servers resolved from this device's local registry for this task.\n * The wire carries only logical toolset ids; command/args never originate\n * from the SaaS task and are never copied into the task instruction.\n */\n mcpServers?: Readonly<Record<string, McpStdioServerConfig>>;\n /** Prepared local checkpoint repository metadata; absent for legacy plain workspaces. */\n gitWorkspace?: {\n workspaceId: string;\n baseline?: string;\n };\n /** M4 Phase 3 — see {@link ApprovalChannel}. Optional/adapter-agnostic: unset for every adapter that never requests an out-of-band approval. */\n approvalChannel?: ApprovalChannel;\n}\n\n/**\n * A running (or resumable) unit of work on a runtime. One `Session` maps to\n * one underlying runtime process/session for the lifetime of a task.\n */\nexport interface Session {\n /** Opaque runtime session id, reported back to the server via `task.complete.sessionRef`. */\n sessionRef: string;\n /** Normalized events for this session; the daemon batches these into `task.progress`. */\n events: AsyncIterable<AgentEvent>;\n /** Inject steering text into a running turn (mid-stream). */\n steer(text: string): Promise<void>;\n /** Send a new instruction on the same session after it has gone idle. */\n followUp(task: TaskOfferPayload): Promise<void>;\n /** Best-effort abort of the current turn (used for `task.cancel`). */\n interrupt(): Promise<void>;\n /** Tear down the underlying runtime process/session. Idempotent. */\n close(): Promise<void>;\n /**\n * Resolve a session paused on `needs_approval` (protocol §5). The\n * server's own state has already moved by the time this is called (§4 —\n * `task.approve`/`task.reject` are best-effort notifications, not\n * requests awaiting a reply): `approved: true` must make the session\n * resume producing events (`task.progress` continuing is the proof);\n * `approved: false` means the caller will immediately follow up with\n * `interrupt()` + `close()` and report `task.fail` — an adapter that has\n * no notion of `needs_approval` at all (i.e. never emits one) should\n * throw a descriptive error here rather than silently no-op, since a\n * caller receiving `task.approve`/`task.reject` for one of its tasks\n * implies something upstream expected approval support that isn't there.\n */\n resolveApproval(approved: boolean, reason?: string): Promise<void>;\n}\n\n/**\n * Uniform seam every concrete runtime (pi now; claude/codex in M2) implements.\n *\n * Credential-isolation rule: an adapter spawns only the runtime's official\n * binary. It never reads, proxies, or forwards that runtime's own credential\n * storage (OAuth tokens, API keys on disk, `~/.claude`, `~/.codex`, `~/.pi`\n * auth state, etc). Presence checks are limited to environment variable\n * *names* (see {@link RuntimeDetectResult.authPresent}).\n *\n * M5: separately, {@link RuntimeAdapter.environmentRequirements} below\n * declares which environment variable NAMES (never values inspected here\n * either) this adapter's runtime needs forwarded into its own spawned\n * process — see that method's own doc comment and `daemon/environment.ts`.\n */\nexport interface RuntimeAdapter {\n id: string;\n /**\n * Explicit opt-in to the authoritative `task.offer.dispatchSelection`\n * contract. A custom adapter using a built-in runtime id must set this only\n * when it validates and pins the exact lane/provider/model semantics; the\n * daemon will otherwise withhold the connection-level capability so a\n * server rejects the offer before sending it.\n */\n readonly supportsDispatchSelection?: true;\n detect(): Promise<RuntimeDetectResult>;\n capabilities(): RuntimeCapabilities;\n start(task: TaskOfferPayload, ctx: TaskContext): Promise<Session>;\n /**\n * M5: declares which environment variable names (exact, or `*`-suffixed\n * prefix) this runtime's own CLI needs beyond the always-included\n * platform baseline (`daemon/environment.ts`'s `buildRuntimeEnv`) —\n * `task-runner.ts` builds each task's `TaskContext.env` from this instead\n * of ever handing a spawned agent the daemon's raw `process.env` again.\n * Optional and fail-closed by omission: an adapter that doesn't implement\n * this gets the platform baseline ONLY, never an implicit \"everything.\"\n */\n environmentRequirements?(): RuntimeEnvironmentRequirements;\n}\n\n/**\n * Thrown by `RuntimeAdapter.start` when the task can never succeed on this\n * adapter as offered — an unsupported `PermissionPolicy` (fail-closed) or an\n * instruction shape the adapter can't handle (e.g. a blob-ref in M0) — as\n * opposed to a transient/environmental failure (spawn error, missing\n * credentials) that might succeed on a later retry. The daemon uses this\n * distinction to set `task.fail`'s `retryable` flag correctly instead of\n * treating every `start()` failure the same way.\n */\nexport class PolicyUnsupportedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PolicyUnsupportedError';\n }\n}\n\n/**\n * Thrown by {@link Session.steer} on an adapter whose runtime has no\n * mid-turn steering channel at all (`capabilities().steer === false`) — a\n * permanent property of the runtime, never a transient failure. Typed\n * rather than a bare `Error` so the daemon can classify an inbound\n * `task.steer` for such a runtime as non-retryable (record + ack, cursor\n * advances) instead of stalling the cursor on it forever, without matching\n * on message strings.\n */\nexport class SteerUnsupportedError extends Error {\n /** The `RuntimeAdapter.id` that cannot steer (e.g. `claude`, `codex`). */\n readonly runtimeId: string;\n\n constructor(runtimeId: string, message: string) {\n super(message);\n this.name = 'SteerUnsupportedError';\n this.runtimeId = runtimeId;\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * The pi coding-agent CLI's real npm package name.\n *\n * IMPORTANT (empirically verified 2026-07-16, see the M0-3 report): the name\n * `@mariozechner/pi` — the identifier this task was originally briefed with —\n * is NOT the coding agent. On npm it resolves to an unrelated \"CLI tool for\n * managing vLLM deployments on GPU pods\" (bin: `pi-pods`). The real coding\n * agent was `@mariozechner/pi-coding-agent`, which is now deprecated in\n * favor of this package. `package.json` carries the exact supported version\n * as a required dependency; pi is a core BYOK capability, not an optional\n * enhancement or an unversioned global executable.\n */\nexport const PI_PACKAGE_NAME = '@earendil-works/pi-coding-agent';\n\nexport interface ResolvedBin {\n command: string;\n source: 'package' | 'env';\n}\n\ninterface MinimalPackageJson {\n name?: string;\n bin?: string | Record<string, string>;\n}\n\nfunction readPackageJson(dir: string): MinimalPackageJson | undefined {\n const candidate = path.join(dir, 'package.json');\n if (!existsSync(candidate)) return undefined;\n try {\n return JSON.parse(readFileSync(candidate, 'utf8')) as MinimalPackageJson;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolve the pi CLI executable from the required package installed alongside\n * `@byok-sdk/client`. There is intentionally no automatic PATH fallback: a\n * global `pi` would create a second, unversioned authority for this contract.\n *\n * `BYOK_PI_BIN` explicitly overrides the package when set: `PiAdapterOptions.resolveBin`\n * is the injectable seam for in-process tests, but the `byok-agent` CLI bin\n * only ever constructs `new PiAdapter()` with no options (see `createDaemon`),\n * so an out-of-process substitution (e.g. examples/basic's e2e run swapping\n * in the fake-pi fixture, or a single-file product injecting its required\n * Node 22.19+ pi sidecar) has no other seam to use.\n *\n * Deliberately does NOT use `createRequire(...).resolve()`: this package is\n * pure ESM with no `require` export condition (`exports[\".\"]` only offers\n * `import`), so CJS-style resolution fails with\n * `ERR_PACKAGE_PATH_NOT_EXPORTED`. It also does NOT resolve the\n * `./package.json` subpath directly (also not exported); instead it resolves\n * the package's main entry via `import.meta.resolve` and walks upward to the\n * package root identified by its manifest name.\n */\nexport function resolvePiBin(): ResolvedBin {\n const override = process.env.BYOK_PI_BIN;\n if (override) {\n return { command: override, source: 'env' };\n }\n try {\n const mainEntryUrl = import.meta.resolve(PI_PACKAGE_NAME);\n let dir = path.dirname(fileURLToPath(mainEntryUrl));\n for (let depth = 0; depth < 6; depth++) {\n const pkg = readPackageJson(dir);\n if (pkg?.name === PI_PACKAGE_NAME) {\n const binRel = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.pi;\n if (binRel) {\n return { command: path.join(dir, binRel), source: 'package' };\n }\n break;\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n } catch (cause) {\n throw new Error(\n `Required ${PI_PACKAGE_NAME} could not be resolved; install @byok-sdk/client dependencies or set BYOK_PI_BIN to a Node 22.19+ pi sidecar`,\n { cause },\n );\n }\n throw new Error(\n `Required ${PI_PACKAGE_NAME} does not expose the pi CLI; reinstall the pinned dependency or set BYOK_PI_BIN to a Node 22.19+ pi sidecar`,\n );\n}\n","import type { PermissionPolicy } from '@byok-sdk/protocol';\n\nexport interface PiPermissionMapping {\n ok: boolean;\n /** CLI args to append to `pi --mode rpc ...`. Only meaningful when `ok` is true. */\n args: string[];\n /** Present when `ok` is false. */\n reason?: string;\n}\n\n/** pi's own read-only built-ins, per `pi --help`'s documented example: `pi --tools read,grep,find,ls -p ...`. */\nconst READONLY_TOOLS: readonly string[] = ['read', 'grep', 'find', 'ls'];\n\n/**\n * Map an effective {@link PermissionPolicy} to `pi --mode rpc` CLI args,\n * fail-closed against the exact pi 0.84.1 CLI contract:\n *\n * - `auto` / `readonly` are expressible via `--tools` / `--no-tools`.\n * - `confirm` and `plan` are NOT expressible: pi ships no built-in per-call\n * approval gate and explicitly \"skips ... plan mode\" (README); both exist\n * only as example third-party extensions (`examples/extensions/\n * permission-gate.ts`, `examples/extensions/plan-mode/`), which is\n * out-of-scope, speculative surface for M0.\n * - `network: false` is NOT expressible: pi has no network sandbox for its\n * bash tool (\"Pi does not include a built-in sandbox\" — docs/security.md).\n * `network: true` or unset proceeds, since nothing needs enforcing then.\n *\n * Workspace confinement is NOT a pi flag — the caller spawns pi with\n * `cwd: ctx.workspaceDir`, the daemon-created per-task directory.\n *\n * - `--tools` is an allowlist and `--exclude-tools` is a denylist. Passing\n * both lets pi remain the authority for its active tool registry instead\n * of duplicating pi's default tool list in this adapter.\n */\nexport function mapPermissionPolicyToPiArgs(policy: PermissionPolicy): PiPermissionMapping {\n if (policy.network === false) {\n return {\n ok: false,\n args: [],\n reason: 'policy requires network:false, which the pi adapter cannot enforce (pi has no network sandbox)',\n };\n }\n\n if (policy.mode === 'confirm' || policy.mode === 'plan') {\n return {\n ok: false,\n args: [],\n reason: `pi adapter cannot express permission mode \"${policy.mode}\" (no built-in per-call approval gate or plan-only mode without a custom extension)`,\n };\n }\n\n const denyTools = policy.denyTools ?? [];\n\n if (policy.mode === 'readonly') {\n const base = policy.allowTools ? policy.allowTools.filter((tool) => READONLY_TOOLS.includes(tool)) : [...READONLY_TOOLS];\n // Never fall through to an absent `--tools` flag here — that would run\n // pi's full default toolset, silently widening a readonly request.\n if (base.length === 0) return { ok: true, args: ['--no-tools'] };\n return {\n ok: true,\n args: ['--tools', base.join(','), ...(denyTools.length > 0 ? ['--exclude-tools', denyTools.join(',')] : [])],\n };\n }\n\n const args: string[] = [];\n if (policy.allowTools && policy.allowTools.length > 0) {\n args.push('--tools', policy.allowTools.join(','));\n }\n if (denyTools.length > 0) args.push('--exclude-tools', denyTools.join(','));\n\n return { ok: true, args };\n}\n","import type { AgentEvent } from '@byok-sdk/protocol';\nimport type { PiRpcMessage } from './rpc-client';\n\n/**\n * Map pi 0.84.1 RPC frames into BYOK's runtime-neutral event contract.\n *\n * `agent_settled` is the only whole-task completion authority. `agent_end`\n * ends one low-level agent run, but pi may still perform retry/compaction or\n * consume queued work before it emits `agent_settled`. Pi's own `turn_end`\n * is even narrower: a tool-using prompt can emit several of them. Mapping\n * either earlier boundary to BYOK `turn_end` would acknowledge the task\n * before the runtime is actually idle.\n *\n * `message_update` is delta-only in this contract. The mapper forwards text\n * deltas and never reads the removed cumulative `message`/`partial` fields.\n */\nexport function mapPiMessageToAgentEvent(msg: PiRpcMessage): AgentEvent | undefined {\n switch (msg.type) {\n case 'message_update': {\n const delta = msg.assistantMessageEvent as { type?: string; delta?: string } | undefined;\n if (delta?.type === 'text_delta' && typeof delta.delta === 'string') {\n return { type: 'progress', text: delta.delta };\n }\n return undefined;\n }\n\n case 'tool_execution_start': {\n if (typeof msg.toolName !== 'string') return undefined;\n return { type: 'tool_use', tool: msg.toolName, input: msg.args };\n }\n\n case 'tool_execution_end': {\n if (typeof msg.toolName !== 'string') return undefined;\n return {\n type: 'tool_result',\n tool: msg.toolName,\n output: { result: msg.result, isError: msg.isError === true },\n };\n }\n\n case 'agent_settled':\n return { type: 'turn_end' };\n\n /**\n * `artifact` is NOT a real pi RPC message — pi's own `write` tool only\n * ever surfaces as `tool_execution_start`/`tool_execution_end` (see\n * `docs/tools/write.js` in the installed package), and neither carries\n * enough on its own at the `_end` message (no `path`) for this stateless,\n * one-message-at-a-time mapper to correlate back to a written file\n * without introducing per-toolCallId state. This case exists purely so\n * the `fake-pi.mjs` test/e2e fixture (M1-4 blob-path acceptance run) has\n * a way to simulate \"the runtime wrote a file and is reporting it as an\n * artifact\" — real pi emits nothing today that reaches this branch, so\n * production traffic through this adapter never takes it. Revisit if a\n * future pi release adds a native artifact-producing message, or if this\n * needs correlating to a real `write` tool call.\n */\n case 'artifact': {\n if (typeof msg.name !== 'string' || typeof msg.contentType !== 'string') return undefined;\n return { type: 'artifact', name: msg.name, contentType: msg.contentType };\n }\n\n case 'extension_error':\n return { type: 'error', message: typeof msg.error === 'string' ? msg.error : 'pi extension error' };\n\n case 'auto_retry_end':\n if (msg.success === false) {\n return {\n type: 'error',\n message: typeof msg.finalError === 'string' ? msg.finalError : 'pi auto-retry exhausted',\n };\n }\n return undefined;\n\n // Routine pi session/turn/streaming bookkeeping with no `AgentEvent`\n // equivalent — see `ROUTINE_PI_EVENT_TYPES` below, which mirrors this\n // list so `PiSession`'s unmapped-frame accounting (rpc-client.ts's\n // `recordUnmappedFrame`) can tell \"known, expected, silently ignored\"\n // apart from \"genuinely never seen before\" (falls to `default` below).\n case 'agent_start':\n case 'agent_end': // one low-level run; `agent_settled` is BYOK completion\n case 'turn_start':\n case 'turn_end': // pi's own per-LLM-turn boundary, not ours\n case 'message_start':\n case 'message_end':\n case 'bash_execution_update':\n case 'tool_execution_update':\n case 'queue_update':\n case 'compaction_start':\n case 'compaction_end':\n case 'auto_retry_start':\n case 'summarization_retry_scheduled':\n case 'summarization_retry_attempt_start':\n case 'summarization_retry_finished':\n case 'session_info_changed':\n case 'thinking_level_changed':\n return undefined;\n\n default:\n return undefined;\n }\n}\n\n/**\n * Pi RPC message types that are routine, expected, and deliberately have no\n * `AgentEvent` equivalent (kept in sync with the switch cases above sharing\n * this comment). Used only for observability: `PiSession`'s event iterator\n * (pi-adapter.ts) calls `PiRpcClient.recordUnmappedFrame` for any message\n * type that maps to `undefined` AND isn't in this set — i.e. traffic nobody\n * has ever told this adapter to expect. That distinction is what makes a\n * regression like a changed completion event\n * self-diagnosing: a warning fires the first time the new/renamed settle\n * event shows up, instead of the daemon just quietly hanging. `default`-only\n * unknowns (a type not listed in the switch at all) are equally \"not\n * routine\" and get flagged the same way — this set exists so *routine*\n * traffic doesn't also trip that alarm on every single task.\n */\nexport const ROUTINE_PI_EVENT_TYPES: ReadonlySet<string> = new Set([\n 'agent_start',\n 'agent_end',\n 'turn_start',\n 'turn_end',\n 'message_start',\n 'message_end',\n 'bash_execution_update',\n 'tool_execution_update',\n 'queue_update',\n 'compaction_start',\n 'compaction_end',\n 'auto_retry_start',\n 'summarization_retry_scheduled',\n 'summarization_retry_attempt_start',\n 'summarization_retry_finished',\n 'session_info_changed',\n 'thinking_level_changed',\n]);\n","/**\n * A minimal push/pull async queue: producers call `push`/`end`, consumers\n * `for await` over the queue itself. Shared by the pi RPC client's event\n * stream, the claude/codex adapters' own event streams, and test fixtures\n * (stub sessions) so all of them get the same order-preserving semantics\n * without duplicating it.\n *\n * M4 hardening: this used to be genuinely back-pressure-free — an unbounded\n * internal `buffered` array, so a producer that outpaces its consumer (a\n * runaway/misbehaving runtime adapter, or simply nobody ever reading\n * `session.events`) could grow it without limit. Bounded now: `capacity`\n * (default {@link DEFAULT_ASYNC_QUEUE_CAPACITY}) caps how many items may sit\n * in `buffered` unconsumed. On overflow this fails fast rather than either of\n * the two worse alternatives — silently dropping the newest/oldest event (a\n * consumer would never know its view of the stream has a hole in it) or\n * blocking `push()` (every producer in this codebase calls `push()`\n * synchronously from an I/O callback; blocking there would stall reading the\n * underlying process/socket entirely). Instead, once `capacity` is exceeded,\n * the queue transitions into a permanent error state: every pending and\n * future consumer `next()` call rejects with an {@link AsyncQueueOverflowError}\n * naming the capacity, and all further `push()` calls are silently ignored\n * (mirrors the pre-existing `ended` no-op precedent — once terminal, always\n * terminal). The error state is checked before `ended`, so it wins even if\n * `end()` is called afterward — a caller must learn the stream broke, not\n * see a quiet, ordinary completion.\n */\nexport class AsyncQueueOverflowError extends Error {\n constructor(public readonly capacity: number) {\n super(\n `AsyncQueue exceeded its capacity of ${capacity} buffered item(s) — failing fast rather than growing unbounded, silently dropping events, or blocking push()`,\n );\n this.name = 'AsyncQueueOverflowError';\n }\n}\n\n/** Default `capacity` (see {@link AsyncQueue}'s own doc comment) when the constructor isn't given one — generous for every producer in this codebase (adapter event streams a consumer reads turn-by-turn), while still catching a genuinely runaway/unconsumed stream instead of growing forever. */\nexport const DEFAULT_ASYNC_QUEUE_CAPACITY = 10_000;\n\ninterface QueueWaiter<T> {\n resolve: (result: IteratorResult<T>) => void;\n reject: (err: Error) => void;\n}\n\nexport class AsyncQueue<T> implements AsyncIterable<T> {\n private readonly buffered: T[] = [];\n private readonly waiters: QueueWaiter<T>[] = [];\n private ended = false;\n private failure: Error | undefined;\n\n constructor(private readonly capacity: number = DEFAULT_ASYNC_QUEUE_CAPACITY) {}\n\n push(item: T): void {\n if (this.ended || this.failure) return;\n const waiter = this.waiters.shift();\n if (waiter) {\n // Handed directly to an already-waiting consumer — never touches\n // `buffered`, so this path can never itself cause an overflow.\n waiter.resolve({ value: item, done: false });\n return;\n }\n if (this.buffered.length >= this.capacity) {\n this.fail(new AsyncQueueOverflowError(this.capacity));\n return;\n }\n this.buffered.push(item);\n }\n\n end(): void {\n if (this.ended || this.failure) return;\n this.ended = true;\n for (const waiter of this.waiters.splice(0)) {\n waiter.resolve({ value: undefined as never, done: true });\n }\n }\n\n /** Transitions the queue into its permanent overflow state: rejects every currently-pending waiter and, from then on, every `next()` call (checked ahead of `ended` — see the class doc comment on why the error must win). */\n private fail(error: Error): void {\n if (this.ended || this.failure) return;\n this.failure = error;\n for (const waiter of this.waiters.splice(0)) {\n waiter.reject(error);\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.failure) {\n return Promise.reject(this.failure);\n }\n if (this.buffered.length > 0) {\n return Promise.resolve({ value: this.buffered.shift() as T, done: false });\n }\n if (this.ended) {\n return Promise.resolve({ value: undefined as never, done: true });\n }\n return new Promise((resolve, reject) => this.waiters.push({ resolve, reject }));\n },\n };\n }\n}\n","import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport { AsyncQueue } from '../../util/async-queue';\n\nexport type SpawnFn = typeof spawn;\n\n/**\n * A pi RPC-mode message: either a `{type:\"response\", id, success, ...}` reply\n * to a command we sent, or an unsolicited event/extension-UI-request. Field\n * shapes vary by `type` (see docs.md / this task's live probes), so this\n * stays a loose bag rather than a full discriminated union — M0 only needs\n * a handful of fields off of each.\n */\nexport interface PiRpcMessage {\n type: string;\n id?: string;\n [key: string]: unknown;\n}\n\nexport interface PiRpcClientOptions {\n command: string;\n args: string[];\n cwd: string;\n env: NodeJS.ProcessEnv;\n spawnFn?: SpawnFn;\n}\n\n/** Bound on retained stderr lines (see `onStderr`/`buildExitError`) — enough to show a real crash reason without an unbounded footprint for a long-lived process that's merely chatty on stderr. */\nconst STDERR_RING_CAPACITY = 20;\n\n/**\n * pi's 4 blocking \"dialog\" extension-UI methods (rpc.md's \"Extension UI\n * Protocol\": `select`/`confirm`/`input`/`editor` emit an\n * `extension_ui_request` and BLOCK pi's whole run until a matching\n * `extension_ui_response` arrives). The other 5 methods\n * (`notify`/`setStatus`/`setWidget`/`setTitle`/`set_editor_text`) are\n * fire-and-forget and must never get a reply (rpc.md: \"Responses are sent\n * for dialog methods only\") — see `respondToExtensionUiRequest`.\n */\nconst DIALOG_UI_METHODS = new Set(['select', 'confirm', 'input', 'editor']);\n\n/**\n * JSONL request/response + event-stream client for `pi --mode rpc`.\n *\n * Framing per pi's own docs/rpc.md: strict JSONL, LF (`\\n`) as the only\n * record delimiter (a trailing `\\r` is stripped; this deliberately does NOT\n * use `node:readline`, which pi's docs call out as non-compliant because it\n * also splits on U+2028/U+2029 — valid inside JSON strings).\n *\n * Responses are correlated by `id`, never by arrival order: empirically,\n * pi's responses do not preserve request order (an immediate parse-failure\n * response can overtake a slower in-flight command's response).\n */\nexport class PiRpcClient {\n private readonly child: ChildProcessWithoutNullStreams;\n private buffer = '';\n private nextId = 1;\n private readonly pending = new Map<\n string,\n { resolve: (msg: PiRpcMessage) => void; reject: (err: Error) => void }\n >();\n private readonly eventQueue = new AsyncQueue<PiRpcMessage>();\n private closed = false;\n private exitError: Error | undefined;\n /** Bounded tail of recent stderr lines — pi discarded this entirely before (nothing ever read `child.stderr`), which is exactly why finding #1 (`Error: Unknown option: --session-id`, exit 1) had to be root-caused by hand instead of reading it off a thrown error. See `buildExitError`. */\n private readonly stderrRing: string[] = [];\n /** Count of pi RPC message types `PiSession` (pi-adapter.ts) has told us have no `AgentEvent` mapping and aren't routine bookkeeping — see `recordUnmappedFrame`. */\n private readonly unmappedFrameCounts = new Map<string, number>();\n\n constructor(options: PiRpcClientOptions) {\n const spawnFn = options.spawnFn ?? spawn;\n this.child = spawnFn(options.command, options.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: ['pipe', 'pipe', 'pipe'],\n }) as ChildProcessWithoutNullStreams;\n\n this.child.stdout.setEncoding('utf8');\n this.child.stdout.on('data', (chunk: string) => this.onData(chunk));\n\n this.child.stderr.setEncoding('utf8');\n this.child.stderr.on('data', (chunk: string) => this.onStderr(chunk));\n\n // `close` (not `exit`): `exit` can fire before stdio streams finish\n // flushing, which matters a lot for finding #1's \"instant exit\" shape —\n // `close` is Node's guarantee that every stdout/stderr byte the process\n // ever wrote has already been delivered to our listeners, so the error\n // built below always has the complete stderr tail, not a truncated one.\n this.child.on('close', (code, signal) => {\n this.onClosed(this.buildExitError(code, signal));\n });\n this.child.on('error', (err) => {\n this.onClosed(err);\n });\n }\n\n /** Send a command, resolved with its correlated `response` message. */\n send(command: Record<string, unknown> & { type: string; id?: string }): Promise<PiRpcMessage> {\n if (this.closed) {\n return Promise.reject(this.exitError ?? new Error('pi process is closed'));\n }\n const id = command.id ?? `req-${this.nextId++}`;\n const full = { ...command, id };\n return new Promise((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n this.child.stdin.write(`${JSON.stringify(full)}\\n`, (err) => {\n if (err) {\n this.pending.delete(id);\n reject(err);\n }\n });\n });\n }\n\n /** Every non-response, non-`extension_ui_request` line — the latter is answered directly by this client (see `respondToExtensionUiRequest`) and never enqueued. */\n get events(): AsyncIterable<PiRpcMessage> {\n return this.eventQueue;\n }\n\n /**\n * Record a pi RPC message `type` that `PiSession` (pi-adapter.ts) decided\n * has no `AgentEvent` mapping and isn't routine bookkeeping (see\n * `events.ts`'s `ROUTINE_PI_EVENT_TYPES`) — i.e. genuinely unexpected\n * traffic. Logs once per distinct type (not per occurrence, so a\n * repeating unmapped type can't spam stdout); the running tally is also\n * folded into this client's exit-time error message (`buildExitError`) so\n * a post-mortem on a failed/hung task has it without separate log scraping.\n */\n recordUnmappedFrame(type: string): void {\n const next = (this.unmappedFrameCounts.get(type) ?? 0) + 1;\n this.unmappedFrameCounts.set(type, next);\n if (next === 1) {\n console.warn(\n `[byok/pi-adapter] pi emitted a frame type with no AgentEvent mapping: \"${type}\" (further occurrences of this type won't be logged individually)`,\n );\n }\n }\n\n /** Best-effort teardown. SIGTERM on POSIX; `taskkill /T /F` on Windows to also reap child processes pi itself spawned (e.g. bash). */\n kill(): void {\n if (this.closed) return;\n const pid = this.child.pid;\n if (process.platform === 'win32' && pid !== undefined) {\n spawnSync('taskkill', ['/pid', String(pid), '/T', '/F']);\n } else {\n this.child.kill('SIGTERM');\n }\n }\n\n private onData(chunk: string): void {\n this.buffer += chunk;\n let newlineIndex = this.buffer.indexOf('\\n');\n while (newlineIndex !== -1) {\n let line = this.buffer.slice(0, newlineIndex);\n this.buffer = this.buffer.slice(newlineIndex + 1);\n if (line.endsWith('\\r')) line = line.slice(0, -1);\n if (line.length > 0) this.onLine(line);\n newlineIndex = this.buffer.indexOf('\\n');\n }\n }\n\n private onLine(line: string): void {\n let msg: PiRpcMessage;\n try {\n msg = JSON.parse(line) as PiRpcMessage;\n } catch {\n return; // a stray non-JSON line is not this client's concern\n }\n if (msg.type === 'response' && typeof msg.id === 'string' && this.pending.has(msg.id)) {\n const waiter = this.pending.get(msg.id);\n this.pending.delete(msg.id);\n waiter?.resolve(msg);\n return;\n }\n if (msg.type === 'extension_ui_request' && typeof msg.id === 'string' && typeof msg.method === 'string') {\n // Answered here, not enqueued — see `respondToExtensionUiRequest`. If\n // left unanswered this blocks pi's entire run (process alive, waiting\n // on stdin forever), so every blocking dialog is handled here.\n this.respondToExtensionUiRequest(msg.id, msg.method);\n return;\n }\n this.eventQueue.push(msg);\n }\n\n /**\n * Answer pi's extension-UI blocking protocol headlessly (rpc.md's\n * \"Extension UI Protocol\"). Fail-closed policy, stated explicitly because\n * it's a security-relevant default, not an incidental one: this NEVER\n * approves or picks a value on the caller's behalf — every dialog method\n * (`select`/`confirm`/`input`/`editor`) gets `{cancelled: true}`, the one\n * response shape rpc.md documents as valid for all four uniformly\n * (\"Dismiss any dialog method... the extension receives `undefined` (for\n * select/input/editor) or `false` (for confirm)\"). An extension asking\n * e.g. `confirm(\"Delete everything?\")` gets a firm decline, never a\n * guessed approval — this adapter has no human in the loop to ask, and\n * silently approving would defeat any extension that uses these dialogs\n * specifically as a permission gate. Fire-and-forget methods\n * (`notify`/`setStatus`/`setWidget`/`setTitle`/`set_editor_text`) get no\n * reply at all — sending one would itself violate rpc.md (\"Responses are\n * sent for dialog methods only\").\n */\n private respondToExtensionUiRequest(id: string, method: string): void {\n if (!DIALOG_UI_METHODS.has(method)) return; // fire-and-forget — no response expected or sent\n this.child.stdin.write(`${JSON.stringify({ type: 'extension_ui_response', id, cancelled: true })}\\n`);\n }\n\n private onStderr(chunk: string): void {\n for (const rawLine of chunk.split('\\n')) {\n const line = rawLine.trim();\n if (line.length === 0) continue;\n this.stderrRing.push(line);\n if (this.stderrRing.length > STDERR_RING_CAPACITY) this.stderrRing.shift();\n }\n }\n\n /**\n * finding #1 (\"bad flag → instant exit\", e.g. the `--session-id`/\n * `--exclude-tools` bugs this task fixes): the daemon used to report only\n * `pi process exited (code=1, signal=null)` — accurate but useless for\n * diagnosing *why* without re-running pi by hand with a raw JSONL logger,\n * exactly as this task's own root-cause investigation had to. Folding in\n * the stderr tail and any unmapped-frame tally makes that self-diagnosing\n * from the thrown error alone.\n */\n private buildExitError(code: number | null, signal: NodeJS.Signals | null): Error {\n const parts = [`pi process exited (code=${code}, signal=${signal})`];\n if (this.stderrRing.length > 0) {\n parts.push(`stderr: ${this.stderrRing.join(' | ')}`);\n }\n if (this.unmappedFrameCounts.size > 0) {\n const summary = [...this.unmappedFrameCounts.entries()].map(([type, count]) => `${type}×${count}`).join(', ');\n parts.push(`unmapped frame types seen: ${summary}`);\n }\n return new Error(parts.join('; '));\n }\n\n private onClosed(err: Error): void {\n if (this.closed) return;\n this.closed = true;\n this.exitError = err;\n for (const [, waiter] of this.pending) waiter.reject(err);\n this.pending.clear();\n this.eventQueue.end();\n }\n}\n","/**\n * Provider credential names that the daemon may explicitly admit for the\n * legacy direct-Pi path. Subscription runtimes and the BYOK custody launcher\n * must not inherit them.\n *\n * This list contains names only. Credential values remain owned by the\n * caller's environment or by the separate keys launcher.\n */\nexport const PROVIDER_CREDENTIAL_ENV_NAMES = [\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_OAUTH_TOKEN',\n 'OPENAI_API_KEY',\n 'GEMINI_API_KEY',\n 'AZURE_OPENAI_API_KEY',\n 'DEEPSEEK_API_KEY',\n 'GROQ_API_KEY',\n 'MISTRAL_API_KEY',\n 'OPENROUTER_API_KEY',\n 'XAI_API_KEY',\n 'ZAI_API_KEY',\n] as const;\n\n/**\n * Credential-shaped names stripped at subscription/BYOK custody boundaries.\n * This is intentionally a superset of the small legacy-Pi allowlist above:\n * denying a credential is safe, while allowing every cloud credential Pi\n * could consume would regress the daemon's ambient-environment isolation.\n */\nexport const PROVIDER_CREDENTIAL_ENV_DENY_NAMES = [\n ...PROVIDER_CREDENTIAL_ENV_NAMES,\n 'ANT_LING_API_KEY',\n 'NVIDIA_API_KEY',\n 'CEREBRAS_API_KEY',\n 'CLOUDFLARE_API_KEY',\n 'AI_GATEWAY_API_KEY',\n 'ZAI_CODING_CN_API_KEY',\n 'OPENCODE_API_KEY',\n 'RADIUS_API_KEY',\n 'FIREWORKS_API_KEY',\n 'TOGETHER_API_KEY',\n 'BASETEN_API_KEY',\n 'KIMI_API_KEY',\n 'MINIMAX_API_KEY',\n 'MINIMAX_CN_API_KEY',\n 'QWEN_TOKEN_PLAN_API_KEY',\n 'QWEN_TOKEN_PLAN_CN_API_KEY',\n 'XIAOMI_API_KEY',\n 'XIAOMI_TOKEN_PLAN_CN_API_KEY',\n 'XIAOMI_TOKEN_PLAN_AMS_API_KEY',\n 'XIAOMI_TOKEN_PLAN_SGP_API_KEY',\n 'AWS_ACCESS_KEY_ID',\n 'AWS_SECRET_ACCESS_KEY',\n 'AWS_SESSION_TOKEN',\n 'GOOGLE_APPLICATION_CREDENTIALS',\n // Reserved by the keys-owned Pi projection. It must never be inherited\n // from the daemon; the launcher deletes any ambient copy and injects only\n // the exact credential it just resolved from OS custody.\n 'PI_PROVIDER_API_KEY',\n] as const;\n\n/** Return a copy that cannot pass ambient provider credentials to a child. */\nexport function withoutProviderCredentials(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const sanitized = { ...env };\n for (const name of PROVIDER_CREDENTIAL_ENV_DENY_NAMES) {\n delete sanitized[name];\n }\n return sanitized;\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport type { AgentEvent, TaskOfferPayload } from '@byok-sdk/protocol';\nimport {\n PolicyUnsupportedError,\n type RuntimeAdapter,\n type RuntimeCapabilities,\n type RuntimeDetectResult,\n type RuntimeEnvironmentRequirements,\n type Session,\n type TaskContext,\n} from '../../types';\nimport { resolvePiBin, type ResolvedBin } from './resolve-bin';\nimport { mapPermissionPolicyToPiArgs } from './permission-mapping';\nimport { mapPiMessageToAgentEvent, ROUTINE_PI_EVENT_TYPES } from './events';\nimport { PiRpcClient, type PiRpcMessage, type SpawnFn } from './rpc-client';\nimport {\n PROVIDER_CREDENTIAL_ENV_NAMES,\n withoutProviderCredentials,\n} from '../provider-credential-environment';\n\nconst execFileAsync = promisify(execFile);\nconst DETECT_TIMEOUT_MS = 5_000;\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Known provider credential env var *names* (never values) — see the\n * credential-isolation rule on `RuntimeAdapter`. `detect()` only checks\n * whether one of these names is set; it never reads pi's own auth storage\n * (`~/.pi/...`) or any file contents. Not exhaustive (pi supports ~30\n * providers); covers the common ones for a useful `authPresent` signal.\n */\nexport interface PiAdapterOptions {\n /** Override bin resolution — tests substitute the fake-pi fixture script. */\n resolveBin?: () => ResolvedBin;\n /** Override process spawning — tests substitute a fake spawn. */\n spawnFn?: SpawnFn;\n /**\n * Separate-process BYOK credential boundary. The launcher receives only\n * non-secret selection/config paths, resolves the OS credential itself,\n * and transparently proxies the pinned Pi RPC process.\n */\n byokLauncher?: PiByokLauncherConfig;\n}\n\nexport interface PiByokLauncherConfig {\n command: string;\n /** Optional fixed launcher arguments, before BYOK's required arguments. */\n args?: string[];\n profileDbPath: string;\n sessionDir: string;\n secretServicePrefix?: string;\n}\n\nexport class PiAdapter implements RuntimeAdapter {\n readonly id = 'pi';\n readonly supportsDispatchSelection = true;\n\n constructor(private readonly options: PiAdapterOptions = {}) {}\n\n async detect(): Promise<RuntimeDetectResult> {\n try {\n const bin = this.resolveBin();\n // Empirically, pi 0.84.1 prints `--version` to stdout. Check stderr too\n // so detection remains accurate if a future pinned release moves it.\n const { stdout, stderr } = await execFileAsync(bin.command, ['--version'], { timeout: DETECT_TIMEOUT_MS });\n const version = stdout.trim() || stderr.trim();\n const authPresent = PROVIDER_CREDENTIAL_ENV_NAMES.some((name) => process.env[name] !== undefined);\n return { present: true, version, authPresent };\n } catch {\n return { present: false };\n }\n }\n\n capabilities(): RuntimeCapabilities {\n // `approvalInteractive: false` — `PiSession.resolveApproval` throws\n // unconditionally: pi has no `needs_approval` notion to resume from.\n return { steer: true, resume: true, approvalInteractive: false, permissionModes: ['auto', 'readonly'] };\n }\n\n /**\n * M5: pi authenticates to its ~30 supported providers via env-var API\n * keys — `detect()`'s own `authPresent` probe above checks this identical\n * list — so these MUST keep flowing into pi's spawned process or pi auth\n * breaks entirely. `KNOWN_PROVIDER_ENV_VARS` above is the single source\n * of truth, reused here rather than duplicated. No `baseNames`: nothing\n * in this adapter or `rpc-client.ts` reads a pi-specific config-discovery\n * variable beyond the platform baseline (`daemon/environment.ts`).\n */\n environmentRequirements(): RuntimeEnvironmentRequirements {\n return { credentialNames: PROVIDER_CREDENTIAL_ENV_NAMES };\n }\n\n async start(task: TaskOfferPayload, ctx: TaskContext): Promise<Session> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('pi adapter only supports string instructions in M0 (no blob-ref fetch yet)');\n }\n\n const mapping = mapPermissionPolicyToPiArgs(ctx.policy);\n if (!mapping.ok) {\n throw new PolicyUnsupportedError(mapping.reason ?? 'policy rejected by pi adapter');\n }\n\n const bin = this.resolveBin();\n // Session/workspace continuity:\n // `task.sessionRef` is only ever non-empty here when `task-runner.ts`\n // has (a) found a recorded workspace for this exact sessionRef in its\n // `SessionWorkspaceStore` and (b) spawned this adapter with\n // `ctx.workspaceDir` set to that SAME directory — see task-runner.ts's\n // `handleOffer`. That matters because pi's real `--session <id>` resume\n // is scoped to the cwd/project a session was created under: resuming\n // from a *different* cwd prompts an interactive \"Session found in\n // different project: ... Fork this session into current directory?\n // [y/N]\" pi cannot answer headlessly, and resuming an id pi never\n // minted fails outright (`No session found matching '<id>'`, exit 1 —\n // empirically confirmed against real pi, both live-probed during this\n // task). An absent `sessionRef` always means \"start fresh\" — pi mints\n // its own session id, which this adapter reads back via `get_state`\n // below and reports as `Session.sessionRef` so a *future* follow-up can\n // resume it. `TaskOfferPayload.workspaceHint` remains unimplemented (no\n // caller populates `DispatchInput` with it yet, and its intended\n // semantics — e.g. does it override or merely suggest a workspace\n // relative to the sessionRef mapping? — are still undesigned; see\n // docs/protocol.md §2's note on this field for the explicit\n // reserved/ignored status); a real implementation is a genuine\n // follow-on design task, not a mechanical fix, and is intentionally\n // left alone here. pi 0.84.1 also offers `--session-id`, but that flag\n // creates a missing session. BYOK deliberately uses `--session` so a\n // lost/unknown authoritative sessionRef fails closed instead of silently\n // starting a new history under the requested id.\n const resumeSessionId = task.sessionRef;\n const piArgs = ['--mode', 'rpc', ...(resumeSessionId ? ['--session', resumeSessionId] : []), ...mapping.args];\n const selection = task.dispatchSelection;\n let command = bin.command;\n let args = piArgs;\n if (selection !== undefined) {\n if (selection.lane !== 'byok' || selection.runtimeId !== 'pi') {\n throw new PolicyUnsupportedError(\n `pi adapter cannot execute ${selection.lane} selection for runtime ${selection.runtimeId}`,\n );\n }\n const launcher = this.options.byokLauncher;\n if (launcher === undefined) {\n throw new PolicyUnsupportedError(\n 'pi BYOK selection requires a configured credential-custody launcher',\n );\n }\n command = launcher.command;\n args = [\n ...(launcher.args ?? []),\n '--pi-bin',\n bin.command,\n '--profile-db',\n launcher.profileDbPath,\n '--session-dir',\n launcher.sessionDir,\n ...(launcher.secretServicePrefix\n ? ['--secret-service-prefix', launcher.secretServicePrefix]\n : []),\n '--provider',\n selection.providerId,\n '--model',\n selection.modelId,\n '--',\n ...piArgs,\n ];\n }\n\n const rpc = new PiRpcClient({\n command,\n args,\n cwd: ctx.workspaceDir,\n env: selection === undefined ? ctx.env : withoutProviderCredentials(ctx.env),\n spawnFn: this.options.spawnFn,\n });\n\n const response = await rpc.send({ type: 'prompt', message: task.instruction });\n if (response.success === false) {\n rpc.kill();\n throw new Error(typeof response.error === 'string' ? response.error : 'pi rejected the initial prompt');\n }\n\n let sessionRef: string;\n if (resumeSessionId) {\n sessionRef = resumeSessionId;\n } else {\n try {\n sessionRef = await resolveFreshSessionId(rpc);\n } catch (err) {\n // Finding F8: fail closed, not a fabricated id — and don't leak the\n // process this adapter just spawned (the `response.success===false`\n // branch above already kills it on ITS failure path; this mirrors\n // that for get_state's).\n rpc.kill();\n throw err;\n }\n }\n return new PiSession(sessionRef, rpc, selection);\n }\n\n private resolveBin(): ResolvedBin {\n return (this.options.resolveBin ?? resolvePiBin)();\n }\n}\n\n/**\n * Learn pi's own real session id for a freshly-started (non-resume) run, so\n * `Session.sessionRef` reports something a *future* follow-up can actually\n * resume via `--session <id>`. `get_state.data.sessionId` is populated from\n * the moment pi's RPC process boots (confirmed live: present even before\n * any prompt is sent, with `messageCount: 0`), so this is safe to call\n * right after the initial prompt is accepted.\n *\n * Finding F8 (fabricated sessionRef): this used to fall back to\n * `crypto.randomUUID()` whenever `get_state` failed, timed out, or omitted\n * `sessionId` — minting an id pi itself never knew about, which could never\n * actually be resumed and silently looked like a legitimate, resumable\n * session to every caller (`TaskRunner`'s `SessionWorkspaceStore`, a future\n * follow-up's `task.offer.sessionRef`, etc). Fail closed instead: if pi\n * doesn't hand back an authoritative session id, `start()` itself fails\n * with the real underlying error (stderr context is already folded in when\n * the rejection comes from the process exiting — see\n * `PiRpcClient.buildExitError`), exactly like any other adapter start()\n * failure `task-runner.ts` already knows how to report as `task.fail`.\n */\nasync function resolveFreshSessionId(rpc: PiRpcClient): Promise<string> {\n let state: PiRpcMessage;\n try {\n state = await rpc.send({ type: 'get_state' });\n } catch (err) {\n throw new Error(`pi did not yield an authoritative session id (get_state failed): ${errorMessage(err)}`, {\n cause: err,\n });\n }\n\n if (state.success === false) {\n const reason = typeof state.error === 'string' ? state.error : 'get_state reported failure';\n throw new Error(`pi did not yield an authoritative session id (get_state failed): ${reason}`);\n }\n\n const data = state.data as { sessionId?: unknown } | undefined;\n if (typeof data?.sessionId === 'string' && data.sessionId.length > 0) {\n return data.sessionId;\n }\n\n throw new Error(\n 'pi did not yield an authoritative session id (get_state succeeded but reported no sessionId) — cannot mint a resumable session',\n );\n}\n\nclass PiSession implements Session {\n constructor(\n public readonly sessionRef: string,\n private readonly rpc: PiRpcClient,\n private readonly selection: TaskOfferPayload['dispatchSelection'],\n ) {}\n\n get events(): AsyncIterable<AgentEvent> {\n const rpc = this.rpc;\n return {\n [Symbol.asyncIterator](): AsyncIterator<AgentEvent> {\n const inner = rpc.events[Symbol.asyncIterator]();\n return {\n async next(): Promise<IteratorResult<AgentEvent>> {\n for (;;) {\n const { value, done } = await inner.next();\n if (done) return { value: undefined as never, done: true };\n const mapped = mapPiMessageToAgentEvent(value);\n if (mapped) return { value: mapped, done: false };\n // Unmapped pi message: routine bookkeeping (compaction/retry/\n // session events — see ROUTINE_PI_EVENT_TYPES) is silently\n // ignored, same as before; anything else is genuinely\n // unexpected traffic worth flagging (see recordUnmappedFrame's\n // doc comment) — keep pulling either way, never surfaced.\n if (!ROUTINE_PI_EVENT_TYPES.has(value.type)) {\n rpc.recordUnmappedFrame(value.type);\n }\n }\n },\n };\n },\n };\n }\n\n async steer(text: string): Promise<void> {\n await this.rpc.send({ type: 'steer', message: text });\n }\n\n async followUp(task: TaskOfferPayload): Promise<void> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('pi adapter only supports string instructions in M0 (no blob-ref fetch yet)');\n }\n const requestedSelection = task.dispatchSelection;\n if (requestedSelection !== undefined && (\n this.selection?.lane !== 'byok' ||\n requestedSelection.lane !== 'byok' ||\n requestedSelection.runtimeId !== 'pi' ||\n requestedSelection.providerId !== this.selection.providerId ||\n requestedSelection.modelId !== this.selection.modelId\n )) {\n throw new PolicyUnsupportedError(\n 'pi persistent session cannot change its authoritative BYOK provider/model selection',\n );\n }\n await this.rpc.send({ type: 'prompt', message: task.instruction, streamingBehavior: 'followUp' });\n }\n\n async interrupt(): Promise<void> {\n await this.rpc.send({ type: 'abort' });\n }\n\n async close(): Promise<void> {\n this.rpc.kill();\n }\n\n async resolveApproval(): Promise<void> {\n // pi has no built-in per-call approval gate (see permission-mapping.ts)\n // and never emits `needs_approval` in M0/M1, so this should be\n // unreachable in practice. Kept as an explicit, descriptive failure\n // rather than a silent no-op so a future caller (or a misbehaving\n // server) gets a clear error instead of a hang.\n throw new Error('pi adapter does not support approval resume: pi never emits needs_approval in M0/M1');\n }\n}\n","export interface ResolvedBin {\n command: string;\n source: 'env' | 'path';\n}\n\n/**\n * Resolve the `claude` (Claude Code) CLI executable.\n *\n * Unlike pi (`../pi/resolve-bin.ts`), this package does NOT bundle a\n * matched `claude` build as a dependency. Claude Code is the end\n * user's own globally-installed, individually-authenticated CLI (`claude\n * auth login`, tied to their Anthropic/claude.ai account) — there is\n * nothing useful to vendor: a bundled copy could never carry the user's own\n * login state, and the credential-isolation rule (see `../../types.ts`'s\n * `RuntimeAdapter` doc comment — this adapter must never read, proxy, or\n * forward `~/.claude`'s own auth storage) means this adapter has no\n * business managing a claude install at all, only spawning whatever `claude`\n * the user already has authenticated on their PATH.\n *\n * Resolution is therefore deliberately two-tier, not three like pi's:\n * `BYOK_CLAUDE_BIN` overrides everything when set (the injectable seam for\n * in-process tests — mirrors `BYOK_PI_BIN` and substitutes the\n * `fake-claude.mjs` fixture ahead of a real claude install, exactly as pi's\n * own override does), otherwise this falls back to the literal command name\n * `claude`, resolved via the child process's own PATH lookup — there is no\n * package-resolution tier in between.\n */\nexport function resolveClaudeBin(): ResolvedBin {\n const override = process.env.BYOK_CLAUDE_BIN;\n if (override) {\n return { command: override, source: 'env' };\n }\n return { command: 'claude', source: 'path' };\n}\n","import { fileURLToPath } from 'node:url';\nimport path from 'node:path';\n\nexport interface ResolvedApprovalMcpBin {\n command: string;\n args: string[];\n source: 'env' | 'dist';\n}\n\n/**\n * Resolve `byok-approval-mcp` — the small stdio MCP server\n * (`bin/byok-approval-mcp.ts`) `claude`'s own `--permission-prompt-tool`\n * spawns as ITS child process (see that file's doc comment, and\n * `permission-mapping.ts`'s `confirm`-mode doc comment, for the full design).\n *\n * Unlike `resolveClaudeBin` (the end user's own separately-installed,\n * separately-authenticated CLI, resolved via bare-name PATH lookup),\n * `byok-approval-mcp` is a script THIS SAME `@byok-sdk/client` package ships —\n * bare-name PATH lookup is NOT safe for it: `@byok-sdk/client` is typically a\n * project-local dependency, so its `node_modules/.bin/byok-approval-mcp`\n * symlink is only on PATH for processes that inherit THAT project's own\n * shell/PATH, not reliably for a background OS service (launchd/systemd\n * often run with a stripped-down PATH that omits project-local\n * `node_modules/.bin` entirely — see `templates/service/**`). Resolving an\n * ABSOLUTE path to this package's own compiled bin avoids depending on PATH\n * at all.\n *\n * `BYOK_APPROVAL_MCP_BIN` overrides everything when set — the injectable\n * seam for tests (mirrors `BYOK_CLAUDE_BIN`/`BYOK_PI_BIN`), letting a test\n * substitute a fixture script instead of computing any real path. The\n * override is a single command string with no separate args (tests don't\n * need to invoke it any differently than `node <script>`); the real default\n * below is `node <absolute-path-to-the-built-bin>`.\n *\n * The default computation is deliberately anchored to THIS module's own\n * `import.meta.url`, resolved once at the real production entry point: when\n * `@byok-sdk/client` is built (`tsup.config.ts`), this file's code ends up\n * bundled into `dist/index.js` at the package root, with `dist/bin/\n * byok-approval-mcp.js` as its direct sibling (same layout `byok-agent.js`\n * already uses) — `path.join(path.dirname(fileURLToPath(import.meta.url)),\n * 'bin', 'byok-approval-mcp.js')` is therefore correct for that one real\n * shape. It is NOT correct for this file's own unbundled TypeScript source\n * location (`src/adapters/claude/` is two directories deeper than `src/`),\n * but nothing in this codebase ever reaches this fallback unbundled — every\n * test that exercises `confirm` mode sets `BYOK_APPROVAL_MCP_BIN` explicitly\n * (see `claude-adapter.test.ts`), exactly like `BYOK_CLAUDE_BIN` already\n * does for the real `claude` binary.\n */\nexport function resolveApprovalMcpBin(): ResolvedApprovalMcpBin {\n const override = process.env.BYOK_APPROVAL_MCP_BIN;\n if (override) {\n return { command: override, args: [], source: 'env' };\n }\n const distBin = path.join(path.dirname(fileURLToPath(import.meta.url)), 'bin', 'byok-approval-mcp.js');\n return { command: process.execPath, args: [distBin], source: 'dist' };\n}\n","import type { PermissionPolicy } from '@byok-sdk/protocol';\n\nexport interface ClaudePermissionMapping {\n ok: boolean;\n /** CLI args to append to `claude -p ...`. Only meaningful when `ok` is true. */\n args: string[];\n /** Present when `ok` is false. */\n reason?: string;\n /**\n * M4 Phase 3: set only for `policy.mode === 'confirm'` — tells\n * `claude-adapter.ts`'s `start()` to additionally spin up the out-of-band\n * approval channel (write the temp `--mcp-config` file pointing at\n * `bin/byok-approval-mcp.ts`, then append `--permission-prompt-tool`) on\n * top of the base `args` returned here. Kept out of `args` itself because\n * generating that config is a real filesystem side effect (a temp\n * directory + file write) that has no business happening inside this\n * otherwise-pure, I/O-free mapping function — see `claude-adapter.ts`'s\n * `start()` for where it actually happens.\n */\n needsApprovalMcp?: boolean;\n}\n\n/**\n * Claude Code's real, capitalized built-in read-only tool names. `Read` is\n * fully empirically confirmed on the installed 2.1.212 binary two ways: (a)\n * under `--permission-mode default` (deny-by-default) with `--allowedTools\n * Read`, a Write attempt is cleanly denied (`permission_denials` populated,\n * no hang); (b) more importantly, under `--tools Read` specifically (the\n * *replacive* flag — see the module doc comment below on why this, not\n * `--allowedTools`, is what this mapper actually uses), the model's own\n * reported tool list (`system/init`'s `tools` array) shrinks to exactly\n * `['Read', ...any always-on MCP tools]` and it cannot even attempt Write\n * (no permission prompt needed — the tool simply isn't offered to it).\n * `Glob`/`Grep` are included by strong, well-documented Claude Code naming\n * convention (identical capitalization pattern to the four tool names\n * empirically confirmed here: Read/Write/Edit/Bash) and were confirmed as\n * syntactically-accepted `--tools`/`--allowedTools` argument values on this\n * real installed binary (no \"unknown tool\" rejection) — but this\n * particular dev machine runs a heavily customized/plugin-extended Claude\n * Code build whose own active tool surface does not include Glob or Grep at\n * all (its `system/init.tools` array is a bespoke orchestration-tool set,\n * not vanilla Claude Code's), so their exact allow-through behavior could\n * not be independently exercised end-to-end here the way Read's was. Flagged\n * for the M2-c freeze decision, not silently assumed.\n */\nconst READONLY_TOOLS: readonly string[] = ['Read', 'Glob', 'Grep'];\n\n/**\n * Map an effective {@link PermissionPolicy} to `claude -p ...` CLI args,\n * fail-closed. Empirically grounded against the real installed `claude`\n * 2.1.212 binary (see the M2-a report) — every claim below was reproduced\n * live, not inferred from `--help` text or training-data recall (`--help`\n * text alone was actively misleading in at least one case: `--allowedTools`\n * reads like a hard allowlist but is NOT one — see the finding below).\n *\n * ## Two DIFFERENT, easily-confused tool-control flags (the central finding)\n *\n * `claude --help` documents two separate flag families that look\n * interchangeable but are not:\n *\n * - `--allowedTools` / `--disallowedTools`: a PERMISSION pre-grant/deny\n * list. Empirically, this only affects whether a tool call needs an\n * interactive prompt — it does NOT reliably restrict what the model can\n * do once a broadly-permissive `--permission-mode` (acceptEdits,\n * bypassPermissions, dontAsk) is also in effect. Confirmed two ways: (a)\n * `--permission-mode acceptEdits --allowedTools Read` still let a Write\n * call succeed (zero `permission_denials`) — the allowlist was silently\n * ignored once the broad grant was in effect; (b) `--permission-mode\n * acceptEdits --disallowedTools Write` correctly blocked the *named*\n * `Write` tool call (`<tool_use_error>Error: No such tool available:\n * Write...`), but the model then simply used `Bash` instead (still\n * enabled) to write the same file — `(Bash completed with no output)`,\n * file created anyway. A single denied tool name is not a security\n * boundary when a general-purpose Bash tool remains available.\n * - `--tools`: REPLACES the entire active built-in tool set (pi's own\n * `--tools` flag works the same way — this is the one place claude and pi\n * share an identical convention). Confirmed: `--tools Read` shrinks\n * `system/init`'s reported `tools` array to just `['Read', ...MCP\n * tools]`; the model then cannot even attempt Write (no tool definition\n * to call), regardless of `--permission-mode`. `--tools \"\"` disables\n * every built-in tool outright (confirmed: only the always-on MCP tool\n * remained). This is the ONLY mechanism this mapper trusts to actually\n * restrict the model's capability surface — never `--allowedTools`/\n * `--disallowedTools` for that purpose.\n *\n * ## Mode mapping\n *\n * - `auto` (no `allowTools`): `--permission-mode acceptEdits`. Empirically\n * confirmed to auto-accept BOTH file edits (Write) and Bash execution\n * with zero `permission_denials` — broader than the interactive-mode\n * folklore that acceptEdits only covers file edits. Verified for a\n * benign `echo ... > file` Bash command specifically; not exhaustively\n * verified across every possible Bash command shape.\n * - `auto` with `allowTools` (no `denyTools`): adds `--tools\n * <allowTools.join(',')>` (the replacive flag) on top of `acceptEdits`,\n * mirroring pi's own `allowTools`-only branch.\n * - `readonly`: ALWAYS `--permission-mode default` (never acceptEdits/\n * bypassPermissions/dontAsk — see the finding above: a permissive mode\n * defeats any restriction) plus `--tools\n * <intersect(allowTools ?? READONLY_TOOLS, READONLY_TOOLS) - denyTools>`.\n * An empty resulting set emits `--tools \"\"` explicitly — never an absent\n * `--tools` flag, which would default to claude's full active set and\n * silently widen a readonly request (the exact class of bug pi's own\n * `--no-tools` fallback exists to prevent).\n * - `plan`: `--permission-mode plan`. Empirically confirmed to never\n * execute the requested mutating tool call against its real target — the\n * model instead writes a plan document and stops. **Caveat, flagged for\n * the M2-c freeze decision, not silently hidden**: it writes that plan\n * file to `~/.claude/plans/<slug>.md` — the real user's home directory,\n * OUTSIDE `ctx.workspaceDir` — unconditionally, regardless of cwd. This\n * is a genuine, confirmed workspace-confinement gap specific to plan\n * mode's own bookkeeping (the path is fixed/product-owned by Claude Code\n * itself, not attacker/model-directed, and no destructive action runs\n * against the actual task target) — mapped as supported rather than\n * failed-closed because refusing would make an entire policy mode whose\n * name and semantics match this protocol's own `plan` mode 1:1\n * completely unusable over a relatively minor, fixed-path side effect,\n * but this is a judgment call for a human to weigh in on, not a fact.\n * - `confirm`: SUPPORTED as of M4 Phase 3 — `--permission-mode default`\n * (deny-by-default baseline, same as `readonly`'s own choice above) plus\n * `allowTools`/`denyTools` mapped exactly like `auto` does (finding F2,\n * fixed same session as the confirm-mode support itself first shipped —\n * see the function body below): an explicit `--tools <allowTools>` when\n * only `allowTools` is set, and a fail-closed refusal whenever `denyTools`\n * is non-empty, since confirm — like `auto`/`plan` and unlike\n * `readonly` — has no bounded, known-safe base tool list to subtract\n * from; the only trustworthy restriction mechanism (`--tools`) is\n * replacive, not subtractive. This composes with, rather than replaces,\n * `--permission-prompt-tool` pointed at a small bundled MCP server\n * (`bin/byok-approval-mcp.ts`) that forwards the pending permission\n * decision to this device's own daemon over its control socket and blocks\n * until a human (or the daemon's own timeout) resolves it. This directly\n * supersedes the M2-a finding that `confirm` was inexpressible: that\n * finding was empirically true for `--permission-mode` ALONE (every\n * decision resolved synchronously, no pause) — `--permission-prompt-tool`\n * is a DIFFERENT flag, undocumented in `claude --help`'s output on the\n * installed 2.1.216 binary (confirmed accepted anyway — an unrecognized\n * flag is rejected outright with `error: unknown option`, this one is\n * not), that genuinely pauses the turn on a real MCP round-trip while\n * claude waits for an answer (M4 Phase 3 STEP 0: live-verified allow,\n * deny, and multi-second-delayed-then-allow, against the real installed\n * binary — see `../../bin/byok-approval-mcp.ts`'s own doc comment for the\n * full empirical writeup, including the one caveat found: a permission-\n * prompt-tool call that NEVER answers at all triggers claude's own\n * internal abandonment of the turn after roughly 1.5s — never actually hit\n * by this design, since `byok-approval-mcp` always eventually answers\n * within its own configured ceiling, but disclosed rather than silently\n * assumed away).\n * - `denyTools` non-empty under `auto`: FAILS CLOSED. Given the\n * `--allowedTools`/`--disallowedTools`-under-a-permissive-mode escape\n * hatch above, the only mechanism this mapper trusts (`--tools`) is\n * REPLACIVE, not subtractive — pi can resolve `denyTools` to an\n * equivalent allowlist because pi's own default active tool set is\n * fixed and known from its installed source; claude's active tool\n * surface is NOT reliably known ahead of time (empirically, this exact\n * dev machine's own installed build exposes a bespoke, non-vanilla tool\n * set — see `READONLY_TOOLS`'s doc comment), so there is no reliable\n * \"default set minus these\" this mapper can construct. Refusing is the\n * fail-closed choice over guessing a set that might not match reality.\n *\n * `network: false` fails closed for the same reason as pi: no verified\n * network sandbox exists for claude's Bash tool either (`claude --help`\n * exposes no network/sandbox flag at all) — this was not independently\n * re-verified against real network traffic the way the tool-restriction\n * findings above were (doing so would require an actual network probe this\n * task didn't run), but is the same conservative, precedent-consistent\n * default pi already applies for an unverifiable constraint.\n */\nexport function mapPermissionPolicyToClaudeArgs(policy: PermissionPolicy): ClaudePermissionMapping {\n if (policy.network === false) {\n return {\n ok: false,\n args: [],\n reason: 'policy requires network:false, which the claude adapter cannot enforce (claude has no network sandbox for its Bash tool)',\n };\n }\n\n const denyTools = policy.denyTools ?? [];\n\n if (policy.mode === 'confirm') {\n // Deny-by-default baseline (never a permissive mode — see readonly's own\n // reasoning above): `--permission-prompt-tool` only intercepts a\n // decision that would otherwise need ONE, so a permissive mode here\n // would defeat confirm's whole point by auto-granting first. The actual\n // `--permission-prompt-tool`/`--mcp-config` flags are appended by\n // `claude-adapter.ts`'s `start()` (see `needsApprovalMcp` above), not\n // here, since generating the mcp-config file is a real I/O side effect.\n //\n // Finding F2 (cross-model adversarial review): this branch used to\n // return unconditionally right here, silently discarding\n // `allowTools`/`denyTools` entirely — a `{mode:'confirm',\n // denyTools:['Bash']}` policy would still let Bash through (subject\n // only to a per-call human prompt), never actually enforcing the\n // caller's denial; that's a silent widening of the requested policy,\n // exactly what this mapper fails closed for everywhere else. Confirm\n // has no bounded, known-safe base tool list the way `readonly` does\n // (`READONLY_TOOLS`) — its whole point is to allow the FULL active\n // tool surface, gated by a human decision per call, not a fixed small\n // set — so its `denyTools` problem is the SAME shape as `auto`/`plan`'s\n // below: the only mechanism this mapper trusts (`--tools`) is\n // REPLACIVE, not subtractive, and there is no reliable \"full active set\n // minus denied\" to compute. Mirrors `auto`'s handling exactly: fail\n // closed whenever `denyTools` is non-empty (composed with `allowTools`\n // or not — an inexpressible constraint is inexpressible either way),\n // otherwise an explicit `--tools <allowTools>` when `allowTools` alone\n // is set.\n if (denyTools.length > 0) {\n return {\n ok: false,\n args: [],\n reason: `claude adapter cannot reliably enforce denyTools ([${denyTools.join(', ')}]) under confirm mode: its only trustworthy tool-restriction mechanism (--tools) replaces the whole active set rather than subtracting from it, and this adapter has no reliable way to learn a target installation's full default tool set to compute \"default minus denied\" (this dev machine's own installed build exposes a non-vanilla, bespoke tool list) — refusing rather than risking a denial that silently doesn't hold`,\n };\n }\n const confirmArgs = ['--permission-mode', 'default'];\n if (policy.allowTools && policy.allowTools.length > 0) {\n confirmArgs.push('--tools', policy.allowTools.join(','));\n }\n return { ok: true, args: confirmArgs, needsApprovalMcp: true };\n }\n\n if (policy.mode === 'readonly') {\n const base = policy.allowTools ? policy.allowTools.filter((tool) => READONLY_TOOLS.includes(tool)) : [...READONLY_TOOLS];\n const effective = subtractDenied(base, denyTools);\n // Never fall through to an absent `--tools` flag here — that would run\n // claude's full active toolset, silently widening a readonly request.\n return { ok: true, args: ['--permission-mode', 'default', '--tools', effective.join(',')] };\n }\n\n // `auto` and `plan` share the same tool-restriction logic below — plan\n // mode never executes a mutating call regardless (see the doc comment\n // above), so an `allowTools`/`denyTools` restriction only shapes what the\n // model is even offered to plan around, not a security boundary for plan\n // mode specifically. Kept as one branch for both, rather than duplicating\n // the denyTools fail-closed reasoning twice.\n if (denyTools.length > 0) {\n return {\n ok: false,\n args: [],\n reason: `claude adapter cannot reliably enforce denyTools ([${denyTools.join(', ')}]): its only trustworthy tool-restriction mechanism (--tools) replaces the whole active set rather than subtracting from it, and this adapter has no reliable way to learn a target installation's full default tool set to compute \"default minus denied\" (this dev machine's own installed build exposes a non-vanilla, bespoke tool list) — refusing rather than risking a denial that silently doesn't hold`,\n };\n }\n\n const permissionMode = policy.mode === 'plan' ? 'plan' : 'acceptEdits';\n const args = ['--permission-mode', permissionMode];\n if (policy.allowTools && policy.allowTools.length > 0) {\n args.push('--tools', policy.allowTools.join(','));\n }\n return { ok: true, args };\n}\n\nfunction subtractDenied(tools: readonly string[], denyTools: readonly string[]): string[] {\n const denied = new Set(denyTools);\n return tools.filter((tool) => !denied.has(tool));\n}\n","import { realpathSync } from 'node:fs';\nimport path from 'node:path';\nimport type { AgentEvent } from '@byok-sdk/protocol';\n\n/**\n * A raw parsed line from `claude --output-format stream-json`. Shapes vary\n * a lot by `type` (and, for `system`, by `subtype`) — see the doc comments\n * on the individual mapping functions below for the concrete shapes this\n * was empirically captured against. Kept as a loose bag rather than a full\n * discriminated union for the same reason pi's `PiRpcMessage` is: this\n * module only needs a handful of fields off of each frame.\n */\nexport interface ClaudeStreamMessage {\n type: string;\n [key: string]: unknown;\n}\n\n/**\n * Cross-message correlation state a single {@link ClaudeSession} (in\n * `../claude-adapter.ts`) owns for its whole lifetime.\n *\n * Unlike pi's `tool_execution_start`/`tool_execution_end` frames (which\n * both carry `toolName` directly, so `../pi/events.ts` can stay a pure,\n * stateless function), claude's Messages-API-shaped transcript splits a\n * tool call across two DIFFERENT frame types: the `assistant` frame's\n * `tool_use` content block carries `{id, name, input}`, but the later\n * `user` frame's `tool_result` content block carries only `{tool_use_id,\n * content, is_error}` — no tool name at all. Since this protocol's own\n * `AgentEvent` schema requires `tool_result.tool: string`, this mapper has\n * no choice but to remember `tool_use_id -> name` from the `tool_use` block\n * and look it up when the matching `tool_result` arrives later. This is a\n * genuine, disclosed structural difference from pi, not an arbitrary\n * design choice — see the M2-a report for the full reasoning.\n */\nexport interface ToolUseCorrelation {\n readonly toolNameByUseId: Map<string, string>;\n}\n\nexport function createToolUseCorrelation(): ToolUseCorrelation {\n return { toolNameByUseId: new Map() };\n}\n\nexport interface MapClaudeMessageOptions {\n /** `ctx.workspaceDir` for the task — used only to compute a workspace-relative `name` for a possible `artifact` AgentEvent (see `tryBuildArtifactEvent`'s doc comment). */\n workspaceDir: string;\n}\n\nexport interface MapClaudeMessageResult {\n events: AgentEvent[];\n /**\n * Set when this exact frame (or, for `assistant`/`user` frames, one\n * content block inside it) was genuinely unrecognized — a frame/subtype/\n * block-type this adapter has never been told to expect — as opposed to\n * routine bookkeeping this mapper deliberately ignores (see\n * `ROUTINE_CLAUDE_SYSTEM_SUBTYPES` and the `thinking`/`redacted_thinking`\n * cases below). The caller (`ClaudeSession`'s event iterator) is\n * responsible for actually recording it (via\n * `ClaudeProcessClient.recordUnmappedFrame`) — mirrors pi's\n * `ROUTINE_PI_EVENT_TYPES` check living in `PiSession`'s iterator rather\n * than inside `mapPiMessageToAgentEvent` itself. At most one label per\n * call even if a frame has several unmapped things in it — sufficient\n * for the \"did this regress\" self-diagnosing purpose this exists for,\n * without needing a list.\n */\n unmappedLabel?: string;\n}\n\n/**\n * `system` frame subtypes empirically observed on real stream-json output\n * from the installed claude 2.1.212 binary that carry no `AgentEvent`\n * equivalent — routine bookkeeping, deliberately ignored:\n *\n * - `init`: session/turn start. Carries `session_id`, `tools`, `cwd`,\n * `permissionMode`, etc. `session_id` specifically is NOT read here —\n * `ClaudeProcessClient.waitForInit()` (`process-client.ts`) captures it\n * directly off the raw line as part of this adapter's own\n * `start()`/sessionRef bookkeeping, since it's needed before any\n * `AgentEvent` mapping is even relevant.\n * - `hook_started` / `hook_response`: fired when the user's own Claude Code\n * installation has configured lifecycle hooks (e.g. `SessionStart`) —\n * machine/config-specific, not part of this protocol's surface at all.\n * - `thinking_tokens`: periodic token-count-estimate bookkeeping emitted\n * while the model is reasoning; no user-visible content.\n *\n * A `system` frame whose `subtype` is NOT in this set is treated as\n * genuinely unmapped (see `mapClaudeMessageToAgentEvents`'s `system` case)\n * rather than silently folded into \"system frames are always routine\" —\n * this is deliberately finer-grained than lumping the whole `system` type\n * together, so a future/unobserved subtype (e.g. something compaction- or\n * budget-related) shows up as a one-time warning instead of disappearing\n * the way the pi adapter's own root-cause hang (a real settle event with no\n * mapping, silently swallowed) did before that bug was found.\n */\nexport const ROUTINE_CLAUDE_SYSTEM_SUBTYPES: ReadonlySet<string> = new Set([\n 'init',\n 'hook_started',\n 'hook_response',\n 'thinking_tokens',\n]);\n\n/** Claude tool names whose successful `tool_result` may correspond to a file written into the task workspace — see `tryBuildArtifactEvent`. `Write` is fully empirically confirmed (real `tool_use_result.filePath`/`.type:\"create\"` shape captured live); `Edit`/`NotebookEdit` are included by the same reasoning/convention as `permission-mapping.ts`'s `Glob`/`Grep` note — not independently re-verified frame-by-frame here. */\nconst FILE_WRITING_TOOLS: ReadonlySet<string> = new Set(['Write', 'Edit', 'NotebookEdit']);\n\nconst EXTENSION_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.json': 'application/json',\n '.js': 'text/javascript',\n '.mjs': 'text/javascript',\n '.cjs': 'text/javascript',\n '.ts': 'text/plain',\n '.tsx': 'text/plain',\n '.jsx': 'text/plain',\n '.html': 'text/html',\n '.css': 'text/css',\n '.csv': 'text/csv',\n '.py': 'text/x-python',\n '.yaml': 'application/yaml',\n '.yml': 'application/yaml',\n};\n\n/** Best-effort, extension-based content-type guess — deliberately not a full MIME sniffer (out of scope; `AgentEventSchema.artifact.contentType` just needs a non-empty string). */\nfunction guessContentType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase();\n return EXTENSION_CONTENT_TYPES[ext] ?? 'application/octet-stream';\n}\n\n/**\n * `assistant` frames carry ONE new Messages-API content block per frame in\n * every live capture this adapter's empirical probes observed (never\n * multiple) — this still iterates the whole `content` array defensively\n * rather than assuming array length 1, since nothing in claude's own\n * documented wire contract guarantees that stays true.\n */\nfunction mapAssistant(msg: ClaudeStreamMessage, correlation: ToolUseCorrelation): MapClaudeMessageResult {\n const message = msg.message as { content?: unknown } | undefined;\n const content = message?.content;\n if (!Array.isArray(content)) return { events: [] };\n\n const events: AgentEvent[] = [];\n let unmappedLabel: string | undefined;\n\n for (const raw of content) {\n const block = raw as { type?: unknown; [key: string]: unknown };\n switch (block.type) {\n case 'text':\n if (typeof block.text === 'string') {\n events.push({ type: 'progress', text: block.text });\n }\n break;\n\n case 'tool_use':\n if (typeof block.id === 'string' && typeof block.name === 'string') {\n correlation.toolNameByUseId.set(block.id, block.name);\n events.push({ type: 'tool_use', tool: block.name, input: block.input });\n }\n break;\n\n // Deliberately NOT mapped to `progress` — mirrors pi's own choice to\n // ignore `thinking_delta` sub-events (see `../pi/events.ts`). Two\n // independent reasons: (1) the daemon's `task-runner.ts` folds every\n // `progress` event's text into `task.complete.summary` verbatim —\n // surfacing raw model reasoning there would leak internal\n // chain-of-thought to whatever SaaS embeds this SDK; (2) on several\n // current-generation models this content is empty by design anyway\n // (`display: \"omitted\"` is the default — see the claude-api skill's\n // \"Thinking & Effort\" reference) even though it was NOT empty in this\n // task's own live captures against `claude-haiku-4-5`. `redacted_thinking`\n // is a documented Anthropic Messages API block type never observed\n // live here; treated identically for the same reasoning.\n case 'thinking':\n case 'redacted_thinking':\n break;\n\n default:\n unmappedLabel = unmappedLabel ?? `assistant-block:${String(block.type)}`;\n }\n }\n\n return { events, unmappedLabel };\n}\n\n/**\n * `user` frames in stream-json output are NOT a human sending input — they\n * are claude echoing a `tool_result` back into the transcript (the same\n * \"user\" role the underlying Messages API uses for tool results). Two\n * concrete verbatim shapes captured live:\n *\n * Denied (no permission granted, headless — see `../claude-adapter.ts`'s\n * doc comment for the full approval-model finding):\n * ```json\n * {\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\n * \"content\":\"Claude requested permissions to write to <path>, but you\n * haven't granted it yet.\",\"is_error\":true,\"tool_use_id\":\"toolu_...\"}]},\n * \"tool_use_result\":\"Error: Claude requested permissions to write to\n * <path>, but you haven't granted it yet.\"}\n * ```\n *\n * Successful Write (the shape `tryBuildArtifactEvent` reads):\n * ```json\n * {\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_...\",\n * \"type\":\"tool_result\",\"content\":\"File created successfully at: <path> ...\"}]},\n * \"tool_use_result\":{\"type\":\"create\",\"filePath\":\"<absolute path>\",\n * \"content\":\"...\",\"structuredPatch\":[],\"originalFile\":null,\"userModified\":false}}\n * ```\n *\n * `content` on the `tool_result` block was only ever observed as a plain\n * string in this task's captures (never the content-block-array shape the\n * general Messages API allows) — `output.content` below is passed through\n * as `unknown` regardless, so a future array shape still round-trips\n * correctly without a mapping change.\n */\nfunction mapUser(msg: ClaudeStreamMessage, correlation: ToolUseCorrelation, options: MapClaudeMessageOptions): MapClaudeMessageResult {\n const message = msg.message as { content?: unknown } | undefined;\n const content = message?.content;\n if (!Array.isArray(content)) return { events: [] };\n\n const events: AgentEvent[] = [];\n let unmappedLabel: string | undefined;\n\n for (const raw of content) {\n const block = raw as { type?: unknown; tool_use_id?: unknown; content?: unknown; is_error?: unknown };\n if (block.type !== 'tool_result') {\n unmappedLabel = unmappedLabel ?? `user-block:${String(block.type)}`;\n continue;\n }\n\n const toolUseId = typeof block.tool_use_id === 'string' ? block.tool_use_id : undefined;\n // Falls back to 'unknown' rather than dropping the event: the\n // AgentEventSchema requires a non-empty `tool` string, and a\n // correlation miss here would mean this adapter's own\n // tool_use<->tool_result bookkeeping has a bug — worth surfacing to the\n // caller as a (still-delivered) event with an honest sentinel name,\n // never silently swallowing real tool-result data.\n const tool = (toolUseId && correlation.toolNameByUseId.get(toolUseId)) || 'unknown';\n const isError = block.is_error === true;\n events.push({ type: 'tool_result', tool, output: { content: block.content, isError } });\n\n if (!isError && FILE_WRITING_TOOLS.has(tool)) {\n const artifact = tryBuildArtifactEvent(msg, options.workspaceDir);\n if (artifact) events.push(artifact);\n }\n }\n\n return { events, unmappedLabel };\n}\n\n/**\n * Real claude has no dedicated \"artifact\" wire message the way pi's fixture\n * fakes one (see `../pi/events.ts`'s doc comment on that case) — but for a\n * successful `Write` (confirmed shape above), the SAME `user` frame's\n * top-level `tool_use_result.filePath` sibling field gives this adapter\n * something pi never had: a real, live-observed, absolute path to the file\n * claude just wrote. This turns it into a workspace-relative `name` for the\n * protocol's `artifact` `AgentEvent`.\n *\n * A written file OUTSIDE `workspaceDir` never produces an artifact event —\n * this is not just defensive coding, it is the concrete fix for a real,\n * confirmed case: `--permission-mode plan` writes its own plan document to\n * `~/.claude/plans/<slug>.md`, the user's actual home directory, regardless\n * of cwd (see `permission-mapping.ts`'s doc comment on that finding). That\n * path always resolves outside `workspaceDir` and must never be reported to\n * the daemon as this task's artifact.\n */\nfunction tryBuildArtifactEvent(msg: ClaudeStreamMessage, workspaceDir: string): AgentEvent | undefined {\n const toolUseResult = msg.tool_use_result as { filePath?: unknown } | undefined;\n const filePath = toolUseResult && typeof toolUseResult.filePath === 'string' ? toolUseResult.filePath : undefined;\n if (!filePath) return undefined;\n\n // `workspaceDir` (`ctx.workspaceDir`) and the absolute `filePath` claude\n // reports can each be expressed through a different symlink alias of the\n // same real location — hit empirically on macOS, where `os.tmpdir()`\n // itself is a symlink (`/var/folders/... -> /private/var/folders/...`)\n // and a spawned child process's own `process.cwd()` reports the\n // REALPATH-resolved form regardless of which alias `cwd` was set to when\n // spawning it. Comparing the raw strings directly misclassifies a file\n // that genuinely IS inside the workspace as outside it purely because of\n // which alias each side happened to use — confirmed live: this silently\n // dropped every artifact event for a workspace created under this\n // machine's own `os.tmpdir()` before this fix. Resolve both through\n // `realpath` before comparing — mirrors `task-runner.ts`'s own\n // `openArtifact()`, which resolves the exact same alias-mismatch class\n // for its downstream containment check.\n //\n // Realpath is applied to the *directory*, not the file itself: the file\n // is expected to exist by the time this runs against real claude output\n // (the tool_result frame only arrives after the write actually\n // happened), but resolving the directory rather than the leaf path keeps\n // this correct even when it doesn't (a stale/removed artifact, or a pure\n // unit test constructing a `tool_use_result` without writing real bytes)\n // — falls back to the raw directory on a realpath failure (e.g. it too\n // doesn't exist), matching `openArtifact`'s own defensive fallback.\n const realWorkspaceDir = tryRealpath(workspaceDir) ?? workspaceDir;\n const fileDir = path.dirname(filePath);\n const realFileDir = tryRealpath(fileDir) ?? fileDir;\n const realFilePath = path.join(realFileDir, path.basename(filePath));\n\n const relative = path.relative(realWorkspaceDir, realFilePath);\n if (relative === '' || relative.startsWith('..') || path.isAbsolute(relative)) {\n return undefined;\n }\n\n return { type: 'artifact', name: relative, contentType: guessContentType(filePath) };\n}\n\nfunction tryRealpath(candidate: string): string | undefined {\n try {\n return realpathSync(candidate);\n } catch {\n return undefined;\n }\n}\n\n/**\n * `result` is claude's real \"whole run settled\" signal (the direct\n * counterpart to pi's `agent_settled` — see `../pi/events.ts`'s doc comment on\n * why that distinction mattered there). Confirmed live: it is always the\n * LAST frame of a turn, whether the turn succeeded (`is_error:false`,\n * `subtype:\"success\"`) or failed before ever reaching the model (e.g. an\n * unresolvable `--resume` target: `subtype:\"error_during_execution\"`,\n * `is_error:true`, `errors:[\"No conversation found with session ID: ...\"]`,\n * process exit code 1 — but STILL a clean, parseable stream-json frame,\n * unlike pi's equivalent bad-invocation case which produces no structured\n * output at all and forces the adapter to fall back to a raw stderr tail).\n * `is_error` (not `subtype`) is the field this switches on, since it is a\n * plain boolean rather than a string this adapter would otherwise need to\n * pattern-match against every observed/unobserved subtype spelling.\n *\n * `usage` (pre-freeze protocol addition — see `extractClaudeUsageEvent`'s\n * doc comment) is extracted on BOTH branches, not just success: a real\n * mid-generation API error could still carry partial usage, and an empty/\n * absent `usage` (the confirmed shape on the pre-flight \"no conversation\n * found\" failure — no model call was ever made) just yields no event, same\n * as the success path. Ordering is load-bearing: `usage`, when present, is\n * placed BEFORE `turn_end`/`error` in the returned array — `ClaudeSession`'s\n * event iterator (`../claude-adapter.ts`) buffers this array and drains it\n * in order, and `task-runner.ts`'s `pump()` returns the instant it sees\n * `turn_end`, so anything queued after it would never be read. `error` does\n * not end the read loop the same way, so the ordering there is for\n * consistency rather than a delivery requirement.\n */\nfunction mapResult(msg: ClaudeStreamMessage): MapClaudeMessageResult {\n const usageEvent = extractClaudeUsageEvent(msg.usage);\n // Cross-model review finding: success must require `is_error === false`\n // EXPLICITLY — the previous `!== true` check treated a MISSING or\n // non-boolean `is_error` (a malformed/future `result` frame) as success\n // too, which would have reported task.complete for a turn that never\n // actually said it succeeded. `result` is the wire's own terminal signal\n // (see this function's own doc comment above) — never inferred.\n if (msg.is_error === false) {\n const events: AgentEvent[] = usageEvent ? [usageEvent, { type: 'turn_end' }] : [{ type: 'turn_end' }];\n return { events };\n }\n const errors = Array.isArray(msg.errors) ? msg.errors.filter((e): e is string => typeof e === 'string') : [];\n // Shared between both failure branches below: the SAME \"errors array wins,\n // result string is the fallback\" extraction `is_error === true` has always\n // used. `undefined` means the frame carried neither.\n const diagnostic =\n errors.length > 0 ? errors.join('; ') : typeof msg.result === 'string' && msg.result.length > 0 ? msg.result : undefined;\n const message =\n msg.is_error === true\n ? (diagnostic ?? 'claude reported an error result')\n : // M4 hardening: a missing/invalid `is_error` is already fail-closed\n // (treated as failure, never inferred as success — see above), but\n // this branch used to report ONLY the generic fallback string, even\n // when the frame actually carried a perfectly usable `result`/\n // `errors` payload (real diagnostic content is only read above when\n // `is_error === true` EXACTLY, so a malformed-but-otherwise-populated\n // frame silently lost it). Appending `diagnostic` here (when\n // present) preserves the fail-closed verdict — this is still always\n // an `error` AgentEvent — while no longer discarding whatever the\n // frame actually said. Truncated defensively: this is an\n // unexpected/malformed frame shape, not the normal error path, so\n // the content could in principle be arbitrarily large.\n diagnostic\n ? `claude result frame had a missing/invalid is_error flag (got ${JSON.stringify(msg.is_error)}) — treating as failure, fail-closed; diagnostic content on the frame: ${truncateResultDiagnostic(diagnostic)}`\n : `claude result frame had a missing/invalid is_error flag (got ${JSON.stringify(msg.is_error)}) — treating as failure, fail-closed`;\n const events: AgentEvent[] = usageEvent ? [usageEvent, { type: 'error', message }] : [{ type: 'error', message }];\n return { events };\n}\n\n/** Sane upper bound on how much of a malformed `result` frame's own diagnostic content (`errors`/`result`) gets folded into the fail-closed message above — no existing truncation helper in this file to reuse (the codex adapter's equivalent, `codex-adapter.ts`'s `JSON.stringify(evt).slice(0, 200)`, lives in a different module entirely), so this is its own small, local bound rather than a shared one. */\nconst RESULT_DIAGNOSTIC_MAX_CHARS = 2000;\n\nfunction truncateResultDiagnostic(text: string): string {\n return text.length > RESULT_DIAGNOSTIC_MAX_CHARS ? `${text.slice(0, RESULT_DIAGNOSTIC_MAX_CHARS)}… [truncated]` : text;\n}\n\n/**\n * Maps the `result` frame's `usage` object to a `usage` `AgentEvent`, or\n * `undefined` when nothing usable is present. Field names and shape\n * confirmed via a live minimal probe against the installed claude 2.1.212\n * binary (same version this adapter's other captures were made against —\n * see `claude-adapter.ts`'s class doc comment): a real `result.usage` looks\n * like `{\"input_tokens\":2,\"cache_creation_input_tokens\":35649,\n * \"cache_read_input_tokens\":0,\"output_tokens\":24,\"server_tool_use\":{...},\n * \"service_tier\":\"standard\",...}` — the standard Anthropic Messages API\n * usage shape, carried through verbatim by the Claude Code CLI.\n *\n * Mapping choices:\n * - `inputTokens` <- `input_tokens` verbatim.\n * - `cachedInputTokens` <- `cache_read_input_tokens` (tokens actually SERVED\n * from a prior cache — the same \"reused, cheaper\" semantic as codex's\n * `cached_input_tokens`). Deliberately NOT `cache_creation_input_tokens`\n * (the cost of WRITING a new cache entry — a distinct, more-expensive-not-\n * cheaper concept the wire schema has no separate slot for; dropped\n * rather than conflated).\n * - `outputTokens` <- `output_tokens` verbatim.\n * - `reasoningTokens` / `totalTokens`: never populated for claude — neither\n * a separate reasoning-token count nor a combined total appears anywhere\n * in real claude output (thinking-block content is folded into\n * `output_tokens`, not counted separately), so these stay absent rather\n * than fabricated.\n */\nfunction extractClaudeUsageEvent(rawUsage: unknown): AgentEvent | undefined {\n if (!rawUsage || typeof rawUsage !== 'object') return undefined;\n const usage = rawUsage as Record<string, unknown>;\n const inputTokens = toNonNegativeInt(usage.input_tokens);\n const cachedInputTokens = toNonNegativeInt(usage.cache_read_input_tokens);\n const outputTokens = toNonNegativeInt(usage.output_tokens);\n if (inputTokens === undefined && cachedInputTokens === undefined && outputTokens === undefined) {\n return undefined;\n }\n const event: Extract<AgentEvent, { type: 'usage' }> = { type: 'usage' };\n if (inputTokens !== undefined) event.inputTokens = inputTokens;\n if (cachedInputTokens !== undefined) event.cachedInputTokens = cachedInputTokens;\n if (outputTokens !== undefined) event.outputTokens = outputTokens;\n return event;\n}\n\n/** Matches `AgentEventSchema`'s `usage` fields (`z.number().int().nonnegative().optional()`) — anything else (missing, a string, a float, negative) is treated as not-reported rather than coerced. */\nfunction toNonNegativeInt(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 ? value : undefined;\n}\n\n/**\n * Map one raw claude stream-json line to zero or more normalized\n * `AgentEvent`s (a single `user` frame can produce two: `tool_result` plus\n * a derived `artifact`). See the per-`type` mapping functions above for the\n * concrete, empirically-captured shapes each branch handles.\n */\nexport function mapClaudeMessageToAgentEvents(\n msg: ClaudeStreamMessage,\n correlation: ToolUseCorrelation,\n options: MapClaudeMessageOptions,\n): MapClaudeMessageResult {\n switch (msg.type) {\n case 'assistant':\n return mapAssistant(msg, correlation);\n case 'user':\n return mapUser(msg, correlation, options);\n case 'result':\n return mapResult(msg);\n case 'system': {\n const subtype = typeof msg.subtype === 'string' ? msg.subtype : undefined;\n if (subtype && ROUTINE_CLAUDE_SYSTEM_SUBTYPES.has(subtype)) return { events: [] };\n return { events: [], unmappedLabel: `system:${subtype ?? 'unknown'}` };\n }\n // Per-turn rate-limit-window bookkeeping, unconditionally emitted\n // alongside every result — no `AgentEvent` equivalent, routine.\n case 'rate_limit_event':\n return { events: [] };\n default:\n return { events: [], unmappedLabel: `top-level:${String(msg.type)}` };\n }\n}\n","import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport { AsyncQueue } from '../../util/async-queue';\nimport type { ClaudeStreamMessage } from './events';\n\nexport type SpawnFn = typeof spawn;\n\nexport interface ClaudeProcessClientOptions {\n command: string;\n args: string[];\n cwd: string;\n env: NodeJS.ProcessEnv;\n spawnFn?: SpawnFn;\n}\n\n/** Bound on retained stderr lines, mirroring pi's identical constant/rationale in `../pi/rpc-client.ts`. */\nconst STDERR_RING_CAPACITY = 20;\n\n/**\n * NDJSON process transport for `claude -p --input-format stream-json\n * --output-format stream-json`.\n *\n * Structurally simpler than pi's `PiRpcClient` in one real way, and\n * different (not simpler) in another:\n *\n * - No request/response correlation. pi's RPC mode replies to each command\n * with a `{type:\"response\", id, success, ...}` — claude's stream-json has\n * no such acknowledgement at all; writing a `{\"type\":\"user\",...}` line\n * just starts (or queues) a turn, and the ONLY confirmation is the\n * ordinary event stream itself (starting with a `system/init` frame).\n * There is therefore no `pending` id->resolver map here.\n * - `waitForInit()` exists specifically to compensate for that missing\n * ack: pi's `start()` fails fast because a bad flag/auth error rejects\n * the pending `send()` promise for the first command. Claude's own\n * `AsyncQueue.end()` (used for the `events` stream) is a CLEAN,\n * non-throwing end — a process that crashes before ever emitting a line\n * would otherwise look, from the async-iteration protocol alone, exactly\n * like a session that legitimately produced zero events, silently\n * swallowing the real failure. `waitForInit()` is a dedicated promise\n * that resolves with the real `session_id` once claude's own\n * `system/init` frame arrives, or rejects with the same enriched\n * exit-error `events` would otherwise swallow — this is what lets\n * `ClaudeAdapter.start()` fail loudly and immediately for a bad\n * `--resume` target etc., mirroring pi's own fail-fast contract with a\n * mechanism suited to claude's ack-less protocol instead of copying pi's\n * request/response one verbatim.\n *\n * Framing, stderr-ring, and unmapped-frame-tally-in-exit-error all mirror\n * `../pi/rpc-client.ts`'s already-proven design directly (LF-delimited\n * JSONL, `node:readline` avoided for the same U+2028/U+2029 reason pi's\n * doc comment explains, `close` not `exit` for the same complete-stderr\n * guarantee) — these are generic, sound patterns, not pi-specific logic,\n * so re-implementing them independently here (rather than importing from\n * `../pi/`) keeps this adapter fully self-contained, matching this repo's\n * existing per-adapter isolation.\n */\nexport class ClaudeProcessClient {\n private readonly child: ChildProcessWithoutNullStreams;\n private buffer = '';\n private readonly eventQueue = new AsyncQueue<ClaudeStreamMessage>();\n private closed = false;\n private exitError: Error | undefined;\n private readonly stderrRing: string[] = [];\n private readonly unmappedFrameCounts = new Map<string, number>();\n\n private sessionId: string | undefined;\n private initWaiter: { resolve: (sessionId: string) => void; reject: (err: Error) => void } | undefined;\n\n constructor(options: ClaudeProcessClientOptions) {\n const spawnFn = options.spawnFn ?? spawn;\n this.child = spawnFn(options.command, options.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: ['pipe', 'pipe', 'pipe'],\n }) as ChildProcessWithoutNullStreams;\n\n this.child.stdout.setEncoding('utf8');\n this.child.stdout.on('data', (chunk: string) => this.onData(chunk));\n\n this.child.stderr.setEncoding('utf8');\n this.child.stderr.on('data', (chunk: string) => this.onStderr(chunk));\n\n // `close` (not `exit`) — see pi's rpc-client.ts doc comment for why\n // this matters: it's Node's guarantee every stdout/stderr byte the\n // process wrote has already been delivered, so a bad-flag/auth-failure\n // exit error always carries the complete stderr tail.\n this.child.on('close', (code, signal) => {\n this.onClosed(this.buildExitError(code, signal));\n });\n this.child.on('error', (err) => {\n this.onClosed(err);\n });\n }\n\n /**\n * Write a new user turn onto stdin (`--input-format stream-json`'s wire\n * shape: `{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"type\":\n * \"text\",\"text\":...}]}}`). Used identically for the very first turn\n * (`ClaudeAdapter.start()`) and any later same-session turn\n * (`ClaudeSession.followUp()`) — empirically confirmed live that claude\n * keeps a `--input-format stream-json` process alive across multiple\n * sequential turns on ONE persistent process/session (same `session_id`\n * reported on each turn's own `system/init` and `result` frames), only\n * exiting when stdin is closed or the process is killed. This is the\n * mechanism `followUp()` relies on instead of spawning a fresh\n * `--resume`'d process per follow-up.\n */\n writeUserMessage(text: string): void {\n if (this.closed) {\n throw this.exitError ?? new Error('claude process is closed');\n }\n const line = `${JSON.stringify({ type: 'user', message: { role: 'user', content: [{ type: 'text', text }] } })}\\n`;\n this.child.stdin.write(line, (err) => {\n if (err) {\n console.error(`[byok/claude-adapter] failed to write to claude stdin: ${err.message}`);\n }\n });\n }\n\n /**\n * Resolves with claude's own `session_id` once its `system/init` frame\n * arrives (see this class's doc comment for why this exists at all).\n * Idempotent: once resolved, further calls resolve immediately with the\n * same id; if the process already closed before init ever arrived,\n * every call rejects with that same exit error.\n */\n waitForInit(): Promise<string> {\n if (this.sessionId !== undefined) return Promise.resolve(this.sessionId);\n if (this.closed) return Promise.reject(this.exitError ?? new Error('claude process is closed'));\n return new Promise((resolve, reject) => {\n this.initWaiter = { resolve, reject };\n });\n }\n\n /** Every parsed stream-json line — `system/init` is consumed internally (see `waitForInit`) but is also forwarded here like any other frame, so routine-frame accounting in `ClaudeSession`'s mapper stays uniform. */\n get events(): AsyncIterable<ClaudeStreamMessage> {\n return this.eventQueue;\n }\n\n /**\n * Record a claude stream-json frame/subtype/content-block label that\n * `ClaudeSession`'s event iterator (`../claude-adapter.ts`) decided has\n * no `AgentEvent` mapping and isn't routine bookkeeping (see\n * `events.ts`'s `MapClaudeMessageResult.unmappedLabel` doc comment) —\n * i.e. genuinely unexpected traffic. Mirrors pi's\n * `PiRpcClient.recordUnmappedFrame` exactly: logs once per distinct\n * label, folds the running tally into a later exit error for a\n * post-mortem without separate log scraping.\n */\n recordUnmappedFrame(label: string): void {\n const next = (this.unmappedFrameCounts.get(label) ?? 0) + 1;\n this.unmappedFrameCounts.set(label, next);\n if (next === 1) {\n console.warn(\n `[byok/claude-adapter] claude emitted a frame with no AgentEvent mapping: \"${label}\" (further occurrences of this label won't be logged individually)`,\n );\n }\n }\n\n /** Best-effort teardown. SIGTERM on POSIX; `taskkill /T /F` on Windows to also reap child processes claude itself spawned (e.g. Bash) — mirrors pi's cross-platform `kill()` exactly. Empirically confirmed on this (POSIX) machine: a running claude process exits cleanly within ~1s of SIGTERM (observed exit code 143 = 128+SIGTERM, i.e. claude catches and handles the signal itself rather than needing a harder kill). */\n kill(): void {\n if (this.closed) return;\n const pid = this.child.pid;\n if (process.platform === 'win32' && pid !== undefined) {\n spawnSync('taskkill', ['/pid', String(pid), '/T', '/F']);\n } else {\n this.child.kill('SIGTERM');\n }\n }\n\n private onData(chunk: string): void {\n this.buffer += chunk;\n let newlineIndex = this.buffer.indexOf('\\n');\n while (newlineIndex !== -1) {\n let line = this.buffer.slice(0, newlineIndex);\n this.buffer = this.buffer.slice(newlineIndex + 1);\n if (line.endsWith('\\r')) line = line.slice(0, -1);\n if (line.length > 0) this.onLine(line);\n newlineIndex = this.buffer.indexOf('\\n');\n }\n }\n\n private onLine(line: string): void {\n let msg: ClaudeStreamMessage;\n try {\n msg = JSON.parse(line) as ClaudeStreamMessage;\n } catch {\n return; // a stray non-JSON line is not this client's concern\n }\n\n if (\n this.sessionId === undefined &&\n msg.type === 'system' &&\n msg.subtype === 'init' &&\n typeof msg.session_id === 'string' &&\n msg.session_id.length > 0\n ) {\n this.sessionId = msg.session_id;\n this.initWaiter?.resolve(this.sessionId);\n this.initWaiter = undefined;\n }\n\n this.eventQueue.push(msg);\n }\n\n private onStderr(chunk: string): void {\n for (const rawLine of chunk.split('\\n')) {\n const line = rawLine.trim();\n if (line.length === 0) continue;\n this.stderrRing.push(line);\n if (this.stderrRing.length > STDERR_RING_CAPACITY) this.stderrRing.shift();\n }\n }\n\n /** Mirrors pi's `buildExitError` exactly — stderr tail + unmapped-frame tally folded into one self-diagnosing message. */\n private buildExitError(code: number | null, signal: NodeJS.Signals | null): Error {\n const parts = [`claude process exited (code=${code}, signal=${signal})`];\n if (this.stderrRing.length > 0) {\n parts.push(`stderr: ${this.stderrRing.join(' | ')}`);\n }\n if (this.unmappedFrameCounts.size > 0) {\n const summary = [...this.unmappedFrameCounts.entries()].map(([label, count]) => `${label}×${count}`).join(', ');\n parts.push(`unmapped frame labels seen: ${summary}`);\n }\n return new Error(parts.join('; '));\n }\n\n private onClosed(err: Error): void {\n if (this.closed) return;\n this.closed = true;\n this.exitError = err;\n this.initWaiter?.reject(err);\n this.initWaiter = undefined;\n this.eventQueue.end();\n }\n}\n","import { createInterface } from 'node:readline';\n\n/**\n * M4 Phase 3: the testable core of `byok-approval-mcp` (`byok-approval-mcp.ts`\n * is the thin stdio-wiring entry point — mirrors this repo's existing\n * `bin/commands/*.ts` split: real logic lives in a plain module, the bin\n * script itself is glue no test ever imports directly).\n *\n * `byok-approval-mcp` is the MCP stdio server `claude`'s own\n * `--permission-prompt-tool` spawns AS ITS OWN CHILD PROCESS when the claude\n * adapter runs a task under `PermissionPolicy.mode: 'confirm'` (see\n * `../adapters/claude/permission-mapping.ts`'s `confirm`-mode doc comment).\n * It implements just enough of the MCP stdio transport (JSON-RPC 2.0,\n * newline-delimited, per the spec) to expose ONE tool — empirically\n * confirmed end-to-end against the real installed claude 2.1.216 binary\n * (M4 Phase 3 STEP 0): `initialize` -> `notifications/initialized` ->\n * `tools/list` -> `tools/call`, with the tool's arguments shaped exactly\n * `{tool_name, input, tool_use_id}` (claude's own real wire shape,\n * live-captured) and its expected response shaped\n * `{content:[{type:'text', text: JSON.stringify({behavior:'allow',\n * updatedInput} | {behavior:'deny', message})}]}` — the SAME shape the\n * Claude Agent SDK's in-process `canUseTool` callback returns (see\n * platform.claude.com/docs/en/agent-sdk/user-input), just crossing a\n * process boundary via MCP instead of an in-process function call.\n */\n\nexport const APPROVAL_TOOL_NAME = 'approval_prompt';\n\n/** Bound on how much of a tool call's `input` gets folded into the wire `task.await_approval.summary` — mirrors `events.ts`'s `RESULT_DIAGNOSTIC_MAX_CHARS`/`truncateResultDiagnostic` convention: a human-facing summary, not a full audit record. */\nexport const APPROVAL_SUMMARY_MAX_CHARS = 500;\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/** `${toolName}: ${input}`, bounded — the human-readable description carried into `task.await_approval.summary` and (via the daemon/CLI's own rendering) whatever a real approver actually reads before deciding. */\nexport function summarizeToolCall(toolName: string, input: unknown): string {\n let inputStr: string;\n try {\n inputStr = JSON.stringify(input);\n } catch (err) {\n inputStr = `<unserializable input: ${errorMessage(err)}>`;\n }\n const bounded =\n inputStr.length > APPROVAL_SUMMARY_MAX_CHARS ? `${inputStr.slice(0, APPROVAL_SUMMARY_MAX_CHARS)}… [truncated]` : inputStr;\n return `${toolName}: ${bounded}`;\n}\n\nexport interface ApprovalOutcome {\n approved: boolean;\n reason?: string;\n}\n\n/** What `byok-approval-mcp.ts` (the real entry point) injects — the one real dependency this module has on the outside world. */\nexport interface ApprovalMcpDeps {\n /** Requests a decision from the daemon this task is running on. Any rejection/throw here is treated as fail-closed (deny) by `handleMcpRequest` — never surfaced to claude as a raw protocol error. */\n requestApproval(taskId: string, summary: string): Promise<ApprovalOutcome>;\n}\n\ninterface JsonRpcRequestLike {\n jsonrpc?: unknown;\n id?: unknown;\n method?: unknown;\n params?: unknown;\n}\n\n/**\n * Handles exactly one already-parsed JSON-RPC request object and returns the\n * exact response object to write back (`undefined` for a notification that\n * expects no reply, e.g. `notifications/initialized`) — no stdio/process\n * concerns at all, so tests call this directly with a stub {@link\n * ApprovalMcpDeps} instead of spawning a real process or a real control\n * socket. `serveApprovalMcpOverStdio` below is the only caller in production.\n */\nexport async function handleMcpRequest(\n req: JsonRpcRequestLike,\n deps: ApprovalMcpDeps,\n taskId: string,\n): Promise<Record<string, unknown> | undefined> {\n const id = req.id;\n\n if (req.method === 'initialize') {\n const params = (req.params ?? {}) as { protocolVersion?: unknown };\n return {\n jsonrpc: '2.0',\n id,\n result: {\n protocolVersion: typeof params.protocolVersion === 'string' ? params.protocolVersion : '2024-11-05',\n capabilities: { tools: {} },\n serverInfo: { name: 'byok-approval-mcp', version: '0.0.1' },\n },\n };\n }\n\n if (req.method === 'notifications/initialized') {\n return undefined; // notification — no response\n }\n\n if (req.method === 'tools/list') {\n return {\n jsonrpc: '2.0',\n id,\n result: {\n tools: [\n {\n name: APPROVAL_TOOL_NAME,\n description:\n 'Requests operator approval for a pending tool call. Blocks until a human (or this device\\'s daemon) decides, or the configured timeout elapses (fail-closed deny on timeout).',\n inputSchema: {\n type: 'object',\n properties: {\n tool_name: { type: 'string' },\n input: { type: 'object' },\n },\n },\n },\n ],\n },\n };\n }\n\n if (req.method === 'tools/call') {\n const params = (req.params ?? {}) as { name?: unknown; arguments?: unknown };\n if (params.name !== APPROVAL_TOOL_NAME) {\n return { jsonrpc: '2.0', id, error: { code: -32602, message: `unknown tool \"${String(params.name)}\"` } };\n }\n const args = (params.arguments ?? {}) as { tool_name?: unknown; input?: unknown };\n const toolName = typeof args.tool_name === 'string' ? args.tool_name : 'unknown tool';\n const input = args.input ?? {};\n const summary = summarizeToolCall(toolName, input);\n\n let outcome: ApprovalOutcome;\n try {\n outcome = await deps.requestApproval(taskId, summary);\n } catch (err) {\n // Fail-closed (task's own mandate): a daemon that's unreachable, a\n // control request that times out, or any other failure reaching the\n // approving device must never leave claude's own MCP call unanswered\n // — that risks claude abandoning the whole turn on its own (M4 Phase 3\n // STEP 0 found claude gives up on a permission-prompt-tool call that\n // never answers at all, ~1.5s in) rather than cleanly denying just\n // this one tool call and letting the conversation continue.\n outcome = { approved: false, reason: `could not reach the approving device: ${errorMessage(err)}` };\n }\n\n const payload = outcome.approved\n ? { behavior: 'allow' as const, updatedInput: input }\n : { behavior: 'deny' as const, message: outcome.reason ?? 'denied' };\n return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: JSON.stringify(payload) }] } };\n }\n\n if (id !== undefined) {\n return { jsonrpc: '2.0', id, error: { code: -32601, message: `unknown method: ${String(req.method)}` } };\n }\n return undefined;\n}\n\nexport interface ServeApprovalMcpOptions {\n taskId: string;\n deps: ApprovalMcpDeps;\n /** Defaults to `process.stdin`/`process.stdout` — overridable so tests can drive this over in-memory streams. */\n input?: NodeJS.ReadableStream;\n output?: NodeJS.WritableStream;\n}\n\n/**\n * Wires {@link handleMcpRequest} to real NDJSON stdio (the MCP stdio\n * transport: one JSON-RPC message per line, both directions) — the only\n * genuinely process-shaped piece of this module. Each line is handled\n * independently and asynchronously (never serialized against the others),\n * since claude's own parallel-tool-use can legitimately fire more than one\n * concurrent `tools/call` over the same connection.\n */\nexport function serveApprovalMcpOverStdio(opts: ServeApprovalMcpOptions): void {\n const input = opts.input ?? process.stdin;\n const output = opts.output ?? process.stdout;\n const rl = createInterface({ input, terminal: false });\n\n rl.on('line', (line) => {\n const trimmed = line.trim();\n if (!trimmed) return;\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n return; // a stray non-JSON line is not this server's concern\n }\n void handleMcpRequest(parsed as JsonRpcRequestLike, opts.deps, opts.taskId).then((response) => {\n if (response !== undefined) output.write(`${JSON.stringify(response)}\\n`);\n });\n });\n}\n","import { execFile } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\nimport type { AgentEvent, TaskOfferPayload } from '@byok-sdk/protocol';\nimport {\n PolicyUnsupportedError,\n SteerUnsupportedError,\n type ApprovalChannel,\n type RuntimeAdapter,\n type RuntimeCapabilities,\n type RuntimeDetectResult,\n type RuntimeEnvironmentRequirements,\n type Session,\n type TaskContext,\n} from '../../types';\nimport { resolveClaudeBin, type ResolvedBin } from './resolve-bin';\nimport { withoutProviderCredentials } from '../provider-credential-environment';\nimport { resolveApprovalMcpBin, type ResolvedApprovalMcpBin } from './resolve-approval-mcp-bin';\nimport { mapPermissionPolicyToClaudeArgs } from './permission-mapping';\nimport { createToolUseCorrelation, mapClaudeMessageToAgentEvents, type ToolUseCorrelation } from './events';\nimport { ClaudeProcessClient, type SpawnFn } from './process-client';\nimport { APPROVAL_TOOL_NAME } from '../../bin/approval-mcp-server';\n\n/** The MCP server NAME this adapter registers `byok-approval-mcp` under in the generated `--mcp-config` (arbitrary, local to this file) — combined with {@link APPROVAL_TOOL_NAME} (imported, single-sourced from `bin/approval-mcp-server.ts` so the two can never independently drift) to form the `mcp__<server>__<tool>` identifier `--permission-prompt-tool` expects. */\nexport const APPROVAL_MCP_SERVER_NAME = 'byokapproval';\n\nconst execFileAsync = promisify(execFile);\n\n/** Applied to both `detect()` probe calls (`--version`, `auth status --json`) — mirrors codex-adapter.ts's identical `DETECT_TIMEOUT_MS`/rationale exactly (cross-model review finding: these two claude probes previously had NO timeout at all, unlike codex's, so a hung claude CLI could block daemon startup and every `detect()` call indefinitely). `detect()` runs on every allowlist-narrowed task offer, so a small ceiling is cheap insurance against either probe ever unexpectedly hanging. */\nconst DETECT_TIMEOUT_MS = 5000;\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/** Best-effort teardown of the task-scoped temp `--mcp-config` directory. */\nasync function cleanupMcpConfigDir(dir: string | undefined): Promise<void> {\n if (!dir) return;\n await fs.rm(dir, { recursive: true, force: true }).catch(() => {});\n}\n\nexport interface ClaudeAdapterOptions {\n /** Override bin resolution — tests substitute the fake-claude fixture script. */\n resolveBin?: () => ResolvedBin;\n /** Override process spawning — tests substitute a fake spawn. */\n spawnFn?: SpawnFn;\n /** M4 Phase 3: override `byok-approval-mcp` bin resolution — tests substitute a fixture script instead of computing a real dist path. Mirrors `resolveBin` above. */\n resolveApprovalMcpBin?: () => ResolvedApprovalMcpBin;\n}\n\n/**\n * Claude Code runtime adapter (`claude -p --input-format stream-json\n * --output-format stream-json`) — the M2-a counterpart to `../pi/pi-adapter.ts`.\n * Every behavioral claim in this file's own doc comments and its sibling\n * modules (`events.ts`, `permission-mapping.ts`, `process-client.ts`) was\n * empirically reproduced against the real installed `claude` 2.1.212 binary\n * on a logged-in machine (per this task's own \"do NOT trust docs over the\n * real binary\" mandate — `claude --help` was actively wrong/misleading for\n * `--allowedTools`, see `permission-mapping.ts`) — not inferred from\n * training-data recall or the Claude API/Agent-SDK docs, which describe a\n * DIFFERENT product surface (the Messages API, not this CLI's headless\n * wire format).\n *\n * ## The central finding: claude's headless approval model has no\n * `needs_approval` pause, at all\n *\n * This is the first real use of the `needs_approval` /\n * `Session.resolveApproval` seam any adapter in this codebase has\n * implemented (pi never emits `needs_approval` — see `PiSession\n * .resolveApproval`'s own doc comment) — so this finding directly informs\n * the M2-c protocol-freeze decision on that seam.\n *\n * Empirically (see the M2-a report for the full live-capture evidence):\n * spawning `claude -p` **non-interactively** with a tool call that would\n * normally prompt a human is resolved **synchronously, before the turn\n * continues** — there is no pause, no wait, no later resumption point:\n *\n * - Under `--permission-mode default` (or no flag at all — headless has no\n * TTY to interactively ask), an unapproved tool call is immediately\n * AUTO-DENIED with a synthesized `tool_result`\n * (`\"Claude requested permissions to write to <path>, but you haven't\n * granted it yet.\"`, `is_error:true`) and the run continues normally to\n * its own `result` frame — no hang, and nothing this adapter could ever\n * resume later even if it wanted to.\n * - Under a permissive `--permission-mode` (`acceptEdits`/`bypassPermissions`),\n * the call is auto-GRANTED, again synchronously, again with nothing to\n * pause on.\n *\n * There is consequently no claude stream-json frame this adapter could\n * ever map to the protocol's `needs_approval` `AgentEvent` — the decision\n * is always already made by the time any frame reaches this adapter at\n * all. `resolveApproval()` below throws a descriptive error rather than\n * silently no-op'ing, mirroring `PiSession.resolveApproval`'s own\n * documented reasoning exactly: a caller that ever receives\n * `task.approve`/`task.reject` for one of this adapter's tasks implies\n * something upstream expected approval support this adapter genuinely does\n * not have.\n *\n * `PermissionPolicy.mode: 'confirm'` — the policy mode whose whole point is\n * \"ask a human, then proceed\" — was therefore rejected outright at\n * `start()` through M2/M3 (fail-closed, see `permission-mapping.ts`), never\n * silently downgraded to auto-accept or auto-deny.\n *\n * ## M4 Phase 3 update: a genuine out-of-band pause DOES exist — it is\n * just invisible to everything written above\n *\n * `--permission-prompt-tool` (a DIFFERENT flag from `--permission-mode`,\n * undocumented in `claude --help`'s own output on the installed 2.1.216\n * binary but empirically confirmed accepted — an unrecognized flag is\n * rejected outright with `error: unknown option`, this one is not) makes\n * claude block a turn on a real MCP round-trip to a server it spawns\n * itself, waiting for that server to answer allow/deny before continuing —\n * genuinely pausing, for real wall-clock time (live-verified: an instant\n * allow/deny, AND a deliberate multi-second delayed answer, both worked\n * identically; only a permission-prompt-tool call that never answers AT\n * ALL was found to make claude abandon the turn on its own, after roughly\n * 1.5s — never actually reachable by this design, since the bundled\n * `bin/byok-approval-mcp.ts` always eventually answers within its own\n * configured ceiling).\n *\n * Everything above this section remains true and is NOT superseded by\n * this: claude's own stream-json output still emits nothing while this\n * pause is in progress — the gap between a `tool_use` frame and its\n * `tool_result` is indistinguishable from ordinary model latency on the\n * wire, and there is still no `needs_approval`-shaped frame this adapter's\n * event mapper could ever produce. The pause is real, but it is invisible\n * to `ClaudeSession.events` and to `task-runner.ts`'s `pump()` entirely —\n * it is only ever observable from OUTSIDE this adapter's own process, by\n * the separate MCP-server child process claude itself spawns. This is why\n * `confirm` mode's daemon-side wiring (`task-runner.ts`'s `requestApproval`,\n * `types.ts`'s `ApprovalChannel`) is driven from the control socket, not\n * from any `AgentEvent` — see those files' own doc comments for the full\n * design this finding drove. `confirm` is now SUPPORTED (see\n * `permission-mapping.ts` and `resolveApproval()` below), still fail-closed\n * whenever no approval channel was actually wired up for this session.\n *\n * ## Steering was also found unsupported (a second, related finding)\n *\n * Live-probed via a persistent `--input-format stream-json` process:\n * writing a second `{\"type\":\"user\",...}` message to stdin WHILE a turn is\n * still generating does NOT redirect that in-flight turn — it QUEUES as a\n * separate, subsequent turn, processed only after the first one reaches\n * its own `result`. This is genuinely useful for `followUp()` (a new turn\n * \"after [the session] has gone idle\" — exactly the queued-after-result\n * case), but it is not what `Session.steer`'s \"inject steering text into a\n * running turn (mid-stream)\" contract promises. `capabilities().steer` is\n * therefore `false`, and `steer()` throws rather than silently behaving\n * like a queued follow-up under a name that implies live redirection.\n */\nexport class ClaudeAdapter implements RuntimeAdapter {\n readonly supportsDispatchSelection = true;\n readonly id = 'claude';\n\n constructor(private readonly options: ClaudeAdapterOptions = {}) {}\n\n async detect(): Promise<RuntimeDetectResult> {\n const bin = this.resolveBin();\n try {\n // Empirically confirmed: claude 2.1.212 prints `--version` output to\n // STDOUT. Only stdout is read here because that is what was probed;\n // pi reads both streams instead because its own `--version` channel\n // has moved between pi releases (see ../pi/pi-adapter.ts).\n const { stdout } = await execFileAsync(bin.command, ['--version'], { timeout: DETECT_TIMEOUT_MS });\n const version = stdout.trim();\n const authPresent = await this.probeAuthPresent(bin.command);\n return { present: true, version, authPresent };\n } catch {\n return { present: false };\n }\n }\n\n capabilities(): RuntimeCapabilities {\n // M4 Phase 3: 'confirm' added — see permission-mapping.ts's confirm-mode\n // doc comment for the empirical basis (`--permission-prompt-tool`,\n // live-verified against the real installed binary).\n // `approvalInteractive: true` — the confirm path is genuinely wired end\n // to end: `--permission-prompt-tool` → the out-of-process approval MCP\n // server (`bin/byok-approval-mcp.ts`) → this daemon's control socket →\n // `ClaudeSession.resolveApproval` really resuming the paused turn.\n return {\n steer: false,\n resume: true,\n approvalInteractive: true,\n mcpToolsets: true,\n permissionModes: ['auto', 'readonly', 'plan', 'confirm'],\n };\n }\n\n /**\n * M5: deliberate product-boundary decision, not an oversight — byok's\n * current ToS posture for claude is login-state-only (`claude auth\n * login`'s own OAuth session — see `probeAuthPresent` below), so this\n * adapter declares NO credential env vars at all; env-based API-key\n * passthrough for claude is a separate, still-pending product decision.\n * A product that genuinely needs it can opt in locally per-device via\n * `DaemonConfig.runtimeEnvironment.claude.allow` (`create-daemon.ts`).\n * `baseNames` is empty too: nothing in this adapter reads a\n * claude-specific config-discovery variable (e.g. `CLAUDE_CONFIG_DIR`)\n * today — if a future version of this adapter starts reading one, it\n * belongs here, not left to rely on the platform baseline alone.\n */\n environmentRequirements(): RuntimeEnvironmentRequirements {\n return { credentialNames: [] };\n }\n\n async start(task: TaskOfferPayload, ctx: TaskContext): Promise<Session> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('claude adapter only supports string instructions in M2 (no blob-ref fetch yet)');\n }\n\n const mapping = mapPermissionPolicyToClaudeArgs(ctx.policy);\n if (!mapping.ok) {\n throw new PolicyUnsupportedError(mapping.reason ?? 'policy rejected by claude adapter');\n }\n const modelId = subscriptionModel(task, 'claude');\n\n // M4 Phase 3: `confirm` mode's approval channel. Generating the\n // temp --mcp-config file is a real filesystem side effect (see\n // permission-mapping.ts's `needsApprovalMcp` doc comment for why this\n // lives here, in start(), rather than in the pure mapping function) —\n // deliberately OUTSIDE ctx.workspaceDir (a fresh, 0700 os.tmpdir()\n // subdirectory instead) so it never shows up to the agent's own\n // workspace-scoped Read/Glob/Grep, even though its contents (a storeDir\n // path, productId, and this taskId — no secret/token material at all;\n // the actual control-socket auth token stays under storeDir, unrelated\n // to this file) would be low-value to an agent that found it anyway.\n let mcpConfigDir: string | undefined;\n const taskMcpServers = ctx.mcpServers ?? {};\n const needsMcpConfig = mapping.needsApprovalMcp || Object.keys(taskMcpServers).length > 0;\n if (mapping.needsApprovalMcp) {\n if (!ctx.approvalChannel) {\n // Internal-consistency fail-closed: TaskRunner always populates this\n // (see task-runner.ts's handleOffer) — a missing one here means\n // something upstream is badly wired, not a normal policy rejection.\n throw new PolicyUnsupportedError(\n 'claude adapter requires policy.mode \"confirm\" to be started with an approval channel (TaskContext.approvalChannel) — none was provided',\n );\n }\n if (Object.prototype.hasOwnProperty.call(taskMcpServers, APPROVAL_MCP_SERVER_NAME)) {\n throw new PolicyUnsupportedError(\n `MCP server name \"${APPROVAL_MCP_SERVER_NAME}\" is reserved by the claude approval channel`,\n );\n }\n }\n\n if (needsMcpConfig) {\n mcpConfigDir = await fs.mkdtemp(path.join(os.tmpdir(), 'byok-mcp-'));\n await fs.chmod(mcpConfigDir, 0o700).catch(() => {});\n const mcpConfigPath = path.join(mcpConfigDir, 'mcp-config.json');\n const mcpServers: Record<string, unknown> = { ...taskMcpServers };\n if (mapping.needsApprovalMcp) {\n const approvalChannel = ctx.approvalChannel;\n if (!approvalChannel) throw new Error('unreachable: approval channel checked above');\n const approvalMcpBin = (this.options.resolveApprovalMcpBin ?? resolveApprovalMcpBin)();\n mcpServers[APPROVAL_MCP_SERVER_NAME] = {\n command: approvalMcpBin.command,\n args: approvalMcpBin.args,\n env: {\n BYOK_STORE_DIR: approvalChannel.storeDir,\n BYOK_PRODUCT_ID: approvalChannel.productId,\n BYOK_TASK_ID: approvalChannel.taskId,\n BYOK_APPROVAL_TIMEOUT_MS: String(approvalChannel.timeoutMs),\n },\n };\n }\n await fs.writeFile(mcpConfigPath, JSON.stringify({ mcpServers }), { mode: 0o600 });\n mapping.args = [\n ...mapping.args,\n ...(mapping.needsApprovalMcp\n ? ['--permission-prompt-tool', `mcp__${APPROVAL_MCP_SERVER_NAME}__${APPROVAL_TOOL_NAME}`]\n : []),\n '--mcp-config',\n mcpConfigPath,\n // The generated file is the complete task-scoped MCP authority.\n // Never merge ambient user/project MCP configuration into it.\n '--strict-mcp-config',\n ];\n }\n\n const bin = this.resolveBin();\n const resumeSessionId = task.sessionRef;\n const args = [\n '-p',\n '--input-format',\n 'stream-json',\n '--output-format',\n 'stream-json',\n // REQUIRED alongside `--output-format stream-json` in `--print` mode —\n // empirically confirmed: omitting this exits 1 immediately with\n // \"Error: When using --print, --output-format=stream-json requires\n // --verbose\", before spawning any model call.\n '--verbose',\n ...(modelId ? ['--model', modelId] : []),\n ...(resumeSessionId ? ['--resume', resumeSessionId] : []),\n ...mapping.args,\n ];\n\n const client = new ClaudeProcessClient({\n command: bin.command,\n args,\n cwd: ctx.workspaceDir,\n env: withoutProviderCredentials(ctx.env),\n spawnFn: this.options.spawnFn,\n });\n\n // `--input-format stream-json` expects the first turn's instruction on\n // stdin too, not as a positional CLI argument — empirically confirmed\n // live (this task's persistent-process multi-turn probes never passed\n // a positional prompt at all, relying entirely on this same write for\n // turn one). Using the identical mechanism for turn one and every\n // `followUp()` afterward (see `ClaudeSession.followUp` /\n // `ClaudeProcessClient.writeUserMessage`) avoids two different\n // send-a-prompt code paths.\n client.writeUserMessage(task.instruction);\n\n let sessionRef: string;\n try {\n // Resolves with claude's own real `session_id` off its `system/init`\n // frame — see `ClaudeProcessClient.waitForInit`'s doc comment for why\n // this is needed at all (claude's stream-json protocol has no\n // request/response ack the way pi's RPC mode does) and why, unlike\n // pi's `resolveFreshSessionId`, no separate follow-up round-trip is\n // needed: claude always surfaces `session_id` directly on the very\n // first frame of a successful run, resume or fresh alike (confirmed:\n // a `--resume <id>` run's own `system/init.session_id` always equals\n // the requested id). An unresolvable `--resume` target (or any other\n // immediate failure) never emits an `init` frame at all and instead\n // exits promptly — `waitForInit()` rejects with the enriched exit\n // error in that case (see `process-client.ts`), which is exactly\n // what should make `start()` fail here, fail-closed, never a\n // fabricated sessionRef.\n sessionRef = await client.waitForInit();\n } catch (err) {\n client.kill();\n await cleanupMcpConfigDir(mcpConfigDir);\n throw err;\n }\n\n // Cross-model review finding: the doc comment above states this is\n // \"confirmed\" to always hold empirically — but nothing actually verified\n // it in code, so a future/unobserved claude behavior (or a bug) silently\n // resuming a DIFFERENT session than `task.sessionRef` asked for would\n // have gone completely unnoticed: this adapter would return a\n // `ClaudeSession` for whatever `sessionRef` claude happened to report,\n // running the task against the wrong workspace/history with no signal\n // to the caller at all. Fail closed instead of trusting the assumption.\n if (resumeSessionId !== undefined && sessionRef !== resumeSessionId) {\n client.kill();\n await cleanupMcpConfigDir(mcpConfigDir);\n throw new Error(\n `claude --resume echoed a different session id than requested (requested ${resumeSessionId}, got ${sessionRef}) — refusing to continue in a possibly-wrong session (fail-closed)`,\n );\n }\n\n return new ClaudeSession(\n sessionRef,\n client,\n ctx.workspaceDir,\n ctx.approvalChannel,\n mcpConfigDir,\n modelId,\n );\n }\n\n /**\n * `claude auth status --json` is claude's OWN non-secret login-state\n * signal (see the credential-isolation rule on `RuntimeAdapter` in\n * `../../types.ts`) — empirically confirmed live on this logged-in\n * machine to report `{\"loggedIn\":true,\"authMethod\":\"claude.ai\",\n * \"apiProvider\":\"firstParty\",\"email\":\"...\",\"orgId\":\"...\",\"orgName\":\"...\",\n * \"subscriptionType\":\"max\"}`, with no token/key material anywhere in it.\n * This spawns the binary and parses ONLY its own reported status — it\n * never reads `~/.claude` or any credential file itself, matching pi's\n * `authPresent` computation being limited to environment-variable\n * *names* (`../pi/pi-adapter.ts`'s `KNOWN_PROVIDER_ENV_VARS`), just via\n * claude's own equivalent non-secret probe instead (claude's auth is\n * OAuth-session-based via `claude auth login`, not primarily an env var,\n * so pi's env-var-presence approach doesn't apply here the same way).\n * A failed/unparseable probe (binary present but not logged in, a future\n * claude release changing this output shape, etc.) fails closed to\n * `false` — this never affects `present`, which is solely about whether\n * `--version` itself succeeded.\n */\n private async probeAuthPresent(command: string): Promise<boolean> {\n try {\n const { stdout } = await execFileAsync(command, ['auth', 'status', '--json'], { timeout: DETECT_TIMEOUT_MS });\n const parsed = JSON.parse(stdout) as { loggedIn?: unknown };\n return parsed.loggedIn === true;\n } catch {\n return false;\n }\n }\n\n private resolveBin(): ResolvedBin {\n return (this.options.resolveBin ?? resolveClaudeBin)();\n }\n}\n\nfunction subscriptionModel(\n task: TaskOfferPayload,\n runtimeId: 'claude',\n): string | undefined {\n const selection = task.dispatchSelection;\n if (selection === undefined) return undefined;\n if (selection.lane !== 'subscription' || selection.runtimeId !== runtimeId) {\n throw new PolicyUnsupportedError(\n `claude adapter cannot execute ${selection.lane} selection for runtime ${selection.runtimeId}`,\n );\n }\n return selection.modelId;\n}\n\nclass ClaudeSession implements Session {\n private readonly correlation: ToolUseCorrelation = createToolUseCorrelation();\n\n constructor(\n public readonly sessionRef: string,\n private readonly client: ClaudeProcessClient,\n private readonly workspaceDir: string,\n /** M4 Phase 3: set only when this session was started under `policy.mode: 'confirm'` — see `resolveApproval()`. */\n private readonly approvalChannel?: ApprovalChannel,\n /** Task-scoped temp `--mcp-config` directory, if any — removed in `close()`. */\n private readonly mcpConfigDir?: string,\n private readonly modelId?: string,\n ) {}\n\n get events(): AsyncIterable<AgentEvent> {\n const client = this.client;\n const correlation = this.correlation;\n const workspaceDir = this.workspaceDir;\n return {\n [Symbol.asyncIterator](): AsyncIterator<AgentEvent> {\n const inner = client.events[Symbol.asyncIterator]();\n // A single raw claude frame can map to more than one AgentEvent\n // (e.g. a Write `tool_result` plus a derived `artifact` — see\n // `events.ts`'s `mapUser`) — buffered here and drained before\n // pulling the next raw line, so this iterator still yields exactly\n // one `AgentEvent` per `next()` call like every other adapter's\n // `Session.events`.\n let pending: AgentEvent[] = [];\n // Cross-model re-review finding (P1 regression, the \"claude-hang\n // class\"): set once THIS turn's own `result` frame has been read off\n // `inner` — `result` is claude's real \"whole run settled\" signal and\n // is always the LAST frame of a turn, success or failure alike (see\n // `events.ts`'s `mapResult` doc comment). On the SUCCESS path\n // (`turn_end`) this is inert: `task-runner.ts`'s `pump()` returns the\n // instant it sees `turn_end`, so no further `next()` call ever\n // happens. It matters on the FAILURE/malformed path: `mapResult`\n // maps a non-success result to a plain `error` AgentEvent with no\n // `turn_end` — and unlike codex (one process per turn, whose own\n // process-close watcher ends the queue — see codex-adapter.ts's\n // `runCodexTurn`), claude's process is PERSISTENT: it stays alive\n // after `result`, awaiting a possible `followUp()` write on stdin\n // (see process-client.ts's own doc comment). Nothing would otherwise\n // ever end this iterator, so `task-runner.ts`'s `pump()` would await\n // a raw line that never arrives — a real, confirmed hang, not just a\n // theoretical one. Once `turnSettled` is true and `pending` has been\n // fully drained (every event from the `result` frame delivered),\n // this iterator ends itself (`done:true`) WITHOUT pulling `inner`\n // again — ending only THIS TURN's own exposed event stream, never\n // the underlying session-lifetime `client.events` queue a future\n // `followUp()` still needs, and never killing the process itself\n // (that stays this session's `close()`/`interrupt()`'s job). This is\n // exactly what lets `pump()`'s existing \"iterable ended without\n // turn_end\" branch report the task as Failed instead of hanging.\n let turnSettled = false;\n return {\n async next(): Promise<IteratorResult<AgentEvent>> {\n for (;;) {\n const buffered = pending.shift();\n if (buffered) return { value: buffered, done: false };\n\n if (turnSettled) return { value: undefined as never, done: true };\n\n const { value, done } = await inner.next();\n if (done) return { value: undefined as never, done: true };\n\n if (value.type === 'result') turnSettled = true;\n\n const mapped = mapClaudeMessageToAgentEvents(value, correlation, { workspaceDir });\n if (mapped.unmappedLabel) {\n client.recordUnmappedFrame(mapped.unmappedLabel);\n }\n if (mapped.events.length > 0) {\n pending = mapped.events;\n }\n // Nothing to yield yet (routine frame, or an unmapped one) —\n // loop around and pull the next raw line.\n }\n },\n };\n },\n };\n }\n\n /**\n * Not supported — see the class-level doc comment on `ClaudeAdapter` for\n * the full empirical basis (a message written to stdin mid-turn was\n * proven to queue as a follow-up turn, not redirect the running one).\n * `capabilities().steer` reports `false` for exactly this reason; this\n * throws rather than silently behaving like `followUp()` under the\n * `steer()` name, which would promise live redirection it cannot deliver.\n */\n async steer(): Promise<void> {\n throw new SteerUnsupportedError(\n 'claude',\n 'claude adapter does not support mid-turn steering: writing to claude\\'s stdin while a turn is in flight queues as a separate subsequent turn rather than redirecting the running one (empirically confirmed) — see capabilities().steer',\n );\n }\n\n async followUp(task: TaskOfferPayload): Promise<void> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('claude adapter only supports string instructions in M2 (no blob-ref fetch yet)');\n }\n const requestedModel = subscriptionModel(task, 'claude');\n if (requestedModel !== undefined && requestedModel !== this.modelId) {\n throw new PolicyUnsupportedError(\n `claude persistent session cannot change model from ${this.modelId ?? '(legacy default)'} to ${requestedModel}`,\n );\n }\n // Writes onto the SAME persistent process this session already has\n // open — empirically confirmed live that claude keeps a\n // `--input-format stream-json` process alive across sequential turns,\n // reusing the identical `session_id`, until stdin closes or the\n // process is killed (see `ClaudeProcessClient.writeUserMessage`'s doc\n // comment).\n this.client.writeUserMessage(task.instruction);\n }\n\n /**\n * Real claude has no distinct \"abort but stay alive\" primitive the way\n * pi's RPC mode does (`{type:'abort'}`, after which pi keeps running and\n * stays queryable) — nothing in `claude --help` exposes one, and this\n * task's probes found none. SIGTERM (via `ClaudeProcessClient.kill()`)\n * is the only verified way to stop an in-flight turn, so `interrupt()`\n * and `close()` both resolve to the same underlying action here. This is\n * consistent with how they are actually used together: `task-runner.ts`'s\n * `handleCancel` always calls `interrupt()` immediately followed by\n * `close()` on the same task, never `interrupt()` alone expecting the\n * session to remain usable afterward.\n */\n async interrupt(): Promise<void> {\n this.client.kill();\n }\n\n async close(): Promise<void> {\n this.client.kill();\n await cleanupMcpConfigDir(this.mcpConfigDir);\n }\n\n /**\n * M4 Phase 3: routes into the out-of-band approval channel `start()`\n * threaded through from `TaskContext.approvalChannel` — see that type's\n * own doc comment (`../../types.ts`) for the full design, and\n * `permission-mapping.ts`'s `confirm`-mode doc comment for the empirical\n * basis. `approved`/`reason` map directly onto `ApprovalChannel.resolve`'s\n * own parameters, which in turn resolve the SAME `ApprovalRegistry` entry\n * `bin/byok-approval-mcp.ts`'s pending `approvals.request` control call is\n * awaiting — answering that call is what lets claude's own blocked\n * `tools/call` (and therefore the paused turn) proceed.\n *\n * Still throws when no channel is present — every adapter/session that\n * ISN'T running under `confirm` mode (the overwhelming majority) has\n * nothing to resolve, and a caller receiving `task.approve`/`task.reject`\n * for one of those implies something upstream expected approval support\n * that isn't there, exactly as this method's doc comment always said.\n * `ApprovalChannel.resolve` itself throws the equally-descriptive \"no\n * pending approval\" error for the narrower case (confirm mode, but nothing\n * currently pending) — this method doesn't need its own separate check for\n * that.\n */\n async resolveApproval(approved: boolean, reason?: string): Promise<void> {\n if (!this.approvalChannel) {\n throw new Error(\n 'claude adapter has no approval channel for this session (not running under policy.mode \"confirm\") — under every other mode, claude resolves every permission decision synchronously (auto-denied under a restrictive --permission-mode, auto-granted under a permissive one) before this adapter ever sees the corresponding frame, so there is nothing to resume later',\n );\n }\n await this.approvalChannel.resolve(approved, reason);\n }\n}\n","export interface ResolvedBin {\n command: string;\n source: 'env' | 'path';\n}\n\n/**\n * Resolve the codex CLI executable.\n *\n * Like pi and Claude Code, the real OpenAI Codex CLI (empirically `codex-cli\n * 0.144.5` on the machine this adapter was built/verified against, installed\n * at a plain PATH location — not inside this repo's `node_modules`) is not\n * published as an npm package this SDK could sensibly depend on: it's a\n * standalone global install (native installer / `npm i -g @openai/codex` /\n * homebrew, depending on platform and version). There is no package-relative\n * resolution to attempt: use an explicit override for tests, else a bare\n * PATH lookup.\n *\n * `BYOK_CODEX_BIN` overrides PATH lookup — substituting the fake-codex test\n * fixture, exactly like `BYOK_PI_BIN` does for pi. The `byok-agent` CLI bin\n * only ever constructs `new CodexAdapter()` with no options (mirroring\n * `createDaemon`'s pi wiring), so an out-of-process substitution (e.g. a\n * future e2e harness swapping in a fake binary ahead of a real codex install)\n * has no other seam to use.\n */\nexport function resolveCodexBin(): ResolvedBin {\n const override = process.env.BYOK_CODEX_BIN;\n if (override) {\n return { command: override, source: 'env' };\n }\n return { command: 'codex', source: 'path' };\n}\n","import type { PermissionPolicy } from '@byok-sdk/protocol';\n\nexport interface CodexPermissionMapping {\n ok: boolean;\n /** `-c key=value` args (as separate argv entries, e.g. `['-c', 'sandbox_mode=read-only']`) to append to `codex exec`/`codex exec resume`. Only meaningful when `ok` is true. */\n args: string[];\n /** Present when `ok` is false. */\n reason?: string;\n}\n\n/**\n * Map an effective {@link PermissionPolicy} to `codex exec`/`codex exec\n * resume` CLI args, fail-closed. Empirically grounded against the real\n * installed `codex exec --json` (`codex-cli 0.144.5`) — every claim below was\n * driven live in a scratch directory before being encoded here, exactly like\n * the pi adapter's own M0-3 findings (`../pi/permission-mapping.ts`), and the\n * same class of \"the CLI's own --help lies\" bug showed up again independently\n * (see the confirm/plan case below).\n *\n * - Uses `-c sandbox_mode=<value>` (a config-override key), NEVER the `-s`/\n * `--sandbox` CLI flag, and NEVER `-a`/`--ask-for-approval`:\n * - `-s`/`--sandbox` works on a fresh `codex exec` but is REJECTED\n * outright (\"error: unexpected argument '-s' found\") on `codex exec\n * resume` — confirmed against the real binary, not inferred from\n * --help (which lists `-s` for `exec` but, correctly for once, omits\n * it entirely from `resume`'s own --help).\n * - `-a`/`--ask-for-approval` is documented in `codex exec --help`\n * itself, yet REJECTED outright by `codex exec`'s real arg parser\n * (\"error: unexpected argument '-a' found\") — a second, independent\n * instance of the exact doc/binary mismatch class the pi adapter's own\n * `--session-id`/`--exclude-tools` findings already hit. Never used.\n * - `-c sandbox_mode=...` was confirmed empirically to produce the\n * IDENTICAL restriction as `-s` on a fresh `exec` (a read-only-sandbox\n * write attempt fails the same way either way), and to also work on\n * `resume` (where `-s` cannot be used at all) — so this one mechanism\n * is used everywhere, for both a fresh start and every later resume,\n * instead of switching mechanisms depending on call site.\n * - CRITICAL: `codex exec resume` does NOT auto-inherit the sandbox mode\n * the session was originally started with. Empirically confirmed: a\n * session started with `sandbox_mode=read-only` (which correctly\n * denied a write on its first turn) had that SAME write SUCCEED on a\n * later `codex exec resume` with no sandbox flag re-passed — silently\n * falling back to this machine's own ambient `~/.codex/config.toml`\n * default. This is exactly the \"silently widens\" failure this whole\n * mapping exists to prevent, so this adapter re-pins\n * `-c sandbox_mode=...` (and `-c approval_policy=never`, see below) on\n * *every* invocation — the initial `start()` AND every `followUp()` —\n * never relying on \"it was already set once.\"\n * - `auto` -> `sandbox_mode=workspace-write`. Empirically, this permits\n * writes inside the given cwd (the task workspace) AND the OS temp dir\n * (e.g. `/tmp`/`$TMPDIR`) — NOT strictly confined to the workspace, a\n * real, non-obvious scope worth knowing about (confirmed: a shell write to\n * `/tmp/...` succeeds even though it's outside `ctx.workspaceDir`; a write\n * to `$HOME` is correctly denied with a clean \"operation not permitted\",\n * not a hang or silent no-op).\n * - `readonly` -> `sandbox_mode=read-only`. Empirically denies every write —\n * both `apply_patch`-style file edits and shell-redirection writes — with\n * an immediate, clean, non-interactive denial (real stderr: \"writing is\n * blocked by read-only sandbox; rejected by user approval settings\").\n * Never a hang, never a silent bypass.\n * - `confirm` / `plan` are NOT expressible and fail closed. `codex exec` has\n * no interactive approval channel at all in this build — confirmed two\n * independent ways: (1) the flag that would request one (`-a`/\n * `--ask-for-approval`) is rejected outright by the real parser (see\n * above); (2) even reaching the underlying setting via the `-c\n * approval_policy=...` config-key path (which IS accepted), a\n * sandbox-denied action under a non-`never` policy resolves the \"ask\"\n * *internally* with no wire-visible pause and no way for an external\n * caller to answer it: it just auto-denies, and the model narrates the\n * denial as a normal `agent_message` — there is no `needs_approval`-\n * equivalent signal anywhere in the `--json` stream to map to (see\n * `../codex/events.ts`'s module doc comment). `plan` has no dedicated\n * codex equivalent either (no plan-only/no-exec CLI mode exists on this\n * build). Rather than pretend either is supported, both fail closed here —\n * the same posture as pi's own `confirm`/`plan` rejection.\n * - `network: true` is NOT expressible and fails closed. The one config key\n * that should enable network under `workspace-write`\n * (`sandbox_workspace_write.network_access=true`) was tested three times\n * against the real binary — including a pure `-c`-only invocation with no\n * `-s` flag mixed in, ruling out CLI-flag/config-table interference as the\n * cause — and never restored real network access: a `curl` to a live host\n * still failed every time, though its failure mode shifted from a\n * DNS-level block (exit 6, \"Could not resolve host\") to a data-level block\n * (exit 56, \"Failure receiving network data\"), hinting the override does\n * *something* internally but not enough to trust as a real grant. A\n * control run under `sandbox_mode=danger-full-access` confirmed network\n * genuinely works from this environment (HTTP 200), so the gap is in this\n * specific override on this codex build, not the test methodology. This\n * adapter never silently proceeds without the grant it was asked for, so\n * `network: true` is rejected rather than quietly running with no network\n * and no explanation. `network: false`/unset needs no special handling:\n * both sandbox modes this adapter ever selects have NO network by default\n * (confirmed empirically for both `read-only` and plain `workspace-write`)\n * — the safe case is also the default case, so nothing to enforce.\n * - `allowTools`/`denyTools` are NOT expressible and fail closed when\n * non-empty. `codex exec` has no verified per-tool allow/deny surface —\n * only the coarse `sandbox_mode` dial — unlike pi's real `--tools`/\n * `--no-tools`. Rather than silently drop a requested tool restriction,\n * this rejects it outright.\n * - `approval_policy=never` is pinned unconditionally on every `ok: true`\n * result, for the same \"never trust the ambient default\" reason as\n * `sandbox_mode` above: a fresh `codex exec` invoked with NO sandbox/\n * approval flags at all on the reference machine successfully ran a live\n * network `curl` with zero prompting — proof this machine's own\n * `~/.codex/config.toml` default is more permissive than anything this\n * adapter should ever grant implicitly. Every invocation pins both keys\n * explicitly so behavior never depends on the end user's own codex config.\n */\nexport function mapPermissionPolicyToCodexArgs(policy: PermissionPolicy): CodexPermissionMapping {\n if (policy.mode === 'confirm' || policy.mode === 'plan') {\n return {\n ok: false,\n args: [],\n reason: `codex adapter cannot express permission mode \"${policy.mode}\" (codex exec has no interactive approval channel — sandbox-denied actions resolve internally with no needs_approval-equivalent wire signal, and -a/--ask-for-approval is rejected outright by codex exec's real arg parser despite being documented in --help)`,\n };\n }\n\n if (policy.network === true) {\n return {\n ok: false,\n args: [],\n reason:\n 'codex adapter cannot guarantee network:true (empirically, -c sandbox_workspace_write.network_access=true did not restore real network access on the installed codex build — see this file\\'s module doc comment) — never silently proceeds without the requested grant',\n };\n }\n\n if ((policy.allowTools && policy.allowTools.length > 0) || (policy.denyTools && policy.denyTools.length > 0)) {\n return {\n ok: false,\n args: [],\n reason:\n 'codex adapter cannot express allowTools/denyTools (codex exec has no verified per-tool allow/deny surface, only the coarse sandbox_mode) — never silently ignores a requested tool restriction',\n };\n }\n\n const sandboxMode = policy.mode === 'readonly' ? 'read-only' : 'workspace-write';\n return { ok: true, args: ['-c', `sandbox_mode=${sandboxMode}`, '-c', 'approval_policy=never'] };\n}\n","import path from 'node:path';\nimport type { AgentEvent } from '@byok-sdk/protocol';\nimport type { CodexRawEvent } from './process-runner';\n\n/**\n * `codex exec --json` event catalog, empirically captured (raw JSONL frames,\n * not inferred from docs) driving the real installed `codex-cli 0.144.5`\n * across: plain chat, file writes (`apply_patch`-style), shell commands,\n * network attempts, sandbox denials, an invalid-model API error, and a\n * SIGTERM-killed-then-resumed thread. Two nesting levels: top-level frames\n * (`thread.started`, `turn.started`, `item.started`, `item.completed`,\n * `turn.completed`, `turn.failed`, and a top-level `error` distinct from any\n * item), and — inside `item.started`/`item.completed` — a nested `item.type`\n * (`agent_message`, `command_execution`, `file_change`, `error`).\n *\n * `thread.started` (always the first line codex ever prints, both for a\n * fresh `codex exec` and for `codex exec resume`) is deliberately NOT\n * handled here — it's consumed directly by `../codex-adapter.ts`'s\n * `runCodexTurn` to resolve `Session.sessionRef` before this mapper ever\n * sees a line, mirroring how pi's adapter resolves a fresh session id via\n * `get_state` before constructing its `Session` (`../pi/pi-adapter.ts`'s\n * `resolveFreshSessionId`). It's still listed in\n * {@link ROUTINE_CODEX_EVENT_TYPES} for defensiveness, in case it were ever\n * unexpectedly seen again mid-stream.\n *\n * No case here ever produces `{type: 'needs_approval'}` — deliberately, not\n * an oversight. `codex exec` (this build) has no wire-visible signal for it\n * at all: a sandbox-denied action under any approval policy resolves\n * internally with no pause an external caller could ever answer — confirmed\n * empirically (a `sandbox_mode=read-only` write attempt, and separately an\n * `approval_policy=untrusted` shell command outside the sandbox's own\n * trusted-command allowlist, both either just ran when the sandbox allowed\n * it or were silently auto-denied when it didn't, narrated only as a normal\n * `agent_message` — never a distinct event this mapper could hook). See\n * `../codex-adapter.ts`'s `resolveApproval`, which throws rather than\n * pretending to support a resume path that cannot exist on the wire.\n */\nexport function mapCodexEventToAgentEvents(evt: CodexRawEvent, workspaceDir: string): AgentEvent[] {\n switch (evt.type) {\n case 'turn.completed': {\n // `usage` (input/cached-input/output/reasoning-output token counts) —\n // pre-freeze protocol change (see `packages/protocol/src/agent-event.ts`)\n // added a `usage` AgentEvent variant specifically for this data, which\n // was real and present here but had no wire slot before that. Mapped\n // 1:1 from codex's own field names (`extractCodexUsageEvent` below);\n // no `total_tokens` field has ever been observed on real codex output,\n // so `totalTokens` is never populated here — never synthesized by\n // summing, per this adapter's \"forward only what's actually reported\"\n // convention.\n //\n // Ordering is load-bearing, not stylistic: `usage` is placed BEFORE\n // `turn_end` in the returned array. `codex-adapter.ts`'s `runCodexTurn`\n // pushes each element of this array onto the session's queue in order,\n // and `task-runner.ts`'s `pump()` returns immediately (ending the read\n // loop) the instant it sees `turn_end` — an event queued AFTER it would\n // never be drained. See `../claude/events.ts`'s `mapResult`, which has\n // the identical ordering constraint for the same reason.\n const usageEvent = extractCodexUsageEvent(evt.usage);\n return usageEvent ? [usageEvent, { type: 'turn_end' }] : [{ type: 'turn_end' }];\n }\n\n case 'turn.failed':\n // The real turn-level failure signal — NOT `item.completed{type:\n // 'error'}` (see below, which is informational/non-fatal and can\n // appear even on a fully successful turn). Deliberately does NOT also\n // synthesize a `turn_end`: task-runner.ts's `pump()` only treats an\n // explicit `turn_end` as success, and an events iterable that ends\n // without one already reports `task.fail` on its own (the codex\n // process exits non-zero right after this frame, ending the stream\n // naturally) — so this only needs to make the failure reason visible\n // via `task.progress` before that happens.\n return [{ type: 'error', message: extractErrorMessage(evt.error) ?? 'codex turn failed' }];\n\n case 'error':\n // Top-level (not nested in an item) — empirically seen paired\n // immediately before a `turn.failed`, carrying the same underlying API\n // error message (itself a JSON-encoded string from the provider\n // response) verbatim, deliberately not re-parsed — forwarding exactly\n // what codex reported is more honest than guessing at its structure.\n return [{ type: 'error', message: typeof evt.message === 'string' ? evt.message : 'codex reported an error' }];\n\n case 'item.started':\n return mapItem(evt.item, 'started', workspaceDir);\n\n case 'item.completed':\n return mapItem(evt.item, 'completed', workspaceDir);\n\n // `thread.started`/`turn.started` carry no AgentEvent-mappable content —\n // see the module doc comment above for why `thread.started` is handled\n // upstream instead. Both are also listed in ROUTINE_CODEX_EVENT_TYPES.\n case 'thread.started':\n case 'turn.started':\n return [];\n\n default:\n return []; // caller (codex-adapter.ts) records this as an unmapped frame unless isRoutineCodexEvent(evt)\n }\n}\n\n/**\n * Maps `turn.completed.usage` to a `usage` `AgentEvent`, or `undefined` when\n * nothing usable is present (e.g. `usage: {}`, empirically also seen on real\n * codex — never emit a content-free `usage` event just because the key\n * existed). Field names are verbatim from real codex output — confirmed live\n * against the installed `codex-cli` binary while building this mapping\n * (`turn.completed` -> `{\"usage\":{\"input_tokens\":24963,\"cached_input_tokens\":\n * 9984,\"output_tokens\":5,\"reasoning_output_tokens\":0}}`), matching the\n * shape this adapter's own fixture (`fake-codex.mjs`) and unit tests already\n * encoded from the ORIGINAL M2-b capture. `0` is a real, reportable value\n * (not absence) for every one of these fields — only a non-number/missing\n * value is treated as \"not reported\".\n */\nfunction extractCodexUsageEvent(rawUsage: unknown): AgentEvent | undefined {\n if (!rawUsage || typeof rawUsage !== 'object') return undefined;\n const usage = rawUsage as Record<string, unknown>;\n const inputTokens = toNonNegativeInt(usage.input_tokens);\n const cachedInputTokens = toNonNegativeInt(usage.cached_input_tokens);\n const outputTokens = toNonNegativeInt(usage.output_tokens);\n const reasoningTokens = toNonNegativeInt(usage.reasoning_output_tokens);\n if (\n inputTokens === undefined &&\n cachedInputTokens === undefined &&\n outputTokens === undefined &&\n reasoningTokens === undefined\n ) {\n return undefined;\n }\n const event: Extract<AgentEvent, { type: 'usage' }> = { type: 'usage' };\n if (inputTokens !== undefined) event.inputTokens = inputTokens;\n if (cachedInputTokens !== undefined) event.cachedInputTokens = cachedInputTokens;\n if (outputTokens !== undefined) event.outputTokens = outputTokens;\n if (reasoningTokens !== undefined) event.reasoningTokens = reasoningTokens;\n return event;\n}\n\n/** Matches `AgentEventSchema`'s `usage` fields (`z.number().int().nonnegative().optional()`) — anything else (missing, a string, a float, negative) is treated as not-reported rather than coerced. */\nfunction toNonNegativeInt(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 ? value : undefined;\n}\n\ntype ItemPhase = 'started' | 'completed';\n\nfunction mapItem(rawItem: unknown, phase: ItemPhase, workspaceDir: string): AgentEvent[] {\n if (!rawItem || typeof rawItem !== 'object') return [];\n const item = rawItem as Record<string, unknown>;\n const itemType = item.type;\n\n switch (itemType) {\n case 'agent_message': {\n // Empirically only ever observed as `item.completed` — codex delivers\n // the whole message atomically, not as streamed token deltas the way\n // pi's `text_delta` does (see `../pi/events.ts`). An `item.started`\n // for `agent_message` has never been seen; if it ever is, this falls\n // through to the empty-array default and gets flagged as unmapped\n // (see `isRoutineCodexEvent`/`unmappedFrameKey`) rather than silently\n // treated as equivalent to `.completed`.\n if (phase !== 'completed') return [];\n return typeof item.text === 'string' ? [{ type: 'progress', text: item.text }] : [];\n }\n\n case 'command_execution': {\n const command = typeof item.command === 'string' ? item.command : undefined;\n if (phase === 'started') {\n return command !== undefined ? [{ type: 'tool_use', tool: 'command_execution', input: { command } }] : [];\n }\n return [\n {\n type: 'tool_result',\n tool: 'command_execution',\n output: {\n command,\n aggregatedOutput: item.aggregated_output,\n exitCode: item.exit_code,\n status: item.status,\n },\n },\n ];\n }\n\n case 'file_change': {\n const changes = Array.isArray(item.changes) ? item.changes : [];\n if (phase === 'started') {\n return [{ type: 'tool_use', tool: 'file_change', input: { changes } }];\n }\n return [\n { type: 'tool_result', tool: 'file_change', output: { changes, status: item.status } },\n ...extractArtifactEvents(changes, workspaceDir),\n ];\n }\n\n case 'error': {\n // Informational/warning-level, NOT necessarily a task failure — e.g.\n // \"Exceeded skills context budget...\" (this environment's installed\n // skill count exceeds codex's own model-visible budget) appeared as\n // `item_0` on EVERY single empirical capture made while building this\n // adapter, success or failure alike, and \"Model metadata for `<bad\n // model>` not found. Defaulting to fallback metadata...\" appeared\n // ahead of a turn that otherwise still ran. Forwarded honestly as an\n // `AgentEvent` (task-runner.ts's `pump()` already tolerates `error`\n // AgentEvents as non-fatal progress — see its own `sendArtifact`\n // failure handling, which does the same) rather than pattern-matching\n // on message text to guess which `error` items \"really\" matter, which\n // would be fragile and machine-specific (this exact notice may not\n // even appear on a machine with fewer installed codex skills).\n return typeof item.message === 'string' ? [{ type: 'error', message: item.message }] : [];\n }\n\n default:\n return [];\n }\n}\n\n/**\n * `file_change` items report the changed file's ABSOLUTE path. Converting to\n * a workspace-relative name before emitting an `artifact` AgentEvent matters\n * for two independent reasons, not just style:\n *\n * 1. `task-runner.ts`'s `openArtifact(workspaceDir, name)` resolves `name`\n * via `path.resolve(realWorkspaceDir, name)` — and `path.resolve` returns\n * an absolute second argument VERBATIM, discarding `realWorkspaceDir`\n * entirely (unlike `path.join`). Handing it codex's raw absolute path\n * would only happen to pass its containment check by coincidence (when\n * that absolute path happens to already be textually prefixed by the\n * realpath'd workspace dir) rather than by the intended, robust\n * relative-path contract every other adapter's artifact name follows\n * (pi's fixture, e.g., always uses a simple relative filename).\n * 2. It lets this mapper itself fail closed — skip, don't forward — a\n * `file_change` that (for whatever reason) landed outside the given\n * `workspaceDir` (`path.relative` starting with `..`), rather than\n * silently handing task-runner.ts's own containment check a path it has\n * to reject on this adapter's behalf.\n *\n * `contentType` is a best-effort extension guess: `file_change` items report\n * only `path`/`kind`, never a content type (unlike pi's synthetic artifact\n * message, which carries one directly) — `application/octet-stream` for\n * anything not in the small known-extension table below is an honest \"don't\n * know\" default, not a wrong guess dressed up as a real one.\n */\nfunction extractArtifactEvents(changes: unknown[], workspaceDir: string): AgentEvent[] {\n const events: AgentEvent[] = [];\n for (const rawChange of changes) {\n if (!rawChange || typeof rawChange !== 'object') continue;\n const change = rawChange as Record<string, unknown>;\n const absolutePath = typeof change.path === 'string' ? change.path : undefined;\n const kind = typeof change.kind === 'string' ? change.kind : undefined;\n if (!absolutePath || kind === 'delete') continue; // nothing to upload for a deletion\n\n const relative = path.relative(workspaceDir, absolutePath);\n if (relative.length === 0 || relative.startsWith('..') || path.isAbsolute(relative)) continue; // outside the workspace — never trust/forward (see doc comment above)\n\n events.push({ type: 'artifact', name: relative, contentType: guessContentType(relative) });\n }\n return events;\n}\n\nconst CONTENT_TYPE_BY_EXTENSION: Readonly<Record<string, string>> = {\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.json': 'application/json',\n '.js': 'text/javascript',\n '.mjs': 'text/javascript',\n '.cjs': 'text/javascript',\n '.ts': 'text/plain',\n '.html': 'text/html',\n '.css': 'text/css',\n '.py': 'text/x-python',\n '.yml': 'application/yaml',\n '.yaml': 'application/yaml',\n '.csv': 'text/csv',\n};\n\nfunction guessContentType(relativePath: string): string {\n return CONTENT_TYPE_BY_EXTENSION[path.extname(relativePath).toLowerCase()] ?? 'application/octet-stream';\n}\n\nfunction extractErrorMessage(rawError: unknown): string | undefined {\n if (typeof rawError === 'string') return rawError;\n if (!rawError || typeof rawError !== 'object') return undefined;\n const message = (rawError as Record<string, unknown>).message;\n return typeof message === 'string' ? message : undefined;\n}\n\n/** Top-level codex event types with no `AgentEvent` equivalent, by design (not \"not yet handled\") — kept in sync with the switch cases above sharing this comment. Used only for observability, mirroring pi's `ROUTINE_PI_EVENT_TYPES`: `codex-adapter.ts`'s per-line pump only records a frame as genuinely unmapped (`unmappedFrameKey` + a one-time console.warn) when it maps to an empty array AND its type isn't in this set. */\nexport const ROUTINE_CODEX_EVENT_TYPES: ReadonlySet<string> = new Set(['thread.started', 'turn.started']);\n\nexport function isRoutineCodexEvent(evt: CodexRawEvent): boolean {\n return ROUTINE_CODEX_EVENT_TYPES.has(evt.type);\n}\n\n/** Diagnostic key for unmapped-frame accounting: for `item.*` frames, folds in the nested `item.type` (e.g. `\"item.completed:reasoning\"`) so a never-seen item shape is distinguishable from a never-seen top-level type. */\nexport function unmappedFrameKey(evt: CodexRawEvent): string {\n if (evt.type === 'item.started' || evt.type === 'item.completed') {\n const item = evt.item;\n const itemType = item && typeof item === 'object' && typeof (item as Record<string, unknown>).type === 'string' ? (item as Record<string, unknown>).type : 'unknown';\n return `${evt.type}:${String(itemType)}`;\n }\n return evt.type;\n}\n","import { spawn, spawnSync, type ChildProcessByStdio } from 'node:child_process';\nimport type { Readable } from 'node:stream';\n\nexport type SpawnFn = typeof spawn;\n\n/** stdin is `null` (never a Writable) — this process never pipes stdin to the child; see the module doc comment below for why. */\ntype CodexChildProcess = ChildProcessByStdio<null, Readable, Readable>;\n\n/**\n * One parsed line of `codex exec --json` / `codex exec resume --json`\n * output. Field shapes vary by `type` (see `./events.ts`'s module doc\n * comment for the empirically-captured catalog), so this stays a loose bag\n * rather than a full discriminated union, mirroring `PiRpcMessage` in\n * `../pi/rpc-client.ts`.\n */\nexport interface CodexRawEvent {\n type: string;\n [key: string]: unknown;\n}\n\nexport interface CodexProcessOptions {\n command: string;\n args: string[];\n cwd: string;\n env: NodeJS.ProcessEnv;\n spawnFn?: SpawnFn;\n /** Called once per parsed JSONL line, in arrival order. */\n onEvent: (evt: CodexRawEvent) => void;\n}\n\n/** Bound on retained stderr lines (see `onStderr`/`buildExitError`) — mirrors `STDERR_RING_CAPACITY` in `../pi/rpc-client.ts`. */\nconst STDERR_RING_CAPACITY = 20;\n\n/**\n * Spawns and streams ONE `codex exec` / `codex exec resume` invocation — i.e.\n * exactly one turn.\n *\n * Unlike pi (a single long-lived RPC server process for a whole session's\n * lifetime — see `../pi/rpc-client.ts`), `codex exec` is a one-shot batch\n * process per turn with no persistent request/response channel: it takes its\n * prompt as an argv positional, streams JSONL to stdout for the one turn\n * it's running, and exits. `../codex-adapter.ts`'s `CodexSession` constructs\n * a fresh `CodexProcessRunner` for every turn (the initial `start()` and\n * every later `followUp()`), forwarding each one's lines into the same\n * long-lived event queue.\n *\n * stdin is deliberately never piped to the child (`stdio: ['ignore', 'pipe',\n * 'pipe']`): `codex exec --help` documents that a piped, non-TTY stdin is\n * read and appended to the prompt as a `<stdin>` block even when a prompt was\n * ALSO given as an argv positional, and empirically every single real\n * invocation made while building this adapter logged \"Reading additional\n * input from stdin...\" on stderr regardless of whether a prompt argument was\n * given. Leaving `stdio: ['pipe', ...]` open for stdin and never closing it\n * risks codex blocking on that read forever — exactly the hang class this\n * task was built to avoid (the pi adapter's own `agent_end`/`agent_settled`\n * mismatch left a task stuck `Running` forever in the M0/M1 GLM run).\n * `'ignore'` presents immediate EOF instead, which was verified live with a\n * dedicated Node `child_process` probe before this was written: no hang,\n * clean completion at normal model latency. This adapter never needs to\n * SEND codex anything over stdin — there is no in-band steer/approval\n * protocol (see `../codex-adapter.ts`'s `steer`/`resolveApproval`).\n */\nexport class CodexProcessRunner {\n private readonly child: CodexChildProcess;\n private readonly onEvent: (evt: CodexRawEvent) => void;\n private buffer = '';\n private readonly stderrRing: string[] = [];\n private closed = false;\n private exitCode: number | null = null;\n private exitSignal: NodeJS.Signals | null = null;\n private readonly closedPromise: Promise<void>;\n private resolveClosed!: () => void;\n\n constructor(options: CodexProcessOptions) {\n this.onEvent = options.onEvent;\n const spawnFn = options.spawnFn ?? spawn;\n this.child = spawnFn(options.command, options.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n this.closedPromise = new Promise((resolve) => {\n this.resolveClosed = resolve;\n });\n\n this.child.stdout.setEncoding('utf8');\n this.child.stdout.on('data', (chunk: string) => this.onData(chunk));\n\n this.child.stderr.setEncoding('utf8');\n this.child.stderr.on('data', (chunk: string) => this.onStderr(chunk));\n\n // `close` (not `exit`), mirroring rpc-client.ts's own reasoning: `close`\n // is Node's guarantee every stdout/stderr byte the process wrote has\n // already reached our listeners, so a post-mortem error built afterward\n // always has the complete stderr tail.\n this.child.on('close', (code, signal) => {\n this.exitCode = code;\n this.exitSignal = signal;\n this.finishClosing();\n });\n this.child.on('error', () => {\n // e.g. ENOENT for a missing binary — buildExitError's stderr tail will\n // be empty in this case, but exitCode/exitSignal staying null is\n // itself informative (never claims a fabricated exit code).\n this.finishClosing();\n });\n }\n\n private finishClosing(): void {\n if (this.closed) return;\n this.closed = true;\n this.resolveClosed();\n }\n\n /** Resolves once the child process has fully exited (both exit and stdio-flush guaranteed — see the `close` listener above). Never rejects. */\n waitClosed(): Promise<void> {\n return this.closedPromise;\n }\n\n get isClosed(): boolean {\n return this.closed;\n }\n\n /**\n * Best-effort teardown. SIGTERM on POSIX: SIGINT was empirically confirmed\n * to be silently ignored by `codex exec` (a real, direct test — a 60s\n * shell `sleep` ran to full, unaffected completion despite SIGINT sent at\n * t=4s) — a genuine, evidence-based correction to this task's own initial\n * assumption (\"interrupt: SIGINT — POSIX here\"). SIGTERM was separately\n * confirmed to terminate the process immediately (exit code 143) with no\n * orphaned child processes left behind (the shell command it was running\n * died with it), and — critically — the underlying codex thread remained\n * cleanly resumable afterward via `codex exec resume` (no corruption from\n * killing mid-turn). `taskkill /T /F` on Windows, mirroring\n * `../pi/rpc-client.ts`'s own cross-platform convention.\n */\n kill(): void {\n if (this.closed) return;\n const pid = this.child.pid;\n if (process.platform === 'win32' && pid !== undefined) {\n spawnSync('taskkill', ['/pid', String(pid), '/T', '/F']);\n } else {\n this.child.kill('SIGTERM');\n }\n }\n\n /** Builds a descriptive error folding in the exit code/signal and the stderr tail — mirrors `PiRpcClient.buildExitError`'s reasoning: a post-mortem on a failed start/resume should never need separately re-running codex by hand with a raw JSONL logger to learn why. */\n buildExitError(context: string): Error {\n const parts = [`${context} (exit code=${this.exitCode}, signal=${this.exitSignal})`];\n if (this.stderrRing.length > 0) {\n parts.push(`stderr: ${this.stderrRing.join(' | ')}`);\n }\n return new Error(parts.join('; '));\n }\n\n private onData(chunk: string): void {\n this.buffer += chunk;\n let newlineIndex = this.buffer.indexOf('\\n');\n while (newlineIndex !== -1) {\n let line = this.buffer.slice(0, newlineIndex);\n this.buffer = this.buffer.slice(newlineIndex + 1);\n if (line.endsWith('\\r')) line = line.slice(0, -1);\n if (line.length > 0) this.parseLine(line);\n newlineIndex = this.buffer.indexOf('\\n');\n }\n }\n\n private parseLine(line: string): void {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n return; // a stray non-JSON line is not this runner's concern\n }\n if (!parsed || typeof parsed !== 'object' || typeof (parsed as { type?: unknown }).type !== 'string') {\n return;\n }\n this.onEvent(parsed as CodexRawEvent);\n }\n\n private onStderr(chunk: string): void {\n for (const rawLine of chunk.split('\\n')) {\n const line = rawLine.trim();\n if (line.length === 0) continue;\n this.stderrRing.push(line);\n if (this.stderrRing.length > STDERR_RING_CAPACITY) this.stderrRing.shift();\n }\n }\n}\n","import { execFile } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\nimport { promisify } from 'node:util';\nimport type { AgentEvent, TaskOfferPayload } from '@byok-sdk/protocol';\nimport {\n PolicyUnsupportedError,\n SteerUnsupportedError,\n type RuntimeAdapter,\n type RuntimeCapabilities,\n type RuntimeDetectResult,\n type RuntimeEnvironmentRequirements,\n type Session,\n type TaskContext,\n} from '../../types';\nimport { AsyncQueue } from '../../util/async-queue';\nimport { resolveCodexBin, type ResolvedBin } from './resolve-bin';\nimport { withoutProviderCredentials } from '../provider-credential-environment';\nimport { mapPermissionPolicyToCodexArgs } from './permission-mapping';\nimport { isRoutineCodexEvent, mapCodexEventToAgentEvents, unmappedFrameKey } from './events';\nimport { CodexProcessRunner, type CodexRawEvent, type SpawnFn } from './process-runner';\n\nconst execFileAsync = promisify(execFile);\n\n/** Applied to both `detect()` probe calls — both are local-only and empirically fast (~50-80ms each), but detect() runs on every allowlist-narrowed task offer (see task-runner.ts's `pickAdapter`), so a small ceiling is cheap insurance against either ever unexpectedly hanging. */\nconst DETECT_TIMEOUT_MS = 5000;\n\nexport interface CodexAdapterOptions {\n /** Override bin resolution — tests substitute the fake-codex fixture script. */\n resolveBin?: () => ResolvedBin;\n /** Override process spawning — tests substitute a fake spawn. */\n spawnFn?: SpawnFn;\n}\n\n/**\n * `RuntimeAdapter` for the OpenAI Codex CLI (`codex exec --json`), the M2-b\n * counterpart to `../pi/pi-adapter.ts`. Every empirical claim in this file\n * and its sibling modules (`events.ts`, `permission-mapping.ts`,\n * `process-runner.ts`) was driven live against the real installed `codex-cli\n * 0.144.5` in a scratch directory before being encoded — repeating the pi\n * adapter's own M0-3 discipline (\"docs lied and shipped a nonexistent flag\")\n * independently found the exact same bug class on codex:\n *\n * - `codex exec --help` documents `-a`/`--ask-for-approval`; the real\n * parser rejects it outright on `codex exec` (\"unexpected argument\").\n * - `-s`/`--sandbox` works on a fresh `codex exec` but is rejected outright\n * on `codex exec resume` (whose own --help correctly omits it).\n * - `codex exec resume` does NOT auto-inherit the sandbox mode a session\n * was originally started with — a read-only-started session's write\n * SUCCEEDED on a bare resume with no sandbox override re-passed,\n * silently falling back to this machine's own ambient config default.\n * - This task's own brief assumed SIGINT for `interrupt()`; empirically,\n * `codex exec` ignores SIGINT entirely (a 60s `sleep` ran to completion\n * despite SIGINT at t=4s) — SIGTERM is used instead (confirmed to work:\n * immediate exit, no orphaned children, thread stays resumable after).\n *\n * See `./permission-mapping.ts` and `./process-runner.ts` for the full\n * per-finding writeups (sandbox scope, network, approval model, resume\n * mechanics, stdin handling).\n *\n * Architecture, and how it differs from pi: pi is one long-lived `pi --mode\n * rpc` process for a whole session's lifetime, driven by a bidirectional\n * JSONL request/response protocol (`../pi/rpc-client.ts`). `codex exec` has\n * no such thing — it's a one-shot batch process per turn, prompt in via\n * argv, JSONL out via stdout, process exits. `CodexSession` here instead\n * spawns a fresh `CodexProcessRunner` for every turn (the initial `start()`\n * and every later `followUp()`), and forwards each one's mapped events into\n * one shared, session-lifetime `AsyncQueue` — the thing `Session.events`\n * actually exposes. `sessionRef` is codex's own `thread_id`, learned from\n * `thread.started`, which is reliably the first JSONL line codex ever prints\n * (confirmed across every empirical capture, fresh starts and resumes\n * alike) — `runCodexTurn` below awaits specifically for that line before\n * resolving, mirroring pi's own \"resolve a real session id before\n * constructing the Session, fail closed if you can't\" discipline\n * (`../pi/pi-adapter.ts`'s `resolveFreshSessionId`, finding F8).\n */\nexport class CodexAdapter implements RuntimeAdapter {\n readonly supportsDispatchSelection = true;\n readonly id = 'codex';\n\n constructor(private readonly options: CodexAdapterOptions = {}) {}\n\n async detect(): Promise<RuntimeDetectResult> {\n const bin = this.resolveBin();\n try {\n const versionResult = await execFileAsync(bin.command, ['--version'], { timeout: DETECT_TIMEOUT_MS });\n const version = versionResult.stdout.trim() || versionResult.stderr.trim();\n const authPresent = await this.probeAuthPresent(bin);\n return { present: true, version, authPresent };\n } catch {\n return { present: false };\n }\n }\n\n /**\n * `authPresent` without ever reading `~/.codex/auth.json` (credential-\n * isolation rule, `../../types.ts`): spawns codex's OWN `login status`\n * subcommand and interprets its human-readable report — the exact\n * \"non-secret signal\" this adapter is required to use, and cleaner than\n * pi's env-var-name check since codex's real credential model (on the\n * reference machine) is a ChatGPT OAuth session, not an env var.\n *\n * Two independently-verified channel gotchas apply here, the \"pi lesson\"\n * yet again:\n * - `codex login status`'s human-readable \"Logged in using ChatGPT\"\n * message prints on STDERR, not stdout — both streams are checked\n * here for exactly that reason. pi's `--version` is the same class of\n * hazard from the other direction: its channel has moved between pi\n * releases (see ../pi/pi-adapter.ts), so neither stream is assumed.\n * - The NOT-logged-in message/exit-code shape was deliberately never\n * empirically tested: this machine has a real, live ChatGPT login, and\n * running `codex logout` to observe the negative case would have\n * broken that login for the rest of this session/machine. The match\n * below is intentionally conservative (`/logged in (using|with)/i`,\n * not a bare `\"logged in\"` substring) specifically because a bare\n * substring check would false-positive on a plausible negative message\n * like \"Not logged in\" (itself containing the substring \"logged in\").\n * This is a documented, known gap — flagged for M2-c / a follow-up\n * empirical pass on a logged-out machine, not asserted as verified.\n */\n private async probeAuthPresent(bin: ResolvedBin): Promise<boolean> {\n try {\n const result = await execFileAsync(bin.command, ['login', 'status'], { timeout: DETECT_TIMEOUT_MS });\n return /logged in (using|with)/i.test(`${result.stdout}\\n${result.stderr}`);\n } catch (err) {\n const withStreams = err as { stdout?: string; stderr?: string };\n return /logged in (using|with)/i.test(`${withStreams.stdout ?? ''}\\n${withStreams.stderr ?? ''}`);\n }\n }\n\n capabilities(): RuntimeCapabilities {\n // `approvalInteractive: false` — `codex exec` never emits a\n // `needs_approval`-equivalent event, so there is nothing to resume and\n // `CodexSession.resolveApproval` throws unconditionally.\n return { steer: false, resume: true, approvalInteractive: false, permissionModes: ['auto', 'readonly'] };\n }\n\n /**\n * M5: same deliberate posture as the claude adapter (see its own doc\n * comment) — codex authenticates via its own `codex login`-managed\n * ChatGPT OAuth session (`probeAuthPresent` above), not an env var, so\n * there is no credential env var this adapter needs forwarded; env-based\n * API-key passthrough remains a separate, pending product decision. No\n * `baseNames` either: nothing in this adapter reads a codex-specific\n * config-discovery variable (e.g. `CODEX_HOME`) today.\n */\n environmentRequirements(): RuntimeEnvironmentRequirements {\n return { credentialNames: [] };\n }\n\n async start(task: TaskOfferPayload, ctx: TaskContext): Promise<Session> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('codex adapter only supports string instructions in M2 (no blob-ref fetch yet)');\n }\n\n const mapping = mapPermissionPolicyToCodexArgs(ctx.policy);\n if (!mapping.ok) {\n throw new PolicyUnsupportedError(mapping.reason ?? 'policy rejected by codex adapter');\n }\n const modelId = subscriptionModel(task);\n\n const bin = this.resolveBin();\n const queue = new AsyncQueue<AgentEvent>();\n const recordUnmapped = makeUnmappedFrameRecorder(new Map<string, number>());\n\n // Realpath'd once, up front — see `resolveRealWorkspaceDir`'s doc\n // comment for why this matters even on a single-machine, non-adversarial\n // path (unlike task-runner.ts's own realpath use, which is defending\n // against symlink swaps): `file_change` items report codex's own\n // absolute paths, built from the CHILD PROCESS's `process.cwd()`, which\n // Node/the OS resolve through any symlink in `ctx.workspaceDir` — a\n // stock `os.tmpdir()`-based workspace on macOS (`/var/folders/...`,\n // itself a symlink to `/private/var/folders/...`) hits this on literally\n // every run, not just a contrived edge case.\n const workspaceDir = await resolveRealWorkspaceDir(ctx.workspaceDir);\n\n const runtimeEnv = withoutProviderCredentials(ctx.env);\n const { sessionRef, runner } = await runCodexTurn({\n command: bin.command,\n resumeRef: task.sessionRef,\n instruction: task.instruction,\n modelId,\n policyArgs: mapping.args,\n cwd: ctx.workspaceDir,\n env: runtimeEnv,\n spawnFn: this.options.spawnFn,\n workspaceDir,\n queue,\n recordUnmapped,\n expectedSessionRef: task.sessionRef,\n preparedGit: ctx.gitWorkspace !== undefined,\n });\n\n return new CodexSession({\n sessionRef,\n command: bin.command,\n workspaceDir,\n env: ctx.env,\n spawnFn: this.options.spawnFn,\n queue,\n recordUnmapped,\n initialRunner: runner,\n preparedGit: ctx.gitWorkspace !== undefined,\n modelId,\n });\n }\n\n private resolveBin(): ResolvedBin {\n return (this.options.resolveBin ?? resolveCodexBin)();\n }\n}\n\n/**\n * Self-discovered finding while building this adapter's own test suite\n * (caught by a real timeout, not inspection): `fs.mkdtemp(path.join(os.\n * tmpdir(), ...))` on macOS returns a path through `/var/folders/...`, which\n * is itself a symlink to `/private/var/folders/...` — confirmed with a\n * direct probe (`fs.realpathSync(os.tmpdir())`). A child process spawned\n * with `cwd` set to the *symlinked* form still reports the *realpath'd* form\n * from its own `process.cwd()` (confirmed with a dedicated spawn probe) —\n * and `file_change` items build their absolute `path` from exactly that.\n * Without this, `events.ts`'s `extractArtifactEvents` would compute a\n * `path.relative(workspaceDir, absolutePath)` full of `../` segments for a\n * file genuinely inside the workspace, trip its own outside-the-workspace\n * safety check, and silently drop the artifact event — not a contrived edge\n * case, but the literal default shape of any workspace root built from\n * `os.tmpdir()` on macOS. Mirrors task-runner.ts's own `openArtifact`, which\n * realpaths its workspace root for the identical reason (there, defending\n * against a symlink swap; here, just matching what the child process's own\n * OS-resolved cwd will report) — same fallback too: a realpath failure\n * (workspace deleted out from under it, etc.) falls through to the original\n * path unchanged rather than failing the whole turn over it.\n */\nasync function resolveRealWorkspaceDir(workspaceDir: string): Promise<string> {\n return fs.realpath(workspaceDir).catch(() => workspaceDir);\n}\n\n/** Count of codex frame \"keys\" (see `unmappedFrameKey`) this session has told us have no `AgentEvent` mapping and aren't routine bookkeeping — i.e. genuinely unexpected traffic. Logs once per distinct key, mirroring `PiRpcClient.recordUnmappedFrame`'s exact reasoning: this is the mechanism that turns a future regression (like this task's own root-cause \"read `thread.started` on every line, not just the first\" class of bug would have been) into an immediate, self-diagnosing warning instead of a silent drop. */\nfunction makeUnmappedFrameRecorder(counts: Map<string, number>): (key: string) => void {\n return (key: string): void => {\n const next = (counts.get(key) ?? 0) + 1;\n counts.set(key, next);\n if (next === 1) {\n console.warn(\n `[byok/codex-adapter] codex emitted a frame with no AgentEvent mapping: \"${key}\" (further occurrences of this type won't be logged individually)`,\n );\n }\n };\n}\n\ninterface RunTurnParams {\n command: string;\n resumeRef: string | undefined;\n instruction: string;\n modelId: string | undefined;\n policyArgs: string[];\n cwd: string;\n env: NodeJS.ProcessEnv;\n spawnFn: SpawnFn | undefined;\n workspaceDir: string;\n queue: AsyncQueue<AgentEvent>;\n recordUnmapped: (key: string) => void;\n /**\n * When resuming, the sessionRef this call expects codex to echo back in\n * `thread.started` — required for BOTH `start()`'s externally-supplied\n * `task.sessionRef` and `followUp()`'s own previously-recorded\n * `sessionRef` (see `CodexSession.followUp`'s doc comment for why the\n * latter also passes this now). Fail-closed on a mismatch: the\n * already-spawned runner for this attempt is killed and the call throws\n * rather than silently adopting whatever id codex actually reported —\n * this is a real check, not just a diagnostic log.\n */\n expectedSessionRef?: string | undefined;\n preparedGit?: boolean;\n}\n\ninterface RunTurnResult {\n sessionRef: string;\n runner: CodexProcessRunner;\n}\n\n/**\n * `--skip-git-repo-check` is used for plain workspaces, not derived from policy:\n * real `codex exec` (confirmed live, via this adapter's own e2e run against a\n * plain non-git scratch workspace) refuses to run at all outside a Git\n * repository — \"Not inside a trusted directory and --skip-git-repo-check\n * was not specified\", exit 1, no JSONL emitted — unless this flag is passed.\n * A successfully prepared Git workspace already satisfies that precondition,\n * so the flag is omitted there. `task-runner.ts`'s `resolveWorkspaceDir`\n * creates plain directories (`fs.mkdir(dir, {recursive:true})`, never a git\n * repo), so the plain-workspace path must retain the flag. It carries no\n * sandbox/approval semantics of its own (purely a \"do you want the git-repo\n * safety net\" gate) — confirmed via `codex exec --help`, which documents it\n * separately from every sandbox/approval flag.\n */\nfunction buildArgv(\n resumeRef: string | undefined,\n policyArgs: string[],\n instruction: string,\n modelId: string | undefined,\n preparedGit = false,\n): string[] {\n const base = resumeRef !== undefined ? ['exec', 'resume', resumeRef] : ['exec'];\n return [\n ...base,\n '--json',\n ...(modelId ? ['--model', modelId] : []),\n ...(preparedGit ? [] : ['--skip-git-repo-check']),\n ...policyArgs,\n instruction,\n ];\n}\n\n/**\n * Spawn one `codex exec`/`codex exec resume` turn, await its first line\n * (must be `thread.started` — fail closed otherwise, never fabricate a\n * `sessionRef`, mirroring `../pi/pi-adapter.ts`'s finding F8), then keep\n * pumping the rest of that process's output into `params.queue` in the\n * background for as long as it runs. Shared by both `CodexAdapter.start()`\n * (a fresh turn, or a resume-via-`task.sessionRef`) and\n * `CodexSession.followUp()` (always a resume).\n */\nasync function runCodexTurn(params: RunTurnParams): Promise<RunTurnResult> {\n const argv = buildArgv(\n params.resumeRef,\n params.policyArgs,\n params.instruction,\n params.modelId,\n params.preparedGit,\n );\n\n let firstLineSettled = false;\n let resolveFirstLine!: (ref: string) => void;\n let rejectFirstLine!: (err: Error) => void;\n const firstLine = new Promise<string>((resolve, reject) => {\n resolveFirstLine = resolve;\n rejectFirstLine = reject;\n });\n\n // Set once THIS turn's own mapped events include an explicit `turn_end` —\n // see the `waitClosed()` watcher below (cross-model review finding, the\n // \"pi-hang class\") for why this matters: `turn_end` already told\n // task-runner.ts's `pump()` this specific turn is done, so the shared\n // session-lifetime queue must stay open afterward (a later `followUp()`\n // reuses this exact same queue — see `CodexSession` below).\n let turnEnded = false;\n\n const runner = new CodexProcessRunner({\n command: params.command,\n args: argv,\n cwd: params.cwd,\n env: params.env,\n spawnFn: params.spawnFn,\n onEvent: (evt: CodexRawEvent) => {\n if (!firstLineSettled) {\n firstLineSettled = true;\n if (evt.type === 'thread.started' && typeof evt.thread_id === 'string' && evt.thread_id.length > 0) {\n resolveFirstLine(evt.thread_id);\n } else {\n rejectFirstLine(\n new Error(`codex did not yield thread.started as its first event (got ${JSON.stringify(evt).slice(0, 200)})`),\n );\n }\n return;\n }\n const mapped = mapCodexEventToAgentEvents(evt, params.workspaceDir);\n for (const agentEvent of mapped) {\n if (agentEvent.type === 'turn_end') turnEnded = true;\n params.queue.push(agentEvent);\n }\n if (mapped.length === 0 && !isRoutineCodexEvent(evt)) {\n params.recordUnmapped(unmappedFrameKey(evt));\n }\n },\n });\n\n /**\n * Cross-model review finding (the \"pi-hang class\" — a task that never\n * completes): `codex exec` is one-shot PER TURN (see this module's own\n * doc comment) — a failed turn (`turn.failed`, mapped to an `error`\n * AgentEvent, deliberately NOT a `turn_end`) or any other unexpected exit\n * (crash, an `interrupt()`-triggered SIGTERM) ends the child process\n * without ever producing the one signal task-runner.ts's `pump()` treats\n * as terminal. Nothing else in this file ever called `params.queue.end()`\n * for that case — confirmed reproducible: the events async-iterable's\n * `next()` stays pending forever, 250ms+ after the process has already\n * exited. pi's own `PiRpcClient.onClosed` (`../pi/rpc-client.ts`)\n * unconditionally ends its eventQueue on process close — correct THERE\n * because pi is one long-lived process for a WHOLE session (closing means\n * the whole session is over). Codex is architecturally different: a fresh\n * process per turn, sharing ONE session-lifetime queue across turns (see\n * `CodexSession.followUp()`) — unconditionally ending it here would wrongly\n * terminate a session a later `followUp()` is about to keep using. So:\n * only when THIS turn never reached `turn_end` do we surface a terminal\n * `error` AgentEvent (exit code/signal + stderr ring tail via\n * `buildExitError` — the real reason, never fabricated) and end the queue.\n * `waitClosed()` never rejects (see process-runner.ts), so no `.catch()`\n * is needed; pushing/ending an already-ended queue (e.g. `close()` beat\n * this watcher to it) is a harmless no-op (`AsyncQueue` is idempotent).\n */\n void runner.waitClosed().then(() => {\n if (turnEnded) return;\n params.queue.push({ type: 'error', message: runner.buildExitError('codex exited without completing the turn').message });\n params.queue.end();\n });\n\n // Race the first line against the process closing before ever producing\n // one (bad flag, ENOENT, an unknown resume id — \"Error: thread/resume:\n // ... no rollout found for thread id ...\" — all empirically confirmed to\n // exit fast with no JSONL at all, never a hang). Built as an explicit\n // executor rather than `Promise.race` so the \"losing\" side can never\n // become an unhandled rejection: `runner.waitClosed()` itself only ever\n // resolves (see process-runner.ts), and `firstLine` always has its\n // resolve/reject consumed below regardless of which settles first.\n let sessionRef: string;\n try {\n sessionRef = await new Promise<string>((resolve, reject) => {\n let settled = false;\n firstLine.then(\n (ref) => {\n if (!settled) {\n settled = true;\n resolve(ref);\n }\n },\n (err: unknown) => {\n if (!settled) {\n settled = true;\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n },\n );\n void runner.waitClosed().then(() => {\n if (!settled) {\n settled = true;\n reject(runner.buildExitError('codex exited before yielding an authoritative thread id'));\n }\n });\n });\n } catch (err) {\n runner.kill();\n throw err;\n }\n\n // Cross-model review finding: this used to only `console.warn` and\n // continue on a mismatch — \"a runtime silently falling back to a\n // different session runs the task in the WRONG context.\" `expectedSessionRef`\n // here is always an EXTERNALLY supplied ask (`CodexAdapter.start()`'s\n // `task.sessionRef`, from the server/daemon — never `CodexSession\n // .followUp()`, which intentionally omits it; see that method's own doc\n // comment for why its own internal resume is handled differently). A\n // mismatch against an external expectation means this adapter can no\n // longer trust that codex resumed the workspace/history the caller\n // actually meant — fail closed rather than quietly proceeding.\n if (params.expectedSessionRef !== undefined && sessionRef !== params.expectedSessionRef) {\n runner.kill();\n throw new Error(\n `codex exec resume echoed a different thread id than requested (requested ${params.expectedSessionRef}, got ${sessionRef}) — refusing to continue in a possibly-wrong session (fail-closed)`,\n );\n }\n\n return { sessionRef, runner };\n}\n\ninterface CodexSessionOptions {\n sessionRef: string;\n command: string;\n workspaceDir: string;\n env: NodeJS.ProcessEnv;\n spawnFn: SpawnFn | undefined;\n queue: AsyncQueue<AgentEvent>;\n recordUnmapped: (key: string) => void;\n initialRunner: CodexProcessRunner;\n preparedGit: boolean;\n modelId: string | undefined;\n}\n\nclass CodexSession implements Session {\n /**\n * NOT `readonly` (cross-model review finding): `followUp()` below\n * re-assigns this from the runtime's own CONFIRMED reflected id on each\n * resume — see its own doc comment. Silently keeping the value captured at\n * construction time was the original bug (the NEW id `runCodexTurn`\n * returns was discarded, so every later `followUp()` kept resuming the\n * OLD, potentially stale, id); that stays fixed here. A LATER cross-model\n * re-review found the fix above was itself incomplete: `followUp()` now\n * also verifies the reflected id matches what it asked to resume BEFORE\n * this field is touched — a mismatch throws (fail-closed) instead of ever\n * reaching this assignment, so `sessionRef` only ever advances to an id\n * codex has proven it actually resumed, never one it silently swapped in.\n */\n public sessionRef: string;\n private readonly command: string;\n private readonly workspaceDir: string;\n private readonly env: NodeJS.ProcessEnv;\n private readonly spawnFn: SpawnFn | undefined;\n private readonly queue: AsyncQueue<AgentEvent>;\n private readonly recordUnmapped: (key: string) => void;\n private readonly preparedGit: boolean;\n private readonly modelId: string | undefined;\n private currentRunner: CodexProcessRunner | undefined;\n private closed = false;\n\n constructor(options: CodexSessionOptions) {\n this.sessionRef = options.sessionRef;\n this.command = options.command;\n this.workspaceDir = options.workspaceDir;\n this.env = options.env;\n this.spawnFn = options.spawnFn;\n this.queue = options.queue;\n this.recordUnmapped = options.recordUnmapped;\n this.preparedGit = options.preparedGit;\n this.modelId = options.modelId;\n this.currentRunner = options.initialRunner;\n void this.forgetRunnerOnceClosed(options.initialRunner);\n }\n\n get events(): AsyncIterable<AgentEvent> {\n return this.queue;\n }\n\n private async forgetRunnerOnceClosed(runner: CodexProcessRunner): Promise<void> {\n await runner.waitClosed();\n if (this.currentRunner === runner) this.currentRunner = undefined;\n }\n\n /**\n * New turn, same session, via the real resume mechanism (`codex exec\n * resume <sessionRef> ...`). Maps `task.policy` FRESH on every call — a\n * deliberate, evidence-based divergence from pi's own `followUp()`\n * (`../pi/pi-adapter.ts`), which ignores `task.policy` entirely and relies\n * on argv baked in once at `start()`. That's safe for pi only because pi\n * reuses one already-running RPC process for its whole session lifetime,\n * so there is nothing to re-apply. codex has no such invariant: a resume\n * spawns a BRAND NEW process, and empirically that new process does NOT\n * inherit the sandbox mode the session originally started with (see this\n * file's module doc comment) — omitting a fresh, explicit mapping here\n * would silently run the follow-up turn under this machine's ambient codex\n * config default instead of the policy this specific follow-up was\n * offered under, exactly the silent-widen failure mode this adapter exists\n * to prevent.\n *\n * Cross-model RE-review finding (this corrects the previous wave's own\n * reasoning, quoted below for context): `expectedSessionRef` IS now passed\n * to `runCodexTurn`, set to the exact id this call asked to resume\n * (`resumeRef`, captured up front before the call). The previous version\n * of this method deliberately omitted it — \"this resume targets OUR OWN\n * previously-recorded `sessionRef`, not an externally supplied server\n * expectation crossing a trust boundary... whatever id codex reports back\n * for THIS resume is unambiguously the current, authoritative id for this\n * session\" — but that reasoning let a resume-A/reports-B mismatch silently\n * MIGRATE this session's identity instead of failing closed: codex has no\n * documented contract for re-keying a thread on resume, so a mismatch here\n * must be treated as an error, exactly like `start()`'s own\n * `task.sessionRef` comparison (see `runCodexTurn`'s doc comment on that\n * check) — never silently adopted. `runCodexTurn` kills the (failed) new\n * runner and throws on a mismatch, BEFORE `sessionRef`/`currentRunner`\n * below are ever touched — so a thrown mismatch leaves this session in its\n * previous, still-good state rather than partially migrated. The\n * `sessionRef` return value is still captured and re-assigned below (the\n * ORIGINAL bug this fixes, one wave further back: the return value used to\n * be discarded entirely, so every later `followUp()` kept resuming a\n * stale id even after codex had moved on) — it just can now only ever be\n * the SAME id this call asked to resume, never a silently-different one.\n */\n async followUp(task: TaskOfferPayload): Promise<void> {\n if (typeof task.instruction !== 'string') {\n throw new PolicyUnsupportedError('codex adapter only supports string instructions in M2 (no blob-ref fetch yet)');\n }\n if (this.closed) {\n throw new Error('cannot follow up on a closed codex session');\n }\n\n const mapping = mapPermissionPolicyToCodexArgs(task.policy);\n if (!mapping.ok) {\n throw new PolicyUnsupportedError(mapping.reason ?? 'policy rejected by codex adapter');\n }\n const requestedModel = subscriptionModel(task);\n if (requestedModel !== undefined && requestedModel !== this.modelId) {\n throw new PolicyUnsupportedError(\n `codex persistent session cannot change model from ${this.modelId ?? '(legacy default)'} to ${requestedModel}`,\n );\n }\n // A follow-up with no additive dispatchSelection still pins the model\n // selected when this session object was created. Omitting --model on a\n // new `codex exec resume` process would let ambient config choose a\n // different target while the session silently continued.\n const modelId = this.modelId;\n\n const resumeRef = this.sessionRef;\n let sessionRef: string;\n let runner: CodexProcessRunner;\n try {\n ({ sessionRef, runner } = await runCodexTurn({\n command: this.command,\n resumeRef,\n instruction: task.instruction,\n modelId,\n policyArgs: mapping.args,\n cwd: this.workspaceDir,\n env: withoutProviderCredentials(this.env),\n spawnFn: this.spawnFn,\n workspaceDir: this.workspaceDir,\n queue: this.queue,\n recordUnmapped: this.recordUnmapped,\n expectedSessionRef: resumeRef,\n preparedGit: this.preparedGit,\n }));\n } catch (err) {\n // Fix (queue-leak-on-failure): confirmed empirically with a diagnostic\n // unbounded `for await` over `session.events` — the SAME shared queue\n // this method's own `runCodexTurn` call already writes into is exposed\n // to the caller from before this call even started (`this.queue` ===\n // `session.events`, session-lifetime). `runCodexTurn` already kills the\n // FAILED attempt's own runner before throwing in both its failure\n // branches (a bad/missing first frame, and this exact session-identity\n // mismatch — see its own doc comment), but never touches `this.queue`.\n // The identity-mismatch case in particular routinely reaches\n // `turn.completed` (mapped to `turn_end`) on the wire BEFORE the\n // mismatch is even detected — this turn's own `onEvent` callback keeps\n // draining the child's stdout independently of when `runCodexTurn`'s\n // own promise settles, and a synchronous fixture's whole output can\n // arrive in one 'data' chunk, processed before the \"await sessionRef\"\n // continuation ever runs. That means `runCodexTurn`'s own \"this turn\n // never reached turn_end\" background watcher sees `turnEnded === true`\n // and correctly declines to end the queue itself (see that watcher's\n // doc comment: a later `followUp()` reusing this same queue is the\n // normal case it's protecting) — leaving NOTHING that will ever end\n // it, so a consumer that reads past everything already buffered gets a\n // `next()` that never resolves. Ending it here closes that gap,\n // mirroring `close()`'s own `this.queue.end()` — deliberately NOT\n // setting `this.closed`, since a failed followUp() must not corrode\n // the SESSION itself (a later `followUp()` with a matching id still\n // succeeds — see the cross-model re-review test above); only this\n // queue's usefulness ends, since nothing can safely keep sharing it\n // once a turn's own result has been this explicitly distrusted.\n this.queue.end();\n throw err;\n }\n this.sessionRef = sessionRef;\n this.currentRunner = runner;\n void this.forgetRunnerOnceClosed(runner);\n }\n\n /** Best-effort abort of the current turn. SIGTERM's the currently-running child, if any — see `process-runner.ts`'s `kill()` doc comment for why SIGTERM (not SIGINT) and why this is safe: the underlying codex thread survives and stays resumable, confirmed empirically. A no-op when no turn is currently in flight. */\n async interrupt(): Promise<void> {\n this.currentRunner?.kill();\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.currentRunner?.kill();\n this.queue.end();\n }\n\n /**\n * `codex exec` has no in-band channel to inject text into an already-\n * running turn — confirmed empirically, not assumed: there is no stdin\n * protocol (stdin is never even piped to the child — see\n * `process-runner.ts`'s module doc comment), SIGINT (a plausible\n * interrupt-and-redirect signal) is silently ignored outright, and `codex\n * exec resume` only ever starts a brand NEW turn strictly after the\n * current one has fully finished — it cannot inject into one still in\n * flight. Throws honestly rather than silently no-op-ing, matching this\n * task's explicit instruction and `capabilities().steer === false` above.\n */\n async steer(): Promise<void> {\n throw new SteerUnsupportedError(\n 'codex',\n 'codex adapter does not support steer: codex exec has no in-band channel to inject text into a running turn (no stdin protocol, SIGINT is ignored, and resume only starts a new turn after the current one finishes)',\n );\n }\n\n /**\n * `codex exec` never emits a `needs_approval`-equivalent event on the wire\n * — confirmed empirically (see `events.ts`'s module doc comment): a\n * sandbox-denied action resolves the approval decision internally with no\n * pause an external caller could ever observe or answer, regardless of\n * `approval_policy`. Since this session can therefore never have emitted a\n * `needs_approval` `AgentEvent` in the first place, a caller reaching this\n * method implies something upstream expected approval support that isn't\n * there — thrown as a descriptive error rather than a silent no-op,\n * mirroring `../pi/pi-adapter.ts`'s identical `resolveApproval`.\n */\n async resolveApproval(): Promise<void> {\n throw new Error(\n 'codex adapter does not support approval resume: codex exec never emits a needs_approval-equivalent event (sandbox-denied actions resolve internally with no wire-visible pause)',\n );\n }\n}\n\nfunction subscriptionModel(task: TaskOfferPayload): string | undefined {\n const selection = task.dispatchSelection;\n if (selection === undefined) return undefined;\n if (selection.lane !== 'subscription' || selection.runtimeId !== 'codex') {\n throw new PolicyUnsupportedError(\n `codex adapter cannot execute ${selection.lane} selection for runtime ${selection.runtimeId}`,\n );\n }\n return selection.modelId;\n}\n"]}