@workerdeck/core 0.17.0 → 0.19.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,"file":"index.mjs","names":["#done","#waiter","#buffer","#listeners","#recordFor","#open","#settle","#records","#sweep","#settleCounter","DEFAULT_APPROVAL_TIMEOUT_MS","#cwd","#config","#permissionMode","#status","#sdkSessionId","#seq","#apiKeySource","#pending","#model","#activityCount","#subagents","#title","#totalCostUsd","#numTurns","#lastActivityAt","#engineTitle","#started","#runPromise","#run","#closed","#input","#emit","#query","#settleApproval","#setStatus","#events","#subscribers","#resetSeq","sdkQuery","#backfillHistory","#buildOptions","#fetchCapabilities","#fetchContextUsage","#fetchRateLimits","#handleMessage","#canUseTool","#turnOverWhileBlocked","#fetchEngineTitle","#capabilitiesEmitted","#subscriptionType","#resolveQuestionByPolicy","#statusDetail","#config","#model","#permissionMode","#modelAlias","#restore","#seq","#events","#activityCount","#messages","#pendingToolCalls","#dispatched","#numTurns","#totalUsage","#turnAccum","#lastActivityAt","#status","#modelId","#title","#started","#turnChain","#setStatus","#closed","#parked","#abort","#restingOnDeferred","#buildSnapshot","#subscribers","#emit","#scheduleTurn","#settlePendingCall","#runTurn","#applyExecutionResult","#announceParked","#dispatchPending","#finishTurn","#options","#execute","#allowsNetwork","#fetchText","#slots","#settle","#options","#early","#applyAnswer","#options","sdkListSessions","#output","#feed","#closed","#nextId","#pending","#write","#notificationHandler","#requestHandler","#buffer","#dispatch","#cwd","#config","#permissionMode","#model","#reasoningEffort","#sdkSessionId","#status","#seq","#approvals","#resolvedModel","#activityCount","#title","#totalCostUsd","#numTurns","#lastActivityAt","#started","#turnChain","#backfillPending","#backfillHistory","#setStatus","#probeSkills","#openScratchConnection","#closed","#refreshSkills","#childEnv","#buildInput","#emit","#queue","#scheduleTurn","#imageDir","#settleApproval","#interruptTurn","#activeTurn","#connection","#events","#subscribers","#runTurn","#ensureThread","#threadLoaded","#handleNotification","#answerServerRequest","#resolvedEffort","#resumedHistory","#skillsRefresh","#skillsFingerprint","#mcpStatus","#producedPaths","#replayTurns","#newTurnState","#replayingHistory","#handleItemCompleted","#finishTurn","#notifications","#emitDelta","#handleItemProgress","#itemProgress","#reasoningDelta","#emitRateLimits","#requestApproval","#resolveQuestionByPolicy","#emitToolUse","#emitFileProduced","#itemCompleted","#emitAssistant","#emitToolResult","#emitContextUsage","#planType"],"sources":["../src/lib/attachments.ts","../src/lib/input-queue.ts","../src/lib/patch.ts","../src/lib/normalize.ts","../src/lib/replay.ts","../src/lib/subscribers.ts","../src/engines/claude/subagents.ts","../src/engines/claude/runner.ts","../src/engines/provider/runner.ts","../src/engines/claude/auth.ts","../src/executors/quickjs-executor.ts","../src/lib/pending-registry.ts","../src/executors/browser-bridge-executor.ts","../src/executors/deferred-executor.ts","../src/engines/provider/tools.ts","../src/engines/provider/web-fetch.ts","../src/engines/provider/session.ts","../src/engines/claude/catalog.ts","../src/engines/claude/adapter.ts","../src/engines/codex/jsonrpc.ts","../src/engines/codex/runner.ts","../src/engines/codex/catalog.ts","../src/engines/codex/process.ts","../src/engines/codex/adapter.ts","../src/engines/provider/adapter.ts","../src/engines/adapter.ts"],"sourcesContent":["import type { MessageAttachment } from '@workerdeck/protocol'\n\n/**\n * An attachment plus its bytes — what the host hands a runner at send time.\n *\n * The split matters: `data` goes into the message the engine sends and nowhere\n * else. What the runner emits into the seq-numbered event log is the\n * {@link MessageAttachment} half, so replay and parking stay cheap (see the\n * protocol's note on why the bytes are not on the wire).\n */\nexport type AttachmentInput = MessageAttachment & {\n /** Base64, no data-URL prefix. */\n data: string\n}\n\n/**\n * How an attachment reaches the model. Not every file can be handed to a model\n * as itself: images and PDFs have native block types, anything textual can be\n * inlined, and the rest has no representation at all — so uploads of it are\n * refused at the door rather than silently dropped from the message.\n */\nexport type AttachmentKind = 'image' | 'document' | 'text'\n\n/** The four the Anthropic API accepts. Notably absent: image/heic — an iPhone's\n * native photo format, which clients must transcode before upload. */\nconst IMAGE_TYPES = new Set(['image/jpeg', 'image/png', 'image/gif', 'image/webp'])\n\n/** Textual types whose media type doesn't start with `text/`. */\nconst TEXT_TYPES = new Set([\n 'application/json',\n 'application/xml',\n 'application/yaml',\n 'application/x-yaml',\n 'application/toml',\n 'application/javascript',\n 'application/typescript',\n 'application/x-sh',\n 'application/x-httpd-php',\n 'application/sql',\n])\n\n/** Strips any `; charset=…` parameter and lowercases. */\nexport function normalizeMediaType(mediaType: string): string {\n return mediaType.split(';')[0]!.trim().toLowerCase()\n}\n\n/** How this media type can be sent, or null if it can't be. */\nexport function attachmentKind(mediaType: string): AttachmentKind | null {\n const type = normalizeMediaType(mediaType)\n if (IMAGE_TYPES.has(type)) return 'image'\n if (type === 'application/pdf') return 'document'\n if (type.startsWith('text/') || TEXT_TYPES.has(type)) return 'text'\n return null\n}\n\n/** Human-readable list for the 415 an unsupported upload gets. */\nexport const SUPPORTED_ATTACHMENT_TYPES = [...IMAGE_TYPES, 'application/pdf', 'text/*'].join(', ')\n\n/**\n * Anthropic content blocks for a set of attachments, in the given order.\n *\n * Blocks lead the message and the user's text follows: the model reads the\n * picture, then the instruction about it. Text files are inlined in a named\n * envelope rather than as a bare block, so \"here is my config\" doesn't read as\n * something the user typed.\n *\n * Structurally typed — `packages/core` models Anthropic content the way\n * `packages/protocol` does, and the caller casts into the SDK's own param type.\n */\nexport function attachmentContentBlocks(\n attachments: readonly AttachmentInput[],\n): Array<Record<string, unknown>> {\n return attachments.map((attachment) => {\n const mediaType = normalizeMediaType(attachment.mediaType)\n switch (attachmentKind(mediaType)) {\n case 'image':\n return {\n type: 'image',\n source: { type: 'base64', media_type: mediaType, data: attachment.data },\n }\n case 'document':\n return {\n type: 'document',\n source: { type: 'base64', media_type: mediaType, data: attachment.data },\n title: attachment.name,\n }\n case 'text':\n return {\n type: 'text',\n text: `<attachment name=\"${attachment.name}\" type=\"${mediaType}\">\\n${decodeText(attachment.data)}\\n</attachment>`,\n }\n default:\n throw new Error(`unsupported attachment media type: ${attachment.mediaType}`)\n }\n })\n}\n\n/** Strip the bytes: the log-safe half of an attachment. */\nexport function attachmentRef(attachment: AttachmentInput): MessageAttachment {\n return {\n id: attachment.id,\n name: attachment.name,\n mediaType: attachment.mediaType,\n bytes: attachment.bytes,\n }\n}\n\nfunction decodeText(base64: string): string {\n return Buffer.from(base64, 'base64').toString('utf8')\n}\n","import type { SDKUserMessage } from '@anthropic-ai/claude-agent-sdk'\n\n/**\n * Push-based single-consumer AsyncIterable bridging imperative sendMessage() calls\n * into the streaming `prompt` the Agent SDK consumes.\n */\nexport class InputQueue implements AsyncIterable<SDKUserMessage> {\n #buffer: SDKUserMessage[] = []\n #waiter: ((result: IteratorResult<SDKUserMessage>) => void) | null = null\n #done = false\n\n push(message: SDKUserMessage): void {\n if (this.#done) return\n if (this.#waiter) {\n const resolve = this.#waiter\n this.#waiter = null\n resolve({ value: message, done: false })\n } else {\n this.#buffer.push(message)\n }\n }\n\n end(): void {\n if (this.#done) return\n this.#done = true\n if (this.#waiter) {\n const resolve = this.#waiter\n this.#waiter = null\n resolve({ value: undefined, done: true })\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<SDKUserMessage> {\n return {\n next: (): Promise<IteratorResult<SDKUserMessage>> => {\n const buffered = this.#buffer.shift()\n if (buffered !== undefined) return Promise.resolve({ value: buffered, done: false })\n if (this.#done) return Promise.resolve({ value: undefined, done: true })\n return new Promise((resolve) => {\n this.#waiter = resolve\n })\n },\n return: (): Promise<IteratorResult<SDKUserMessage>> => {\n this.end()\n return Promise.resolve({ value: undefined, done: true })\n },\n }\n }\n}\n","import type { FilePatch, PatchHunk } from '@workerdeck/protocol'\n\n/**\n * Turning an engine's edit output into the wire's {@link FilePatch}.\n *\n * Both engines know exactly which lines of which file changed, and both say so\n * in their own vocabulary: the Claude SDK hands over a `structuredPatch` array\n * on `SDKUserMessage.tool_use_result`, codex puts a unified diff string on each\n * `fileChange` item. A client can reconstruct neither — it has never seen the\n * file — so anything not normalized here is a diff that renders without line\n * numbers.\n *\n * Normalizing in the runner rather than in each client is the point: one shape\n * reaches the wire, and the dashboard, the extension and the phone all render\n * from it without a per-engine branch or a diff parser of their own.\n */\n\n/**\n * The most lines a patch may put on the wire.\n *\n * A patch is replayed on every attach and captured into parking snapshots, so\n * \"the diff is big\" must not become \"this session is expensive to open forever\".\n * Whole hunks are kept or dropped — half a hunk has misleading line numbers —\n * and the drop is flagged so a renderer can say the diff is partial instead of\n * presenting it as the whole change.\n */\nconst MAX_PATCH_LINES = 400\n\nfunction capHunks(hunks: PatchHunk[]): { hunks: PatchHunk[]; truncated?: boolean } {\n const kept: PatchHunk[] = []\n let lines = 0\n for (const hunk of hunks) {\n if (lines + hunk.lines.length > MAX_PATCH_LINES && kept.length > 0) {\n return { hunks: kept, truncated: true }\n }\n kept.push(hunk)\n lines += hunk.lines.length\n }\n return { hunks: kept }\n}\n\n/** Structural, not `instanceof`: this reads a field the SDK types as `unknown`,\n * and a shape check is the only honest way to know what arrived. */\nfunction isHunk(value: unknown): value is PatchHunk {\n const hunk = value as Partial<PatchHunk> | null\n return (\n !!hunk &&\n typeof hunk.oldStart === 'number' &&\n typeof hunk.oldLines === 'number' &&\n typeof hunk.newStart === 'number' &&\n typeof hunk.newLines === 'number' &&\n Array.isArray(hunk.lines) &&\n hunk.lines.every((line) => typeof line === 'string')\n )\n}\n\n/**\n * A {@link FilePatch} from the Claude SDK's structured tool output\n * (`SDKUserMessage.tool_use_result` for Edit/Write/NotebookEdit).\n *\n * Everything else on that object is deliberately left behind — `originalFile`\n * alone is the entire pre-edit file, which is precisely what must not be logged\n * (see `FilePatch`'s own note).\n */\nexport function filePatchFromToolResult(result: unknown): FilePatch | undefined {\n const output = result as\n | { filePath?: unknown; structuredPatch?: unknown; originalFile?: unknown; type?: unknown }\n | null\n | undefined\n if (!output || !Array.isArray(output.structuredPatch)) return undefined\n const hunks = output.structuredPatch.filter(isHunk)\n if (hunks.length === 0) return undefined\n const { hunks: kept, truncated } = capHunks(hunks)\n return {\n ...(typeof output.filePath === 'string' && { path: output.filePath }),\n // Write reports `type: 'create' | 'update'` directly. Edit has no such\n // field, but `originalFile` answers the same question: null means there was\n // no file to edit. Absent entirely (neither field) leaves `kind` unset\n // rather than assuming an update.\n ...(output.type === 'create' || output.originalFile === null\n ? ({ kind: 'create' } as const)\n : output.type === 'update' || typeof output.originalFile === 'string'\n ? ({ kind: 'update' } as const)\n : {}),\n hunks: kept,\n ...(truncated && { truncated }),\n }\n}\n\n/** `@@ -oldStart,oldLines +newStart,newLines @@` — the counts are optional and\n * mean 1 when absent, which is what a single-line hunk looks like. */\nconst HUNK_HEADER = /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@/\n\n/**\n * A {@link FilePatch} from a unified diff — codex's `fileChange.diff`.\n *\n * Only the hunks are read. A diff's `---`/`+++` header names the file, but codex\n * already reports the path on the change itself, and a header path is often\n * relative or `/dev/null`, so the caller's path is the one worth trusting.\n *\n * Returns undefined when there is no hunk header at all: that is not a unified\n * diff, and inventing hunk numbers for it would put wrong line numbers on screen\n * — worse than none.\n */\nexport function parseUnifiedDiff(diff: string, path?: string): FilePatch | undefined {\n const hunks: PatchHunk[] = []\n let current: PatchHunk | undefined\n for (const line of diff.split('\\n')) {\n const header = HUNK_HEADER.exec(line)\n if (header) {\n current = {\n oldStart: Number(header[1]),\n oldLines: header[2] === undefined ? 1 : Number(header[2]),\n newStart: Number(header[3]),\n newLines: header[4] === undefined ? 1 : Number(header[4]),\n lines: [],\n }\n hunks.push(current)\n continue\n }\n if (!current) continue\n // Inside a hunk, a line belongs to it when it carries a diff prefix. A\n // '\\' line (\"\\") is a note about the previous\n // line, not a line of the file, and is dropped.\n if (line.startsWith(' ') || line.startsWith('-') || line.startsWith('+')) {\n current.lines.push(line)\n } else if (line === '') {\n // An empty line in a diff body is a context line whose trailing space was\n // stripped somewhere between the engine and here — common enough that\n // dropping it would silently shift every line number after it.\n current.lines.push(' ')\n } else {\n current = undefined\n }\n }\n if (hunks.length === 0) return undefined\n const { hunks: kept, truncated } = capHunks(hunks)\n return { ...(path && { path }), hunks: kept, ...(truncated && { truncated }) }\n}\n","import type { McpServerStatus, SDKMessage } from '@anthropic-ai/claude-agent-sdk'\nimport type {\n ApiMessage,\n ContentBlock,\n McpServerStatusInfo,\n ModelOption,\n SessionEventBody,\n TextBlock,\n} from '@workerdeck/protocol'\nimport { filePatchFromToolResult } from './patch.ts'\n\n/** Does this message answer exactly one tool call? A patch is per-file-edit and\n * the message says nothing about which of two results it describes, so anything\n * else gets no patch rather than a diff pinned to the wrong call. */\nfunction singleToolResult(message: ApiMessage): boolean {\n const content = message.content\n if (!Array.isArray(content)) return false\n return content.filter((block) => block.type === 'tool_result').length === 1\n}\n\n/**\n * The wrappers the CLI writes into the transcript when the *harness* is talking\n * to the model rather than a person talking to the session.\n *\n * Deliberately a text test, and only these two. The live path has structure to\n * go on (`isSynthetic`, `origin.kind`), but **the resumed path has none**: the\n * SDK's `SessionMessage` carries exactly `message`, `uuid`, `session_id`,\n * `parent_tool_use_id`, `parent_agent_id` and `timestamp` — every one of\n * `isMeta`, `isSidechain`, `promptSource` and `origin` is dropped between the\n * stored JSONL and what `getSessionMessages` hands back (verified against real\n * transcripts). So on resume this is the only signal there is, and without it a\n * `<task-notification>` blob comes back as a blue user row and a scrubber mark,\n * as if someone had typed it.\n *\n * `<local-command-caveat>` is here for symmetry and cheap insurance: the SDK\n * filters `isMeta` entries out of a resumed transcript itself today, which is\n * not a contract anyone wrote down.\n *\n * What is *not* here matters as much:\n * - `<local-command-stdout>` — the reducer turns it into a notice row on\n * purpose; marking it synthetic would delete a row both paths show.\n * - `<command-name>` — that is a person running a slash command. The reducer\n * renders it as the command line they typed; hiding it would erase the turn's\n * cause.\n */\nconst SYNTHETIC_USER_PREFIXES = ['<task-notification>', '<local-command-caveat>']\n\n/** First text block's leading tag, for the test above. Tool results and images\n * carry no text and are never synthetic by this rule (a tool result is already\n * a tool result to every renderer). */\nexport function isSyntheticUserText(message: ApiMessage): boolean {\n const content = message.content\n const text =\n typeof content === 'string'\n ? content\n : Array.isArray(content)\n ? content.find((block): block is TextBlock => block.type === 'text')?.text\n : undefined\n if (typeof text !== 'string') return false\n const head = text.trimStart()\n return SYNTHETIC_USER_PREFIXES.some((prefix) => head.startsWith(prefix))\n}\n\nexport function toApiMessage(message: unknown): ApiMessage {\n const m = message as {\n role?: 'user' | 'assistant'\n content: string | ContentBlock[]\n model?: string\n stop_reason?: string | null\n usage?: ApiMessage['usage']\n }\n return {\n role: m.role ?? 'assistant',\n content: m.content,\n model: m.model,\n stop_reason: m.stop_reason,\n usage: m.usage,\n }\n}\n\n/** The half of the CLI's `/usage` response this package reads. Structurally typed\n * rather than imported: the SDK marks the control request experimental and its\n * method name says so out loud, so the runner probes for it at runtime and this\n * describes only the fields it needs. */\nexport type UsageRateLimits = {\n /** 'pro' | 'max' | 'team' | 'enterprise', or null for API-key / 3P sessions. */\n subscription_type?: string | null\n rate_limits_available?: boolean\n rate_limits?: {\n five_hour?: UsageWindow\n seven_day?: UsageWindow\n seven_day_opus?: UsageWindow\n seven_day_sonnet?: UsageWindow\n seven_day_oauth_apps?: UsageWindow\n model_scoped?: Array<{ display_name: string; utilization: number | null }>\n } | null\n}\n\ntype UsageWindow = { utilization: number | null; resets_at?: string | null } | null | undefined\n\n/**\n * Plan rate-limit windows from the CLI's structured `/usage` data, as `rate_limit`\n * events — the same shape a live `rate_limit_event` produces.\n *\n * Without this a client shows no usage at all until a window *changes*, which the\n * CLI only reports after a turn moves the needle, and never for a session that is\n * only being watched. Polling the snapshot and forwarding it through the existing\n * event means replay, the dashboard and the iOS app all get it for free, with no\n * new protocol surface.\n *\n * `status` is not per-window in the usage payload — 'allowed' is what a session\n * the CLI is running for us is, by construction. A window with no utilization is\n * unknown, not zero, and is dropped rather than reported at 0%.\n */\nexport function rateLimitEventsFromUsage(usage: UsageRateLimits): SessionEventBody[] {\n if (!usage.rate_limits_available || !usage.rate_limits) return []\n const limits = usage.rate_limits\n const events: SessionEventBody[] = []\n const seen = new Set<string>()\n const push = (rateLimitType: string, window: UsageWindow): void => {\n if (!window || window.utilization === null || seen.has(rateLimitType)) return\n seen.add(rateLimitType)\n const resetsAt = window.resets_at ? Date.parse(window.resets_at) : NaN\n events.push({\n type: 'rate_limit',\n info: {\n status: 'allowed',\n rateLimitType,\n utilization: window.utilization,\n ...(Number.isFinite(resetsAt) ? { resetsAt: resetsAt / 1000 } : {}),\n },\n })\n }\n push('five_hour', limits.five_hour)\n push('seven_day', limits.seven_day)\n push('seven_day_opus', limits.seven_day_opus)\n push('seven_day_sonnet', limits.seven_day_sonnet)\n push('seven_day_oauth_apps', limits.seven_day_oauth_apps)\n // Server-driven per-model buckets, keyed off their display name so a client that\n // groups on the `seven_day_` prefix keeps them with the other weekly windows.\n for (const bucket of limits.model_scoped ?? []) {\n const slug = bucket.display_name.trim().toLowerCase().replace(/[^a-z0-9]+/g, '_')\n if (slug) push(`seven_day_${slug}`, bucket)\n }\n return events\n}\n\n/**\n * The CLI's MCP status, as `McpServerStatusInfo`.\n *\n * The narrowing is the point: the SDK's config object carries `env` for stdio\n * servers and `headers` for HTTP ones, and both routinely hold API tokens. This\n * is the one place they are dropped, so no client — dashboard, phone, or a host\n * app reading the REST route — can turn \"show me my MCP servers\" into a\n * credential dump. Only the connection's identity survives.\n */\nexport function mcpStatusInfo(status: McpServerStatus): McpServerStatusInfo {\n const config = status.config as\n | { type?: string; command?: string; args?: string[]; url?: string }\n | undefined\n // stdio is the CLI's implicit default: a config with a command and no type.\n const transport = config?.type ?? (config?.command ? 'stdio' : undefined)\n return {\n name: status.name,\n status: status.status,\n scope: status.scope,\n error: status.error,\n serverInfo: status.serverInfo,\n transport:\n transport === 'stdio' || transport === 'http' || transport === 'sse' || transport === 'sdk'\n ? transport\n : undefined,\n command: config?.command,\n args: config?.args,\n url: config?.url,\n tools: status.tools?.map((tool) => ({\n name: tool.name,\n description: tool.description,\n annotations: tool.annotations,\n })),\n }\n}\n\n/** The half of the SDK's `ModelInfo` this package forwards. Structurally typed so\n * the mapping can be unit-tested without a live query. */\nexport type SdkModelInfo = {\n value: string\n resolvedModel?: string\n displayName: string\n description?: string\n /** Per-model reasoning efforts, when the SDK reports them (0.3.221+). */\n supportedEffortLevels?: string[]\n supportsEffort?: boolean\n}\n\n/**\n * The CLI's model list, as `ModelOption[]`.\n *\n * Two decisions live here rather than in each client:\n *\n * - **`default` is dropped.** The CLI offers a row whose id is literally\n * `default` (\"Default (recommended)\"), meaning \"whatever I would have picked\".\n * It is a legal id to send, but it is not a model: a session running on it\n * reports a real model, so a picker showing it has a row that can never be\n * checked, and a status bar naming it would say \"Default\" for a session\n * answering as Opus. Which model the default resolved to is a different\n * question, and `system_init` answers it.\n * - **`primary` is derived.** The CLI reports one flat list; Claude Code's own\n * picker shows the newest of each family and files the rest under \"more\n * models\". The list arrives newest-first, so the first row of each family is\n * the primary one. A heuristic, but a stable one — and doing it once here\n * means the dashboard and the phone group identically.\n */\n/** What the CLI's `default` row resolves to — the model a session will answer as\n * before it has answered anything. Dropped from the list, kept as this. */\nexport function defaultModelFromSdk(models: readonly SdkModelInfo[]): string | undefined {\n return models.find((model) => model.value === 'default')?.resolvedModel\n}\n\nexport function modelOptionsFromSdk(models: readonly SdkModelInfo[]): ModelOption[] {\n const rows = models.filter((model) => model.value !== 'default')\n // A derived name is only used when it is unambiguous. Two rows of one model\n // (a 1M-context variant beside a plain one) would derive the same string, and\n // there the CLI's own names are the ones that tell them apart.\n const derivedCounts = new Map<string, number>()\n for (const model of rows) {\n const derived = friendlyModelName(model.resolvedModel ?? model.value)\n if (derived) derivedCounts.set(derived, (derivedCounts.get(derived) ?? 0) + 1)\n }\n\n const seenFamilies = new Set<string>()\n const options: ModelOption[] = rows.map((model) => {\n const family = modelFamily(model.resolvedModel ?? model.value)\n const primary = !seenFamilies.has(family)\n seenFamilies.add(family)\n const derived = friendlyModelName(model.resolvedModel ?? model.value)\n return {\n value: model.value,\n // Carried through so a client can match the model a session *reports*\n // ('claude-opus-5[1m]') against the row that names it ('opus[1m]').\n resolvedModel: model.resolvedModel,\n displayName: derived && derivedCounts.get(derived) === 1 ? derived : model.displayName,\n description: model.description,\n primary,\n // Explicit [] when the CLI reports no effort support, so clients don't\n // fall back to the engine-wide default set for an effortless model.\n reasoningEfforts: model.supportedEffortLevels ?? (model.supportsEffort === false ? [] : undefined),\n }\n })\n\n // Capability order, which is what a person picking a model is choosing along\n // and what the CLI's own selector shows. The CLI reports its list in a\n // different order and gives no ranking field, so it is declared here — a\n // family this list has never heard of sorts after the known ones rather than\n // to the top, and ties keep the CLI's order.\n return options\n .map((option, index) => ({ option, index }))\n .sort((a, b) => {\n const rankA = familyRank(a.option)\n const rankB = familyRank(b.option)\n return rankA === rankB ? a.index - b.index : rankA - rankB\n })\n .map(({ option }) => option)\n}\n\nconst FAMILY_ORDER = ['fable', 'opus', 'sonnet', 'haiku']\n\nfunction familyRank(option: ModelOption): number {\n const rank = FAMILY_ORDER.indexOf(modelFamily(option.resolvedModel ?? option.value))\n return rank === -1 ? FAMILY_ORDER.length : rank\n}\n\n/**\n * The name a person says, from a wire model id: 'claude-opus-5[1m]' → \"Opus 5\",\n * 'claude-haiku-4-5-20251001' → \"Haiku 4.5\".\n *\n * The CLI's own `displayName` is the family alone (\"Opus\", \"Haiku\") or carries a\n * variant instead of a version (\"Opus (1M context)\"), and the version is the part\n * that answers \"is this the current one\". It is only ever in the id, so it is\n * read from there. Returns null when the id has no version to read — a bare\n * alias like 'sonnet' — and the CLI's name stands.\n */\nexport function friendlyModelName(id: string): string | null {\n const withoutVariant = id.split('[')[0] ?? id\n const parts = withoutVariant.toLowerCase().split('-').filter(Boolean)\n if (parts[0] === 'claude') parts.shift()\n const family = parts.shift()\n if (!family) return null\n // Trailing snapshot date ('20251001') is a build, not a version.\n const version = parts.filter((part) => !/^\\d{8}$/.test(part))\n if (version.length === 0 || version.some((part) => !/^\\d+$/.test(part))) return null\n return `${family.charAt(0).toUpperCase()}${family.slice(1)} ${version.join('.')}`\n}\n\n/** 'claude-opus-4-8[1m]' → \"opus\". The vendor prefix, the context-window suffix\n * and the version tail are all dropped; what is left is the family a person\n * names. Unrecognisable ids become their own family, so a model this rule has\n * never seen lands in the main list rather than being hidden. */\nfunction modelFamily(id: string): string {\n const withoutVariant = id.split('[')[0] ?? id\n const parts = withoutVariant.toLowerCase().split('-')\n if (parts[0] === 'claude') parts.shift()\n return parts[0] ?? withoutVariant\n}\n\n/**\n * Map one SDKMessage to a wire-protocol event body, or null for messages the runner\n * consumes itself (system_init and session-state changes carry runner state and are\n * emitted by the runner with extra context).\n */\nexport function normalizeSdkMessage(msg: SDKMessage): SessionEventBody | null {\n switch (msg.type) {\n case 'assistant':\n return {\n type: 'assistant_message',\n message: toApiMessage(msg.message),\n parentToolUseId: msg.parent_tool_use_id,\n uuid: msg.uuid,\n }\n case 'user': {\n const message = toApiMessage(msg.message)\n return {\n type: 'user_message',\n message,\n parentToolUseId: msg.parent_tool_use_id,\n replay: 'isReplay' in msg && msg.isReplay === true ? true : undefined,\n // Three ways to be the harness rather than a person: the SDK says so,\n // the message's origin says so (a background task reporting in is not\n // someone typing), or the text is one of the CLI's own wrappers — which\n // is the only one of the three a *resumed* transcript still carries.\n synthetic:\n msg.isSynthetic === true ||\n msg.origin?.kind === 'task-notification' ||\n isSyntheticUserText(message)\n ? true\n : undefined,\n // The engine's own line numbers, projected down to the hunks — see\n // `filePatchFromToolResult` for why the rest of `tool_use_result` stays\n // off the wire. Only with a single tool_result block, because nothing\n // in the message says which call a patch belongs to.\n patch: singleToolResult(message) ? filePatchFromToolResult(msg.tool_use_result) : undefined,\n uuid: msg.uuid,\n }\n }\n case 'stream_event':\n return {\n type: 'stream_delta',\n event: msg.event as { type: string; [key: string]: unknown },\n parentToolUseId: msg.parent_tool_use_id,\n uuid: msg.uuid,\n }\n case 'result':\n return {\n type: 'turn_result',\n subtype: msg.subtype,\n isError: msg.is_error,\n durationMs: msg.duration_ms,\n numTurns: msg.num_turns,\n totalCostUsd: msg.total_cost_usd,\n result: msg.subtype === 'success' ? msg.result : undefined,\n errors: msg.subtype === 'success' ? undefined : msg.errors,\n usage: msg.usage,\n }\n case 'conversation_reset':\n // /clear, plan-mode exit, fresh-conversation flows: same session, fresh\n // conversation. The runner reacts to this body too (reset watermark,\n // sdkSessionId adoption) — see SessionRunner.#handleMessage.\n return { type: 'conversation_reset', sdkSessionId: msg.new_conversation_id }\n case 'rate_limit_event':\n return {\n type: 'rate_limit',\n info: {\n status: msg.rate_limit_info.status,\n rateLimitType: msg.rate_limit_info.rateLimitType,\n utilization: msg.rate_limit_info.utilization,\n resetsAt: msg.rate_limit_info.resetsAt,\n isUsingOverage: msg.rate_limit_info.isUsingOverage,\n },\n }\n case 'system':\n // init and session_state_changed are handled by the runner directly.\n if (msg.subtype === 'init' || msg.subtype === 'session_state_changed') return null\n return { type: 'sdk_event', payload: msg as unknown as { type: string } }\n default:\n return { type: 'sdk_event', payload: msg as unknown as { type: string } }\n }\n}\n","import {\n imagePartRef,\n replayCoalesceKey,\n replayRetains,\n TOOL_RESULT_HEAD_CHARS,\n transcriptContent,\n type SessionEvent,\n type ToolResultBlock,\n} from '@workerdeck/protocol'\n\n/**\n * Which buffered events a coalesced replay should skip: everything superseded\n * by a later event with the same {@link replayCoalesceKey}.\n *\n * A **backwards** scan, keeping the first occurrence of each key — which is the\n * whole trick. Walking forwards would need a second pass to know which of the\n * fifty context readings was the last one; walking backwards, the first one you\n * meet *is* the last one, and everything after it (in scan order) is history.\n *\n * Note what this does **not** do: it never reorders and never touches an event\n * with no key. Transcript content is an ordered fold — a stream delta\n * accumulates onto a message, a tool result attaches to a call that came\n * earlier, a turn result finalizes — so it must arrive exactly as it was\n * emitted. Only last-write-wins *state* is eligible, and `replayCoalesceKey`\n * is where that judgement lives.\n *\n * `afterSeq` is honoured so the scan agrees with the caller's replay window: an\n * event the caller was never going to send must not suppress one it was.\n */\nexport function staleReplaySeqs(events: readonly SessionEvent[], afterSeq: number): Set<number> {\n const stale = new Set<number>()\n const seen = new Set<string>()\n for (let index = events.length - 1; index >= 0; index--) {\n const event = events[index]!\n if (event.seq <= afterSeq) break\n const key = replayCoalesceKey(event)\n if (key === undefined) continue\n if (seen.has(key)) stale.add(event.seq)\n else seen.add(key)\n }\n return stale\n}\n\n/**\n * The one replay body, and what a socket receives from it.\n *\n * Every runner had a byte-identical copy of this loop — three spellings of four\n * rules, one of which (\"never drop the highest-seq event, whatever the rule\n * says\") is load-bearing and was three copies of a comment. Not a base class:\n * the runners share nothing else, and a base class would have to own `#emit`,\n * the most engine-specific method each of them has.\n *\n * The rules, in the order they are applied:\n *\n * 1. `afterSeq` — the caller already holds everything at or below it.\n * 2. `resetSeq` — transcript *content* strictly below the latest\n * `conversation_reset` is skipped, so a re-attach cannot resurrect a cleared\n * conversation while state events still replay. Claude's alone; the other\n * engines pass 0.\n * 3. `coalesceReplay` — last-write-wins state readings superseded later in the\n * same replay (`staleReplaySeqs`), plus everything `replayRetains` says the\n * reducer reads and discards. Opt-in, and only sound for a consumer whose\n * handling of those events is last-write-wins.\n * 4. `imageRefs` — a base64 image part is delivered as an `image_ref` address\n * (protocol's {@link imagePartRef}), its bytes one REST fetch away. Applied\n * **before** rule 5, because it stamps indices from the stored part array\n * which rule 5 then reshapes. Unlike rule 5 this also applies to the live\n * path (see `SubscriberSet`), which is the one place these two rules differ.\n * 5. `truncateResults` — a huge `tool_result` block is delivered as its head\n * plus the markers that say so. **Never mutates the stored event**: the live\n * path, the parking snapshot and the fetch route all need the whole thing,\n * so this builds a copy and the log stays the log.\n *\n * The highest-seq event is delivered whatever rules 2 and 3 say — a client's\n * replay hold waits for `state.lastSeq` to reach the attach's and would\n * otherwise hang forever — but it is still *truncated* when rule 4 applies. A\n * session that ends on a `find /` puts its 641 KB frame exactly there.\n */\nexport function replaySlice(\n events: readonly SessionEvent[],\n options: {\n afterSeq: number\n resetSeq?: number\n coalesceReplay?: boolean\n truncateResults?: boolean\n imageRefs?: boolean\n },\n): SessionEvent[] {\n const { afterSeq, resetSeq = 0, coalesceReplay, truncateResults, imageRefs } = options\n const stale = coalesceReplay ? staleReplaySeqs(events, afterSeq) : undefined\n const lastSeq = events[events.length - 1]?.seq ?? 0\n const out: SessionEvent[] = []\n for (const event of events) {\n if (event.seq <= afterSeq) continue\n if (event.seq < resetSeq && transcriptContent(event)) continue\n if (stale?.has(event.seq)) continue\n if (coalesceReplay && event.seq !== lastSeq && !replayRetains(event)) continue\n // Refs before heads, always: `refImageParts` stamps addresses from the\n // stored part array and `truncateResultBlocks` reshapes it.\n let delivered = event\n if (imageRefs) delivered = refImageParts(delivered)\n if (truncateResults) delivered = truncateResultBlocks(delivered)\n out.push(delivered)\n }\n return out\n}\n\n/**\n * A copy of `event` whose oversized `tool_result` blocks carry their head and\n * say so — or `event` itself, unchanged and un-copied, when nothing is over the\n * budget. That identity matters: an attach is mostly small events, and a fresh\n * object for every one of them would cost more than the feature saves.\n *\n * Blocks are measured and cut **individually**. A message answering three calls\n * where one is a `find /` keeps the two small results whole, which is what makes\n * the per-block marker (rather than a per-event one) honest.\n */\nexport function truncateResultBlocks(event: SessionEvent): SessionEvent {\n if (event.type !== 'user_message') return event\n const content = event.message.content\n if (!Array.isArray(content)) return event\n let cut = false\n const blocks = content.map((block) => {\n if (block.type !== 'tool_result') return block\n const result = block as ToolResultBlock\n if (result.truncated) return block\n const total = resultChars(result.content)\n if (total <= TOOL_RESULT_HEAD_CHARS) return block\n cut = true\n return {\n ...result,\n content: headOf(result.content, TOOL_RESULT_HEAD_CHARS),\n truncated: true,\n total_chars: total,\n } satisfies ToolResultBlock\n })\n if (!cut) return event\n return { ...event, message: { ...event.message, content: blocks } }\n}\n\n/** Characters in a result's content, in the same terms a reader sees it: the\n * string itself, or every text part of a block list joined by newlines — which\n * is exactly what `blockText` in the reducer builds. Non-text parts (an image\n * block) contribute nothing, because they are not what is large here and\n * slicing them would corrupt them. */\nfunction resultChars(content: ToolResultBlock['content']): number {\n if (typeof content === 'string') return content.length\n if (!Array.isArray(content)) return 0\n return content.reduce(\n (total, part, index) =>\n total + (typeof part.text === 'string' ? part.text.length + (index > 0 ? 1 : 0) : 0),\n 0,\n )\n}\n\n/** The first `chars` characters, in the content's own shape — a string stays a\n * string, a block list stays a block list (cut at the part that crosses the\n * budget, with the remaining parts dropped). Shape-preserving on purpose: the\n * reducer, both renderers and the copy button all read this the same way they\n * read a whole one, so truncation is a shorter result and never a different\n * kind of one. */\nfunction headOf(content: ToolResultBlock['content'], chars: number): ToolResultBlock['content'] {\n if (typeof content === 'string') return content.slice(0, chars)\n if (!Array.isArray(content)) return content\n const parts: Array<{ type: string; text?: string; [key: string]: unknown }> = []\n let used = 0\n for (const part of content) {\n // An address this gateway itself just minted (`refImageParts` runs first).\n // Keeping it is what lets the two rules compose: dropped here, a socket\n // asking for both heads and refs would lose every picture with no marker.\n // Raw `image` parts keep being dropped exactly as Part 4 shipped them,\n // which is what keeps a truncate-only socket byte-identical.\n if (part.type === 'image_ref') {\n parts.push(part)\n continue\n }\n if (typeof part.text !== 'string') continue\n // `continue`, not `break`: an exhausted text budget must not strand the\n // refs that come after it. Identical output for text either way.\n if (used >= chars) continue\n const text = part.text.slice(0, chars - used)\n parts.push({ ...part, text })\n used += text.length + 1\n }\n return parts\n}\n\n/**\n * A copy of `event` whose `tool_result` blocks carry `image_ref` addresses in\n * place of their base64 image parts — or `event` itself, unchanged and\n * un-copied, when it holds none. Same identity rule as\n * {@link truncateResultBlocks}, and it matters more here: an event carrying an\n * image at all is the exception, so the common path must not allocate.\n *\n * **Never mutates the stored event.** The log is what the parking snapshot\n * embeds, what `Runner.eventAt` reads, and therefore what the fetch route\n * serves the bytes back from — a drop that reached the log would 404 the very\n * lazy-load this rule promises.\n *\n * Indices are stamped from the **stored** array, which is why this runs *before*\n * truncation rather than after: `headOf` reshapes a block's parts, so an address\n * computed on its output would name the wrong part of the stored block. That\n * ordering is asserted in `replay-image-ref.test.ts`, not merely intended.\n */\nexport function refImageParts(event: SessionEvent): SessionEvent {\n if (event.type !== 'user_message') return event\n const content = event.message.content\n if (!Array.isArray(content)) return event\n let changed = false\n const blocks = content.map((block) => {\n if (block.type !== 'tool_result') return block\n const result = block as ToolResultBlock\n const parts = result.content\n if (!Array.isArray(parts)) return block\n let blockChanged = false\n const mapped = parts.map((part, index) => {\n const ref = imagePartRef(part, index)\n if (!ref) return part\n blockChanged = true\n return ref\n })\n if (!blockChanged) return block\n changed = true\n return { ...result, content: mapped } satisfies ToolResultBlock\n })\n if (!changed) return event\n return { ...event, message: { ...event.message, content: blocks } }\n}\n","/**\n * The other half of `replaySlice`, and the same argument.\n *\n * Three runners had a byte-identical `subscribe` body — replay the buffer, add\n * to a `Set`, return a deleter — and a byte-identical fan-out loop beside it,\n * try/catch comment included. `replaySlice` retired the first half of that\n * duplication when the replay grew rules worth stating once. This retires the\n * second, and it is not merely tidiness: the moment a rule applies to **live**\n * events as well as replayed ones, a bare `Set<listener>` has nowhere to keep\n * the options that rule is conditioned on, and each runner would grow its own\n * copy of the answer.\n *\n * So a subscriber is a listener *plus what it asked for*, and delivery is one\n * method. Not a base class, for `replaySlice`'s reason: the runners share\n * nothing else, and a base class would have to own `#emit`, the most\n * engine-specific method each of them has.\n *\n * **Which rules reach the live path is the whole judgement here**, and there is\n * exactly one:\n *\n * - `coalesceReplay` — replay-only by construction. It drops readings superseded\n * *later in the same replay*; live, there is no later.\n * - `truncateResults` — replay-only by decision. `TOOL_RESULT_HEAD_CHARS` is set\n * above both clients' own display budgets, so a result arriving while you\n * watch is already fully on screen and truncating it would buy a fetch for\n * nothing.\n * - `imageRefs` — **both**. The client's one render path is ref-then-fetch, so\n * bytes on a live event would either be discarded (335 KB median, once per\n * attached watcher) or need a second decode-from-event path — which pins\n * megabytes of base64 inside `TranscriptState`, which the transcript LRU then\n * retains across session switches. That is the disease relocated, not cured.\n *\n * Consumers that subscribe with no options — parking, notifications, the queue —\n * see every byte, as they do for every other rule in this family.\n */\nimport type { SessionEvent } from '@workerdeck/protocol'\nimport type { SessionEventListener } from '../runner-interface.ts'\nimport { refImageParts, replaySlice } from './replay.ts'\n\n/** What a subscriber asked for. Absent fields mean the untransformed stream. */\nexport type SubscribeOptions = {\n coalesceReplay?: boolean\n truncateResults?: boolean\n imageRefs?: boolean\n}\n\nexport class SubscriberSet {\n readonly #listeners = new Map<SessionEventListener, SubscribeOptions>()\n\n /**\n * Replay `events` to `listener` under `options`, then hold it for live\n * delivery. Returns the unsubscribe.\n *\n * The replay runs *before* the listener joins the set, which is the ordering\n * every runner already had and is load-bearing: joining first would deliver a\n * live event emitted mid-replay ahead of the buffered events preceding it.\n */\n subscribe(\n events: readonly SessionEvent[],\n listener: SessionEventListener,\n afterSeq = 0,\n options?: SubscribeOptions,\n resetSeq = 0,\n ): () => void {\n const asked = options ?? {}\n for (const event of replaySlice(events, { ...asked, afterSeq, resetSeq })) listener(event)\n this.#listeners.set(listener, asked)\n return () => {\n this.#listeners.delete(listener)\n }\n }\n\n /** Drop every subscriber — a park, which ends the session's live stream. */\n clear(): void {\n this.#listeners.clear()\n }\n\n /** Fan one event out, transformed per subscriber. */\n emit(event: SessionEvent): void {\n for (const [listener, asked] of this.#listeners) {\n try {\n listener(asked.imageRefs ? refImageParts(event) : event)\n } catch {\n // Listener errors must not break the runner loop.\n }\n }\n }\n}\n","import {\n SUBAGENT_HISTORY,\n type ContentBlock,\n type SessionEventBody,\n type SubagentInfo,\n} from '@workerdeck/protocol'\n\n/**\n * The rollup behind `SessionInfo.subagents` — what a sessions list (which never\n * attaches) can know about the sub-agents running inside a session. Fed from\n * `SessionRunner.#emit`, the one chokepoint every event passes through, so the\n * resume backfill — which replays history through the same path — reconstructs\n * it with no persistence of its own. Grouping is by Task id throughout, never\n * adjacency: parallel sub-agents interleave in the stream, the same fact that\n * broke the terminal theme's positional row model.\n *\n * Three decisions live here rather than in the protocol doc:\n *\n * **What counts as a spawn.** A record opens when a *top-level* assistant\n * message carries a `tool_use` named `Task` or `Agent` — the moment the\n * sub-agent exists, so a just-spawned agent is visible before its first nested\n * event, with the block's input in hand for its labels. Both names are observed\n * SDK spellings (`Task` synchronous, `Agent` async), and the name is a\n * convention, not a law — a session that spawned three `Agent`s under a tracker\n * that only knew `Task` reported all three as label-less failures. So three\n * more openers back the allowlist up: the CLI's own `task_started` system event\n * (which positively names the `tool_use_id` an agent runs under, with the brief\n * as labels), the launch acknowledgement (below), and — as before — any nested\n * event whose `parentToolUseId` has no record: an id that events demonstrably\n * nest under *is* a sub-agent, whatever the spawning call was named. A fallback\n * record never saw an input, so it stays label-less until a named signal fills\n * it in rather than resetting an accumulated count.\n *\n * **A background agent's `tool_result` is a launch receipt, not a verdict.**\n * An async agent's spawn call resolves seconds after the spawn with \"Async\n * agent launched successfully. (This tool result is internal metadata …)\" —\n * long before the agent has done anything — and its actual outcome travels on\n * a `task_notification` system event instead (`status: 'completed'` is `done`,\n * any other way of stopping is `failed`: the report the notification exists to\n * deliver never came). Settling on the receipt would read \"0 of 3 agents\n * running\" while three agents burn tokens, so a non-error result on a record\n * known to be background never settles it. Known how: the `task_started` event\n * live, or the receipt's own wrapper text on a resume — the stored transcript\n * carries none of the CLI's system events, so, exactly as\n * `isSyntheticUserText` documents for the `<task-notification>` blob, the text\n * is the only signal the replayed path has.\n *\n * **What an interrupted turn leaves behind.** A Task whose `tool_result` never\n * arrives — interrupt, session error, a turn or budget cap — would otherwise\n * read `running` on an idle session forever, a lie a list re-renders at every\n * poll. So the end of a turn settles every still-running record as `failed`:\n * the report never came, which is the one thing `done` could have claimed. The\n * sweep keys on `turn_result`, on the status coming to rest (`idle` — which is\n * how a resumed history that ends mid-Task settles, since the backfill replays\n * no `turn_result` — or a terminal state), and on the session closing. A real\n * verdict arriving anyway outranks the sweep's inference. The sweep's premise\n * — \"the turn ended, so anything still running was cut off\" — is false for a\n * background agent, which is *designed* to outlive its turn: the real session\n * behind this file ended three turns while its agents ran, and every\n * `turn_result` re-branded live, working agents as failures. So the turn and\n * idle sweeps spare a record marked background by a **live** signal. They do\n * not spare one whose only evidence is replayed: the backfill describes a\n * process that is gone, and a background agent the old process died inside can\n * never notify — `running` would be the forever-lie again. `session_closed`\n * and the terminal statuses settle everything, background included, for the\n * same reason: the process hosting those agents is gone.\n */\nexport class SubagentTracker {\n #records = new Map<string, TrackedSubagent>()\n #settleCounter = 0\n\n /** Fold one emitted event body into the rollup, in log order. */\n observe(body: SessionEventBody, ts: number): void {\n switch (body.type) {\n case 'assistant_message': {\n if (body.parentToolUseId != null) {\n const record = this.#recordFor(body.parentToolUseId, ts)\n // Progress is tool calls, not prose: a sub-agent's text and thinking\n // are its working, and counting them would make two agents' readings\n // incomparable. A nested `Task` block (a grandchild spawn, should an\n // engine ever nest) is still one tool call of *this* sub-agent.\n record.toolCount += toolUseBlocks(body.message.content).length\n return\n }\n for (const block of toolUseBlocks(body.message.content)) {\n if (!SPAWNER_NAMES.has(block.name)) continue\n this.#open(block, ts)\n }\n return\n }\n case 'user_message': {\n if (body.parentToolUseId != null) {\n // A sidechain's first event is usually its brief; touching the record\n // here is what makes the fallback catch a renamed spawner promptly.\n this.#recordFor(body.parentToolUseId, ts)\n return\n }\n // A background agent stopping, as the resume backfill spells it: the\n // `<task-notification>` wrapper the CLI writes into the transcript\n // (live, the same fact arrives as a `task_notification` system event\n // and no user message at all). Parsed before the plain-string return\n // below — the stored form is a bare string.\n const note = parseTaskNotification(firstText(body.message.content))\n if (note) {\n const record = this.#recordFor(note.toolUseId, ts)\n const status = note.status === 'completed' ? 'done' : 'failed'\n if (record.status !== status) this.#settle(record, status)\n return\n }\n const content = body.message.content\n if (typeof content === 'string') return\n for (const block of content) {\n if (block.type !== 'tool_result') continue\n const result = block as {\n tool_use_id?: unknown\n is_error?: unknown\n content?: unknown\n }\n if (typeof result.tool_use_id !== 'string') continue\n if (result.is_error !== true && isLaunchAck(result.content)) {\n // The receipt marks the record background rather than settling it —\n // and *how* it was marked matters to the sweep: `live` outlives the\n // turn, `replay` describes a process that is gone. Live evidence is\n // never downgraded by a re-streamed duplicate on resume.\n const record = this.#recordFor(result.tool_use_id, ts)\n if (record.background !== 'live') {\n record.background = body.replay === true ? 'replay' : 'live'\n }\n continue\n }\n const record = this.#records.get(result.tool_use_id)\n if (!record) continue\n // Belt beside the wording sniff above: whatever the receipt says, a\n // non-error result on a background record carries no verdict — the\n // verdict travels on the notification.\n if (result.is_error !== true && record.background !== undefined) continue\n const status = result.is_error === true ? 'failed' : 'done'\n // Equal-verdict results are skipped rather than re-stamped: the SDK\n // re-streams user messages on resume, and a duplicate that re-stamped\n // settle order would shuffle the retention bound. An *unequal* one\n // re-settles — the engine's own verdict outranks the sweep's.\n if (record.status === status) continue\n this.#settle(record, status)\n }\n return\n }\n case 'sdk_event': {\n // The CLI's background-task lifecycle, live only — none of it lands in\n // the stored transcript a resume replays. `task_updated` is skipped on\n // purpose: it is keyed by task id alone, and the `task_notification`\n // that follows it carries the `tool_use_id` this rollup is keyed by.\n // `task_progress`'s `description` is skipped too — it is the agent's\n // *current activity* (\"Running grep …\"), not its brief, and a label\n // slot that changed per poll would be a status field wearing a label's\n // name.\n const p = body.payload as {\n type?: unknown\n subtype?: unknown\n tool_use_id?: unknown\n status?: unknown\n subagent_type?: unknown\n description?: unknown\n }\n if (p.type !== 'system' || typeof p.tool_use_id !== 'string') return\n if (p.subtype === 'task_started') {\n const record = this.#recordFor(p.tool_use_id, ts)\n record.background = 'live'\n record.agentType ??= cleaned(p.subagent_type)\n record.description ??= cleaned(p.description)\n return\n }\n if (p.subtype === 'task_notification') {\n const record = this.#recordFor(p.tool_use_id, ts)\n const status = p.status === 'completed' ? 'done' : 'failed'\n if (record.status !== status) this.#settle(record, status)\n return\n }\n return\n }\n case 'turn_result':\n this.#sweep(false)\n return\n case 'session_closed':\n this.#sweep(true)\n return\n case 'status_changed':\n if (body.status === 'idle') this.#sweep(false)\n else if (body.status === 'failed' || body.status === 'closed') this.#sweep(true)\n return\n case 'conversation_reset':\n // The conversation is gone; so are the Tasks it ran. The same claim the\n // reset watermark makes for replay: a fresh attacher never sees those\n // rows, so a rollup pointing into them would dangle.\n this.#records.clear()\n return\n default:\n // stream_delta lands here on purpose: deltas count zero (superseded by\n // construction), and they must not open the fallback either — the\n // resume backfill replays no deltas, so a record only a delta opened\n // would not survive a rebuild.\n return\n }\n }\n\n /**\n * The rollup as `SessionInfo.subagents` serves it: spawn order (the\n * transcript's own), fresh objects, and `undefined` when there is nothing to\n * say — absent and empty mean the same thing to a client, and an empty array\n * on every row of a 1.2s-polled list is bytes spent saying nothing.\n */\n list(): SubagentInfo[] | undefined {\n if (this.#records.size === 0) return undefined\n const out: SubagentInfo[] = []\n for (const r of this.#records.values()) {\n out.push({\n toolUseId: r.toolUseId,\n agentType: r.agentType,\n description: r.description,\n status: r.status,\n startedAt: r.startedAt,\n toolCount: r.toolCount,\n })\n }\n return out\n }\n\n #recordFor(toolUseId: string, ts: number): TrackedSubagent {\n let record = this.#records.get(toolUseId)\n if (!record) {\n record = { toolUseId, status: 'running', startedAt: ts, toolCount: 0 }\n this.#records.set(toolUseId, record)\n }\n return record\n }\n\n #open(block: { id: string; input: unknown }, ts: number): void {\n const record = this.#recordFor(block.id, ts)\n const input = block.input as\n | { subagent_type?: unknown; description?: unknown }\n | null\n | undefined\n // Fill-in, never overwrite: this may be upgrading a label-less fallback\n // record that already accumulated a count.\n record.agentType ??= cleaned(input?.subagent_type)\n record.description ??= cleaned(input?.description)\n }\n\n /**\n * End of turn (`final: false`): anything still running was cut off before\n * its report — except a background agent the live process still hosts, which\n * is designed to outlive the turn and settles by notification instead. End\n * of session (`final: true`): everything, background included, because the\n * process those agents lived in is gone.\n */\n #sweep(final: boolean): void {\n for (const record of this.#records.values()) {\n if (record.status !== 'running') continue\n if (!final && record.background === 'live') continue\n this.#settle(record, 'failed')\n }\n }\n\n #settle(record: TrackedSubagent, status: 'done' | 'failed'): void {\n record.status = status\n record.settledOrder = ++this.#settleCounter\n // The bound is enforced here rather than in list(): a settle happens once\n // per sub-agent, list() once per row of a polled sessions list. Running\n // records are never evicted — they are the live reading and the reason the\n // field exists.\n let settled = 0\n for (const r of this.#records.values()) {\n if (r.settledOrder !== undefined) settled++\n }\n while (settled > SUBAGENT_HISTORY) {\n let oldestId: string | undefined\n let oldestOrder = Infinity\n for (const r of this.#records.values()) {\n if (r.settledOrder === undefined || r.settledOrder >= oldestOrder) continue\n oldestId = r.toolUseId\n oldestOrder = r.settledOrder\n }\n if (oldestId === undefined) break\n this.#records.delete(oldestId)\n settled--\n }\n }\n}\n\ntype TrackedSubagent = SubagentInfo & {\n /** Monotonic settle stamp; the retention bound evicts the smallest. Insertion\n * order cannot stand in for it — records open in spawn order, and a slow\n * early Task settles after a fast late one. */\n settledOrder?: number\n /** Set when this is a *background* agent — one designed to outlive the turn\n * that spawned it — and by what kind of evidence: `live` (the `task_started`\n * event, or the launch receipt arriving on the live stream) spares it from\n * the turn/idle sweep; `replay` (the receipt replayed from a resumed\n * transcript) does not, because the process that ran it is gone and it can\n * never notify. Never downgraded from `live`. Internal — protocol's\n * `SubagentInfo` deliberately says nothing about it. */\n background?: 'live' | 'replay'\n}\n\n/** The spawner names observed in the wild: `Task` runs the agent inside the\n * turn, `Agent` launches it in the background. Deliberately just these two —\n * a third spelling is caught by `task_started`, the launch receipt, or the\n * nested-event fallback, so widening this to every tool would only turn\n * ordinary calls into phantom agents. */\nconst SPAWNER_NAMES = new Set(['Task', 'Agent'])\n\n/** The async spawn's immediate `tool_result` — \"Async agent launched\n * successfully. (This tool result is internal metadata …)\" — recognized by its\n * wrapper text because on a resume that text is the only signal there is (the\n * `SYNTHETIC_USER_PREFIXES` argument; the CLI's system events are not stored).\n * Live, `task_started` marks the record first and this is redundant armor. */\nconst isLaunchAck = (content: unknown): boolean => {\n const text =\n typeof content === 'string' ? content : firstText(Array.isArray(content) ? content : [])\n return typeof text === 'string' && text.trimStart().startsWith('Async agent launched')\n}\n\n/** A background agent stopping, parsed from the `<task-notification>` wrapper\n * the CLI writes into the transcript. Field-tolerant on purpose: only the\n * `tool-use-id` (this rollup's key) and the `status` verdict are read. */\nconst parseTaskNotification = (\n text: string | undefined,\n): { toolUseId: string; status: string } | undefined => {\n if (text === undefined || !text.trimStart().startsWith('<task-notification>')) return undefined\n const toolUseId = /<tool-use-id>\\s*([^<\\s]+)\\s*<\\/tool-use-id>/.exec(text)?.[1]\n if (toolUseId === undefined) return undefined\n const status = /<status>\\s*([^<]*?)\\s*<\\/status>/.exec(text)?.[1] ?? ''\n return { toolUseId, status }\n}\n\n/** The first text of a message body, however the content is spelled — the\n * stored transcript uses bare strings, the live stream uses blocks. */\nconst firstText = (content: string | ContentBlock[] | unknown[]): string | undefined => {\n if (typeof content === 'string') return content\n for (const block of content) {\n const b = block as { type?: unknown; text?: unknown } | null | undefined\n if (b?.type === 'text' && typeof b.text === 'string') return b.text\n }\n return undefined\n}\n\n/** Trim, drop blank, clip at the same 80 the terminal theme's `taskLabel` uses.\n * Model-authored input rides every row of a polled sessions list, so it is\n * bounded here rather than trusted — a 10KB `description` would be paid for at\n * every poll. */\nconst cleaned = (value: unknown): string | undefined => {\n if (typeof value !== 'string') return undefined\n const text = value.trim()\n if (text === '') return undefined\n return text.length > 80 ? text.slice(0, 79) + '…' : text\n}\n\n/** The `tool_use` blocks of a message body, however the content is spelled. */\nfunction toolUseBlocks(\n content: string | ContentBlock[],\n): Array<{ id: string; name: string; input: unknown }> {\n if (typeof content === 'string') return []\n const blocks: Array<{ id: string; name: string; input: unknown }> = []\n for (const block of content) {\n if (block.type !== 'tool_use') continue\n const b = block as { id?: unknown; name?: unknown; input?: unknown }\n if (typeof b.id !== 'string' || typeof b.name !== 'string') continue\n blocks.push({ id: b.id, name: b.name, input: b.input })\n }\n return blocks\n}\n","import { randomUUID } from 'node:crypto'\nimport {\n getSessionInfo,\n getSessionMessages,\n query as sdkQuery,\n type CanUseTool,\n type Options,\n type PermissionResult,\n type Query,\n type SDKMessage,\n type SDKSessionInfo,\n type SDKUserMessage,\n type SessionMessage,\n} from '@anthropic-ai/claude-agent-sdk'\nimport {\n ENGINE_CAPABILITIES,\n transcriptActivity,\n type CreateSessionRequest,\n type McpServerStatusInfo,\n type PermissionMode,\n type PermissionRequest,\n type SessionEvent,\n type SessionEventBody,\n type SessionInfo,\n type SessionStatus,\n} from '@workerdeck/protocol'\nimport {\n type AttachmentInput,\n attachmentContentBlocks,\n attachmentRef,\n} from '../../lib/attachments.ts'\nimport { InputQueue } from '../../lib/input-queue.ts'\nimport {\n type UsageRateLimits,\n defaultModelFromSdk,\n isSyntheticUserText,\n mcpStatusInfo,\n modelOptionsFromSdk,\n normalizeSdkMessage,\n rateLimitEventsFromUsage,\n toApiMessage,\n} from '../../lib/normalize.ts'\nimport type { PermissionDecision, Runner, SessionEventListener } from '../../runner-interface.ts'\nimport { SubscriberSet, type SubscribeOptions } from '../../lib/subscribers.ts'\nimport { SubagentTracker } from './subagents.ts'\n\nexport type QueryFn = (params: {\n prompt: AsyncIterable<SDKUserMessage>\n options?: Options\n}) => Query\n\nexport type HistoryFn = (\n sdkSessionId: string,\n options: { dir?: string },\n) => Promise<SessionMessage[]>\n\nexport type SessionInfoFn = (\n sdkSessionId: string,\n options: { dir?: string },\n) => Promise<SDKSessionInfo | undefined>\n\nexport type SessionRunnerConfig = CreateSessionRequest & {\n /** Injectable query implementation (tests, instrumentation). Defaults to the SDK's query(). */\n queryFn?: QueryFn\n /** Environment for the spawned Claude Code process. Defaults to process.env. */\n env?: Record<string, string | undefined>\n pathToClaudeCodeExecutable?: string\n /** Escape hatch merged last into the SDK Options. */\n extraOptions?: Partial<Options>\n /** Timeout for pending approvals when the request itself doesn't set one. Default 300000. */\n defaultApprovalTimeoutMs?: number\n /** With `resume`: emit the resumed session's history as replay events before the query\n * starts, so late-attaching clients get a full transcript. Default true. */\n backfillHistory?: boolean\n /** Injectable history reader (tests). Defaults to the SDK's getSessionMessages. */\n historyFn?: HistoryFn\n /** Injectable session-metadata reader (tests). Defaults to the SDK's\n * getSessionInfo — the only place the CLI's own session title is readable\n * from, since no message on the stream carries it. */\n sessionInfoFn?: SessionInfoFn\n}\n\nconst DEFAULT_APPROVAL_TIMEOUT_MS = 300_000\n\ntype PendingApproval = {\n request: PermissionRequest\n resolve: (result: PermissionResult) => void\n timer: ReturnType<typeof setTimeout>\n}\n\n/**\n * One live Agent SDK session: owns the query() call, the streaming input queue, the\n * pending-approval table, and a seq-numbered event log that subscribers can replay.\n * No transport — the server (or any host) subscribes and bridges to the wire.\n */\nexport class SessionRunner implements Runner {\n readonly id: string\n readonly createdAt: number\n\n #config: SessionRunnerConfig\n /** {@link SessionRunnerConfig.cwd}, checked once in the constructor. */\n readonly #cwd: string\n #events: SessionEvent[] = []\n #subscribers = new SubscriberSet()\n #seq = 0\n #activityCount = 0\n /**\n * Seq of the latest `conversation_reset` event, 0 when none. The log itself is\n * never truncated — it still carries the state-bearing events (`capabilities`,\n * `system_init`, …) a fresh attacher depends on and which are not re-emitted —\n * but `subscribe()` skips transcript *content* strictly below this mark, so a\n * replay does not resurrect a cleared conversation. A later reset supersedes\n * an earlier one by overwriting it.\n */\n #resetSeq = 0\n #status: SessionStatus = 'starting'\n #statusDetail: string | undefined\n #sdkSessionId: string | undefined\n #model: string | undefined\n #apiKeySource: string | undefined\n #permissionMode: PermissionMode | undefined\n #pending = new Map<string, PendingApproval>()\n /**\n * The turn ended while an approval was standing, and nothing has started a\n * new one since.\n *\n * `awaiting_approval` rightly outranks `idle` for display, so a turn-over\n * signal arriving under a standing approval cannot be applied when it lands.\n * It used to be **discarded** for that reason, which is a different thing\n * from outranked: the settle path then asserted `running` on the assumption\n * that an answered approval means work resumes, and when the turn was already\n * over — an interrupt, a timeout — the session claimed to be running one that\n * had produced its result. Status is purely edge-driven here, with no poll and\n * no reconciliation anywhere, so that single dropped edge never came back and\n * every client rendered it faithfully for the life of the session.\n *\n * So the fact is *deferred* rather than dropped, and it is deliberately\n * cleared the moment work genuinely resumes — a turn-over belongs to the turn\n * that produced it and must not settle the next one.\n */\n #turnOverWhileBlocked = false\n /** The read-time sub-agent rollup (`SessionInfo.subagents`), fed from #emit —\n * the one chokepoint — so the resume backfill reconstructs it for free. */\n #subagents = new SubagentTracker()\n #totalCostUsd: number | undefined\n #numTurns: number | undefined\n #lastActivityAt: number | undefined\n #input = new InputQueue()\n #query: Query | undefined\n #capabilitiesEmitted = false\n /** Last plan reported by the usage poll, so `plan_info` is emitted on change\n * rather than once per turn. */\n #subscriptionType: string | undefined\n /** The title the CLI gave this thread (see `#fetchEngineTitle`). Undefined\n * until it has one — a session gets its summary a turn or two in. */\n #engineTitle: string | undefined\n #started = false\n #closed = false\n #runPromise: Promise<void> | undefined\n\n constructor(config: SessionRunnerConfig, id: string = randomUUID()) {\n // Optional on the wire (an engine with no host filesystem takes none) but\n // required here: this one spawns the CLI in a real directory. The gateway\n // enforces it off `EngineCapabilities.hostCwd`, so reaching this throw means\n // a host built a runner around that check.\n if (!config.cwd) throw new Error('the claude engine requires a cwd')\n this.#cwd = config.cwd\n this.#config = config\n this.#permissionMode = config.permissionMode\n this.id = id\n this.createdAt = Date.now()\n }\n\n get status(): SessionStatus {\n return this.#status\n }\n\n get sdkSessionId(): string | undefined {\n return this.#sdkSessionId\n }\n\n get lastSeq(): number {\n return this.#seq\n }\n\n /** 'oauth' = claude.ai subscription credentials; other values are API-key provenance. */\n get apiKeySource(): string | undefined {\n return this.#apiKeySource\n }\n\n get pendingApprovals(): PermissionRequest[] {\n return [...this.#pending.values()].map((p) => p.request)\n }\n\n info(): SessionInfo {\n return {\n id: this.id,\n sdkSessionId: this.#sdkSessionId,\n status: this.#status,\n cwd: this.#cwd,\n profile: this.#config.profile,\n engine: 'claude',\n capabilities: ENGINE_CAPABILITIES.claude,\n model: this.#model ?? this.#config.model,\n permissionMode: this.#permissionMode,\n // Fixed at spawn: the CLI refuses to switch into bypass unless it was\n // launched for it (see #buildOptions). Reported so a client can disable\n // the mode rather than offer a switch that will be refused.\n canBypassPermissions:\n this.#config.permissionMode === 'bypassPermissions' ||\n this.#config.allowDangerouslySkipPermissions === true,\n apiKeySource: this.#apiKeySource,\n createdAt: this.createdAt,\n lastSeq: this.#seq,\n activityCount: this.#activityCount,\n pendingPermissionCount: this.#pending.size,\n subagents: this.#subagents.list(),\n meta: this.#config.meta,\n scope: this.#config.scope,\n title: this.#title(),\n totalCostUsd: this.#totalCostUsd,\n numTurns: this.#numTurns,\n lastActivityAt: this.#lastActivityAt,\n }\n }\n\n /**\n * Three sources, most-deliberate first: the host's own rename (`meta.title`),\n * the title the CLI gave this thread (`#engineTitle`), then the first prompt\n * truncated.\n *\n * The rename outranks everything by design — a person naming a session must\n * not have it renamed under them by a model — which is also why the engine\n * title is *only ever read* while `meta.title` is unset (see\n * `#fetchEngineTitle`), rather than read and then discarded here.\n */\n #title(): string | undefined {\n const metaTitle = this.#config.meta?.title\n if (typeof metaTitle === 'string' && metaTitle.length > 0) return metaTitle\n if (this.#engineTitle) return this.#engineTitle\n const prompt = this.#config.prompt\n if (!prompt) return undefined\n return prompt.length > 80 ? prompt.slice(0, 77) + '…' : prompt\n }\n\n /** Host-facing rename: writes `meta.title`, which `#title()` prefers. Clearing\n * it (undefined) restores the derived title. The engine is never told. */\n setTitle(title: string | undefined): void {\n const meta = { ...this.#config.meta }\n if (title) meta.title = title\n else delete meta.title\n this.#config = { ...this.#config, meta }\n }\n\n /** Begin the session. Idempotent; returns the run promise (resolves when the query ends). */\n start(): Promise<void> {\n if (this.#started) return this.#runPromise!\n this.#started = true\n if (this.#config.prompt) this.sendMessage(this.#config.prompt)\n this.#runPromise = this.#run()\n return this.#runPromise\n }\n\n /** Queue a user message for the session (starts the next turn when idle).\n *\n * `attachments` carry their own bytes; they reach the CLI as content blocks and\n * are logged as references. A message may be attachments alone — an empty text\n * block is not valid API input, so the text is only added when there is some. */\n sendMessage(text: string, attachments?: readonly AttachmentInput[]): void {\n if (this.#closed) throw new Error('session is closed')\n const blocks = attachments?.length ? attachmentContentBlocks(attachments) : []\n const content = blocks.length\n ? ([...blocks, ...(text ? [{ type: 'text', text }] : [])] as unknown as SDKUserMessage['message']['content'])\n : text\n this.#input.push({\n type: 'user',\n message: { role: 'user', content },\n parent_tool_use_id: null,\n session_id: this.#sdkSessionId,\n })\n // The SDK does not echo streamed-input user messages back, so the transcript\n // would never show them — emit the event here (the one place input enters).\n this.#emit({\n type: 'user_message',\n message: { role: 'user', content: text },\n parentToolUseId: null,\n attachments: attachments?.length ? attachments.map(attachmentRef) : undefined,\n uuid: randomUUID(),\n })\n }\n\n /** Live MCP server status, straight from the CLI. Undefined when the engine\n * can't answer (an injected fake query in tests) — the caller 501s rather than\n * pretending the session has no servers. */\n async mcpServers(): Promise<McpServerStatusInfo[] | undefined> {\n const query = this.#query\n if (typeof query?.mcpServerStatus !== 'function') return undefined\n return (await query.mcpServerStatus()).map(mcpStatusInfo)\n }\n\n async reconnectMcpServer(name: string): Promise<void> {\n const query = this.#query\n if (typeof query?.reconnectMcpServer !== 'function') {\n throw new Error('this session cannot reconnect MCP servers')\n }\n await query.reconnectMcpServer(name)\n }\n\n async setMcpServerEnabled(name: string, enabled: boolean): Promise<void> {\n const query = this.#query\n if (typeof query?.toggleMcpServer !== 'function') {\n throw new Error('this session cannot enable or disable MCP servers')\n }\n await query.toggleMcpServer(name, enabled)\n }\n\n /** Resolve a pending permission request. Returns false if the id is unknown (e.g. timed out). */\n resolvePermission(requestId: string, decision: PermissionDecision): boolean {\n const pending = this.#pending.get(requestId)\n if (!pending) return false\n this.#settleApproval(requestId, pending, decision, 'client')\n return true\n }\n\n async interrupt(): Promise<void> {\n await this.#query?.interrupt()\n }\n\n async setPermissionMode(mode: PermissionMode): Promise<void> {\n await this.#query?.setPermissionMode(mode)\n this.#permissionMode = mode\n this.#emit({ type: 'permission_mode_changed', mode })\n }\n\n /** Switch the model for subsequent responses; undefined = back to the default. */\n async setModel(model?: string): Promise<void> {\n await this.#query?.setModel(model)\n this.#model = model\n this.#emit({ type: 'model_changed', model })\n }\n\n /** Emit a session_error and terminate. For host-enforced policy (e.g. requireApiKey). */\n fail(message: string): void {\n if (this.#closed) return\n this.#emit({ type: 'session_error', message })\n this.#setStatus('failed')\n this.close('error')\n }\n\n /** Terminate the session and the underlying CLI subprocess. */\n close(reason: 'client' | 'server' | 'error' = 'client'): void {\n if (this.#closed) return\n this.#closed = true\n for (const [id, pending] of this.#pending) {\n this.#settleApproval(id, pending, { behavior: 'deny', message: 'Session closed' }, 'policy')\n }\n this.#input.end()\n this.#query?.close()\n this.#emit({ type: 'session_closed', reason })\n this.#setStatus('closed')\n }\n\n /** See `Runner.eventAt`. A linear scan: the one caller is a reader pressing\n * \"show everything\" on one row, so a per-runner seq index would be a map\n * maintained on every emit to save a walk nobody makes twice a minute. */\n eventAt(seq: number): SessionEvent | undefined {\n return this.#events.find((event) => event.seq === seq)\n }\n\n /**\n * Replay buffered events with seq > afterSeq, then deliver live events.\n * Returns an unsubscribe function.\n *\n * Replay honours the reset watermark: transcript content below the latest\n * `conversation_reset` is skipped (the reducer would clear it again anyway,\n * and a pre-reset client that never learned the reducer's case would render\n * a conversation the engine has discarded), while state-bearing events —\n * which are emitted once and never again — always replay. The reset event\n * itself replays (the skip is strictly-below), which is what clears a\n * reconnecting client still holding pre-reset rows; superseded resets are\n * content below the newer one and are skipped with what they cleared.\n */\n subscribe(\n listener: SessionEventListener,\n afterSeq = 0,\n options?: SubscribeOptions,\n ): () => void {\n return this.#subscribers.subscribe(this.#events, listener, afterSeq, options, this.#resetSeq)\n }\n\n async #run(): Promise<void> {\n const queryFn = this.#config.queryFn ?? (sdkQuery as QueryFn)\n try {\n await this.#backfillHistory()\n if (this.#closed) return\n this.#query = queryFn({ prompt: this.#input, options: this.#buildOptions() })\n // Without an initial prompt the CLI stays silent (no init handshake) until the\n // first message arrives, so 'starting' would never resolve — the session is\n // already accepting input, which is what 'idle' means. The control channel\n // does answer before init, though — fetch capabilities, a context baseline and\n // the plan's usage now so promptless sessions aren't blank until their first\n // turn. A session opened only to be watched may never have one.\n if (!this.#config.prompt) {\n this.#setStatus('idle')\n void this.#fetchCapabilities()\n void this.#fetchContextUsage()\n void this.#fetchRateLimits()\n }\n for await (const message of this.#query) {\n this.#handleMessage(message)\n }\n if (!this.#closed) {\n this.#closed = true\n this.#input.end()\n this.#emit({ type: 'session_closed', reason: 'server' })\n this.#setStatus('closed')\n }\n } catch (error) {\n if (!this.#closed) {\n this.#emit({\n type: 'session_error',\n message: error instanceof Error ? error.message : String(error),\n })\n this.#setStatus('failed')\n this.close('error')\n }\n }\n }\n\n /**\n * On resume, emit the prior session's transcript as replay events (seq'd before any\n * live event). The SDK only re-streams *user* messages on resume; assistant history\n * would otherwise be lost to clients attaching after a server restart. Duplicated\n * user messages are deduped client-side by uuid.\n */\n async #backfillHistory(): Promise<void> {\n const c = this.#config\n if (!c.resume || c.backfillHistory === false) return\n const historyFn = c.historyFn\n ?? ((sessionId: string, options: { dir?: string }) => getSessionMessages(sessionId, options))\n let messages: SessionMessage[]\n try {\n messages = await historyFn(c.resume, { dir: this.#cwd })\n } catch {\n // Best-effort: a missing/unreadable transcript must not block the resume itself.\n return\n }\n for (const m of messages) {\n if (this.#closed) return\n if (m.type === 'user') {\n const message = toApiMessage(m.message)\n this.#emit({\n type: 'user_message',\n message,\n parentToolUseId: m.parent_tool_use_id,\n replay: true,\n // The live path reads this off `isSynthetic` / `origin.kind`; a stored\n // message carries neither (see `isSyntheticUserText`), so the wrapper\n // text is the only thing left to read it from. Without it a resumed\n // session's `<task-notification>` blobs come back as blue user rows —\n // and, because `transcriptActivity` counts a non-synthetic user\n // message as a row, as unread badges for work nobody typed.\n synthetic: isSyntheticUserText(message) ? true : undefined,\n uuid: m.uuid,\n })\n } else if (m.type === 'assistant') {\n this.#emit({\n type: 'assistant_message',\n message: toApiMessage(m.message),\n parentToolUseId: m.parent_tool_use_id,\n replay: true,\n uuid: m.uuid,\n })\n }\n }\n }\n\n #buildOptions(): Options {\n const c = this.#config\n const options: Options = {\n cwd: this.#cwd,\n permissionMode: c.permissionMode,\n allowedTools: c.allowedTools,\n disallowedTools: c.disallowedTools,\n mcpServers: c.mcpServers as Options['mcpServers'],\n settingSources: c.settingSources,\n model: c.model,\n maxTurns: c.maxTurns,\n maxBudgetUsd: c.maxBudgetUsd,\n resume: c.resume,\n forkSession: c.forkSession,\n // Open string on the wire; the SDK's union lags the CLI's vocabulary and\n // the CLI silently downgrades an effort the model doesn't support.\n effort: c.reasoningEffort as Options['effort'],\n includePartialMessages: c.includePartialMessages ?? true,\n // Without this the SDK forwards only a subagent's tool_use/tool_result\n // blocks — \"enough for a heartbeat counter\", in its own words — and its\n // prompt, thinking and final report never reach the stream at all. That\n // is not a rendering gap a client can close: a nested transcript with no\n // text in it is a list of tool names. On, therefore, because this surface\n // claims to be the session rather than a summary of it; a host that wants\n // the quieter stream sets it back through `extraOptions`, which is spread\n // last precisely so it can.\n forwardSubagentText: true,\n canUseTool: this.#canUseTool,\n env: c.env,\n pathToClaudeCodeExecutable: c.pathToClaudeCodeExecutable,\n // The CLI refuses to *switch into* bypassPermissions unless it was spawned\n // with the capability — smoke-verified: \"Cannot set permission mode to\n // bypassPermissions because the session was not launched with\n // --dangerously-skip-permissions\".\n ...(c.permissionMode === 'bypassPermissions' || c.allowDangerouslySkipPermissions\n ? { allowDangerouslySkipPermissions: true }\n : {}),\n ...c.extraOptions,\n }\n return options\n }\n\n #handleMessage(msg: SDKMessage): void {\n if (msg.type === 'system' && msg.subtype === 'init') {\n this.#sdkSessionId = msg.session_id\n this.#model = msg.model\n this.#permissionMode = msg.permissionMode\n this.#apiKeySource = msg.apiKeySource\n this.#emit({\n type: 'system_init',\n sdkSessionId: msg.session_id,\n model: msg.model,\n cwd: msg.cwd,\n apiKeySource: msg.apiKeySource,\n tools: msg.tools,\n skills: msg.skills,\n slashCommands: msg.slash_commands,\n permissionMode: msg.permissionMode,\n claudeCodeVersion: msg.claude_code_version,\n mcpServers: msg.mcp_servers,\n })\n this.#turnOverWhileBlocked = false\n this.#setStatus('running')\n void this.#fetchCapabilities()\n void this.#fetchContextUsage()\n void this.#fetchRateLimits()\n // A resumed thread usually already has one; a fresh one will not for a\n // turn or two, which is what the turn-end poll is for.\n void this.#fetchEngineTitle()\n return\n }\n if (msg.type === 'system' && msg.subtype === 'session_state_changed') {\n // Authoritative turn-over signal — but a pending approval outranks it for\n // *display*, which is not a reason to forget what it said. Remember, and\n // apply it when the approval settles.\n if (this.#pending.size > 0) {\n if (msg.state === 'idle') this.#turnOverWhileBlocked = true\n else if (msg.state === 'running') this.#turnOverWhileBlocked = false\n return\n }\n if (msg.state === 'idle') this.#setStatus('idle')\n else if (msg.state === 'running') this.#setStatus('running')\n return\n }\n const body = normalizeSdkMessage(msg)\n if (body) {\n this.#emit(body)\n if (body.type === 'conversation_reset') {\n // Same session, fresh conversation: adopt the new conversation id now\n // rather than waiting for the follow-up system_init (which only arrives\n // with the next prompt) — a dormant record written in between must\n // resume the fresh conversation, not replay the cleared one. The next\n // system_init stays authoritative and overwrites it.\n if (body.sdkSessionId) this.#sdkSessionId = body.sdkSessionId\n // The window now holds an almost-empty conversation; re-poll so clients\n // aren't left staring at the cleared conversation's reading.\n void this.#fetchContextUsage()\n }\n if (body.type === 'turn_result') {\n // total_cost_usd / num_turns are session-cumulative on each result message.\n this.#totalCostUsd = body.totalCostUsd\n this.#numTurns = body.numTurns\n // Fallback for SDK versions without session_state_changed, deferred\n // under a standing approval for the reason above.\n if (this.#pending.size === 0) this.#setStatus('idle')\n else this.#turnOverWhileBlocked = true\n // Context usage moves every turn; the poll is a cheap control request.\n void this.#fetchContextUsage()\n void this.#fetchRateLimits()\n void this.#fetchEngineTitle()\n }\n }\n }\n\n /** Ask the CLI what models/commands it supports and surface them as an event\n * (replayed to late attachers). Called eagerly for promptless sessions and again\n * on init — the flag keeps it a single emit. Optional-chained: injected fake\n * queries in tests may not implement these, and a failure must not affect the\n * session. */\n async #fetchCapabilities(): Promise<void> {\n if (this.#capabilitiesEmitted) return\n const query = this.#query\n if (typeof query?.supportedModels !== 'function' || typeof query.supportedCommands !== 'function') {\n return\n }\n try {\n const [models, commands] = await Promise.all([\n query.supportedModels(),\n query.supportedCommands(),\n ])\n if (this.#closed || this.#capabilitiesEmitted) return\n this.#capabilitiesEmitted = true\n this.#emit({\n type: 'capabilities',\n models: modelOptionsFromSdk(models),\n defaultModel: defaultModelFromSdk(models),\n commands: commands.map((c) => ({\n name: c.name,\n description: c.description,\n argumentHint: c.argumentHint,\n aliases: c.aliases,\n })),\n })\n } catch {\n // Capabilities are best-effort decoration; the session works without them.\n }\n }\n\n /**\n * Adopt the title the CLI gave this thread — the \"friendly title\" it writes a\n * turn or two into a session, and the name a resumed thread already carries.\n *\n * A **poll, not an observation**, and unavoidably so: no member of the SDK's\n * `SDKMessage` union carries it (the whole union was checked). It lives on\n * `SDKSessionInfo`, which only `getSessionInfo` / `listSessions` return — the\n * same record `GET /sdk-sessions` already serves as `SdkSessionSummary`. So it\n * is read at init and after each turn, which is also roughly the rate at which\n * it changes.\n *\n * Two rules:\n * - **Never while `meta.title` is set.** A rename is a person's decision and a\n * generated summary must not overwrite it. Not read at all in that case, so\n * there is no stored value waiting to resurface if the rename is cleared —\n * the next turn simply fetches it again.\n * - `summary` falls back to the first prompt when the session has no real\n * title yet, so it is taken only when it *differs* from `firstPrompt`.\n * Otherwise `#title()`'s own prompt fallback covers it, and the two would\n * disagree only in how they truncate.\n *\n * Best-effort throughout: an unreadable transcript, a session file that is not\n * there yet, an SDK without the function — all leave the title as it was.\n */\n async #fetchEngineTitle(): Promise<void> {\n const metaTitle = this.#config.meta?.title\n if (typeof metaTitle === 'string' && metaTitle.length > 0) return\n const sdkSessionId = this.#sdkSessionId\n if (!sdkSessionId) return\n const read = this.#config.sessionInfoFn ?? getSessionInfo\n try {\n const info = await read(sdkSessionId, { dir: this.#cwd })\n if (this.#closed || !info) return\n const summary =\n info.summary && info.summary !== info.firstPrompt ? info.summary : undefined\n const title = info.customTitle || summary\n if (title) this.#engineTitle = title\n } catch {\n // The title is decoration; a session with none works exactly as well.\n }\n }\n\n /** Snapshot the context window after a turn and surface it as an event. Optional-chained\n * and best-effort for the same reasons as #fetchCapabilities. */\n async #fetchContextUsage(): Promise<void> {\n const query = this.#query\n if (typeof query?.getContextUsage !== 'function') return\n try {\n const usage = await query.getContextUsage()\n if (this.#closed) return\n this.#emit({\n type: 'context_usage',\n usage: {\n categories: usage.categories.map((c) => ({\n name: c.name,\n tokens: c.tokens,\n color: c.color,\n })),\n totalTokens: usage.totalTokens,\n maxTokens: usage.maxTokens,\n percentage: usage.percentage,\n model: usage.model,\n },\n })\n } catch {\n // Usage is best-effort decoration; the session works without it.\n }\n }\n\n /**\n * Snapshot the plan's rate-limit windows and surface them as `rate_limit`\n * events — the same event a live `rate_limit_event` produces, so clients need\n * nothing new to render it.\n *\n * The CLI only *pushes* a window when it changes, which for a session being\n * watched rather than driven can be never; polling is what makes usage show up\n * at all. The control request is marked experimental in the SDK, name included,\n * so it is probed for by name and every failure is silent — one more reason\n * this can only ever be decoration.\n */\n async #fetchRateLimits(): Promise<void> {\n const query = this.#query as\n | { usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET?: () => Promise<unknown> }\n | undefined\n const fetchUsage = query?.usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET\n if (typeof fetchUsage !== 'function') return\n try {\n const usage = (await fetchUsage.call(query)) as UsageRateLimits\n if (this.#closed) return\n // The plan names the windows, so it goes out ahead of them — and only when\n // it changes, since this is polled after every turn and the answer is the\n // same one all session long.\n const subscriptionType = usage.subscription_type\n if (subscriptionType && subscriptionType !== this.#subscriptionType) {\n this.#subscriptionType = subscriptionType\n this.#emit({ type: 'plan_info', subscriptionType })\n }\n for (const body of rateLimitEventsFromUsage(usage)) this.#emit(body)\n } catch {\n // Best-effort, and experimental on top of that.\n }\n }\n\n #canUseTool: CanUseTool = (toolName, input, options) => {\n const id = randomUUID()\n const timeoutMs = this.#config.approvalTimeoutMs ?? this.#config.defaultApprovalTimeoutMs\n ?? DEFAULT_APPROVAL_TIMEOUT_MS\n const request: PermissionRequest = {\n id,\n toolName,\n input,\n toolUseId: options.toolUseID,\n title: options.title,\n displayName: options.displayName,\n description: options.description,\n decisionReason: options.decisionReason,\n agentId: options.agentID,\n expiresAt: Date.now() + timeoutMs,\n }\n const questionBehavior = this.#config.questionBehavior ?? 'ask'\n if (toolName === 'AskUserQuestion' && questionBehavior !== 'ask') {\n delete request.expiresAt\n return Promise.resolve(this.#resolveQuestionByPolicy(request, questionBehavior))\n }\n return new Promise<PermissionResult>((resolve) => {\n const timer = setTimeout(() => {\n const pending = this.#pending.get(id)\n if (pending) {\n this.#settleApproval(\n id,\n pending,\n { behavior: 'deny', message: 'Approval timed out' },\n 'timeout',\n )\n }\n }, timeoutMs)\n this.#pending.set(id, { request, resolve, timer })\n options.signal.addEventListener('abort', () => {\n const pending = this.#pending.get(id)\n if (pending) {\n this.#settleApproval(\n id,\n pending,\n { behavior: 'deny', message: 'Turn aborted' },\n 'policy',\n )\n }\n })\n this.#emit({ type: 'permission_requested', request })\n this.#setStatus('awaiting_approval')\n })\n }\n\n /** 'auto'/'deny' sessions settle AskUserQuestion synchronously instead of pending:\n * 'auto' picks each question's first (recommended) option, 'deny' sends the model\n * back to decide for itself. Request/resolved events still fire so transcripts and\n * job webhooks show what was chosen. */\n #resolveQuestionByPolicy(request: PermissionRequest, mode: 'auto' | 'deny'): PermissionResult {\n this.#emit({ type: 'permission_requested', request })\n if (mode === 'deny') {\n const message =\n 'Interactive questions are disabled for this session — choose the most reasonable option yourself and continue.'\n this.#emit({\n type: 'permission_resolved',\n requestId: request.id,\n behavior: 'deny',\n resolvedBy: 'policy',\n message,\n })\n return { behavior: 'deny', message, toolUseID: request.toolUseId }\n }\n this.#emit({\n type: 'permission_resolved',\n requestId: request.id,\n behavior: 'allow',\n resolvedBy: 'policy',\n })\n return {\n behavior: 'allow',\n updatedInput: { ...request.input, answers: recommendedAnswers(request.input) },\n toolUseID: request.toolUseId,\n }\n }\n\n #settleApproval(\n id: string,\n pending: PendingApproval,\n decision: PermissionDecision,\n resolvedBy: 'client' | 'timeout' | 'policy',\n ): void {\n clearTimeout(pending.timer)\n this.#pending.delete(id)\n if (decision.behavior === 'allow') {\n pending.resolve({\n behavior: 'allow',\n // The SDK requires a record here even for an unmodified allow — echo the\n // original input back when the client didn't rewrite it.\n updatedInput: decision.updatedInput ?? pending.request.input,\n toolUseID: pending.request.toolUseId,\n })\n } else {\n pending.resolve({\n behavior: 'deny',\n message: decision.message ?? 'Denied',\n interrupt: decision.interrupt,\n toolUseID: pending.request.toolUseId,\n })\n }\n this.#emit({\n type: 'permission_resolved',\n requestId: id,\n behavior: decision.behavior,\n resolvedBy,\n message: decision.behavior === 'deny' ? (decision.message ?? 'Denied') : undefined,\n })\n if (this.#pending.size === 0) {\n // The deferred turn-over wins: this approval was the only thing standing\n // between the session and the truth. Consumed either way, so a later\n // approval in a live turn cannot inherit it.\n const endedWhileBlocked = this.#turnOverWhileBlocked\n this.#turnOverWhileBlocked = false\n if (endedWhileBlocked) this.#setStatus('idle')\n else if (this.#status === 'awaiting_approval') this.#setStatus('running')\n }\n }\n\n #setStatus(status: SessionStatus, detail?: string): void {\n if (this.#status === status && this.#statusDetail === detail) return\n // Terminal states win.\n if (this.#status === 'closed' || this.#status === 'failed') return\n this.#status = status\n this.#statusDetail = detail\n this.#emit({ type: 'status_changed', status, detail })\n }\n\n #emit(body: SessionEventBody): void {\n const event: SessionEvent = { ...body, seq: ++this.#seq, ts: Date.now() }\n this.#lastActivityAt = event.ts\n // Rows, not events: what a client diffs to know how much it missed. The\n // count is monotonic across a conversation_reset on purpose — it is an\n // unread cursor, not an item count (see SessionInfo.activityCount).\n this.#activityCount += transcriptActivity(body)\n if (body.type === 'conversation_reset') this.#resetSeq = event.seq\n // Before fan-out, like #pending: a listener that reads info() on this very\n // event must see it already folded in.\n this.#subagents.observe(body, event.ts)\n this.#events.push(event)\n this.#subscribers.emit(event)\n }\n}\n\n/** Answer each AskUserQuestion question with its first option's label — the tool's\n * convention puts the recommended choice first. Keyed by question text, the shape the\n * CLI expects back in `updatedInput.answers`. */\nfunction recommendedAnswers(input: Record<string, unknown>): Record<string, string> {\n const answers: Record<string, string> = {}\n const questions = Array.isArray(input.questions) ? input.questions : []\n for (const entry of questions) {\n const q = entry as { question?: unknown; options?: unknown }\n if (typeof q.question !== 'string' || !Array.isArray(q.options)) continue\n const first = q.options[0] as { label?: unknown } | undefined\n if (typeof first?.label === 'string') answers[q.question] = first.label\n }\n return answers\n}\n","import { randomUUID } from 'node:crypto'\nimport {\n ToolLoopAgent,\n generateText,\n isStepCount,\n type LanguageModel,\n type ModelMessage,\n type ToolSet,\n} from 'ai'\nimport {\n ENGINE_CAPABILITIES,\n snapshotRetains,\n transcriptActivity,\n type ContentBlock,\n type CreateSessionRequest,\n type McpServerStatusInfo,\n type PermissionMode,\n type PermissionRequest,\n type SessionEvent,\n type SessionEventBody,\n type SessionInfo,\n type SessionStatus,\n type ToolExecutionBackend,\n} from '@workerdeck/protocol'\nimport type { SandboxVfs } from '@workerdeck/sandbox'\nimport { type AttachmentInput, attachmentRef, normalizeMediaType } from '../../lib/attachments.ts'\nimport type {\n ParkedExecution,\n PermissionDecision,\n Runner,\n RunnerSnapshot,\n SessionEventListener,\n} from '../../runner-interface.ts'\nimport type { ToolExecutionCall, ToolExecutionResult, ToolExecutor } from '../../executors/tool-executor.ts'\nimport { SubscriberSet, type SubscribeOptions } from '../../lib/subscribers.ts'\n\n/** Permission modes this engine can honor. The rest of the protocol vocabulary\n * (acceptEdits/plan/auto) is Claude Code CLI semantics with no meaning here —\n * setPermissionMode rejects them, which the server surfaces as protocol_error. */\nconst SUPPORTED_PERMISSION_MODES: readonly PermissionMode[] = ['default', 'bypassPermissions', 'dontAsk']\n\n/** `cwd` is optional for this engine: the loop has no host-filesystem coupling\n * (tools get scoped VFS handles instead). Defaults to process.cwd() for display. */\nexport type AiSdkRunnerConfig = Omit<CreateSessionRequest, 'cwd'> & {\n cwd?: string\n /** AI SDK language model instance (or gateway model id string). Provider\n * resolution from profiles happens host-side; core takes the resolved model. */\n languageModel: LanguageModel\n /** Tools available to the loop. Tools WITHOUT `execute` halt the loop when\n * called; their calls surface via `pendingToolCalls` and are answered with\n * `resolveToolCall()`, which re-enters the loop by message-state replay. */\n tools?: ToolSet\n /** System prompt (AI SDK v7 `instructions`). */\n instructions?: string\n /** Max loop steps per turn. Default 20. */\n maxSteps?: number\n /**\n * Executes tool calls the loop cannot run inline (tools declared without\n * `execute`). With one set, the runner drives the whole cycle itself:\n * dispatch on park, apply the result, re-enter. Without one, parked calls\n * stay on {@link pendingToolCalls} for the host to answer via\n * {@link resolveToolCall}.\n */\n executor?: ToolExecutor\n /** Names the executor handles. Others stay pending for the host. */\n executableTools?: string[]\n /** Scratch filesystem handed to sandboxed executions. */\n vfs?: SandboxVfs\n /** Per-execution limits passed to the executor. */\n executionLimits?: { timeoutMs?: number; memoryLimitBytes?: number }\n /** Which backend the executor represents, for `execution_dispatched` events. */\n executionBackend?: ToolExecutionBackend\n /** Swap models mid-session (`set_model`). Unset = setModel() is rejected. */\n resolveModel?: (modelId: string | undefined) => LanguageModel\n /**\n * Live MCP status for this session, when the host wired MCP at all. Unlike\n * the CLI engines — which ask their binary — this engine's MCP is entirely\n * host-assembled, so the host is the only party that can answer. Unset means\n * \"no MCP here\", which reads as an empty list rather than an error: a session\n * with no servers is a fact, not a missing feature.\n *\n * Named apart from the inherited `mcpServers` request field on purpose —\n * that one is the *wire configuration* a client asked for, this one is what\n * the host actually connected.\n */\n reportMcpServers?: () => Promise<McpServerStatusInfo[] | undefined>\n /** Called once when the session closes — release per-session resources the\n * host attached (an MCP connection, a watcher). Errors are swallowed. Also\n * runs when the session parks: parking releases the same resources. */\n onClose?: () => void | Promise<void>\n /**\n * Rebuild a parked session from {@link AiSdkRunner.park}'s snapshot instead of\n * starting a fresh one: the id, event log, seq counter, message history, and\n * the executions it parked on are all adopted. The rest of the config is the\n * live wiring (model, tools, executor, VFS) and is taken as given — a\n * rehydrated session may legitimately come up against a re-created tool set.\n */\n restore?: RunnerSnapshot\n}\n\n/** An external (execute-less) tool call the loop is parked on. */\nexport type PendingToolCall = {\n toolCallId: string\n toolName: string\n input: unknown\n /** True when the executor declared the execution deferred — the session may\n * park on it, and only a host-delivered result can settle it. */\n deferred?: boolean\n /** Epoch ms the host's execution watchdog should fire at. */\n expiresAt?: number\n}\n\n/** The provider engine's half of a {@link RunnerSnapshot} — its continuation\n * state. Opaque to the host; only this class reads it. */\nexport type AiSdkSessionState = {\n messages: ModelMessage[]\n pendingToolCalls: PendingToolCall[]\n /** Calls already handed to an executor, so rehydration never re-dispatches them. */\n dispatched: string[]\n numTurns: number\n totalUsage: { input: number; output: number; cacheWrite: number; cacheRead: number }\n /** The in-progress turn's accumulator: a parked turn's earlier legs still owe\n * their tokens and elapsed time to the turn_result that eventually lands. */\n turnAccum?: { startedAt: number; input: number; output: number; cacheWrite: number; cacheRead: number }\n permissionMode: PermissionMode\n /** Model alias last requested (config.model or a set_model), NOT the resolved\n * provider model id — re-resolution goes back through `resolveModel`. */\n model?: string\n lastActivityAt?: number\n /** When the snapshot was taken, so a rehydrated turn can discount the time it\n * spent parked instead of billing it as elapsed turn duration. */\n parkedAt?: number\n}\n\nexport type ToolCallOutput =\n | { type: 'text'; value: string }\n | { type: 'json'; value: unknown }\n\n/**\n * Model-agnostic runner over the AI SDK v7 ToolLoopAgent. The session's durable\n * state is its ModelMessage history: every turn — including continuation after an\n * externally-executed tool call — is a fresh streamed call over that history\n * (message-state replay; the loop cannot be suspended). Output is emitted as it\n * happens: `stream_delta` per token (unless includePartialMessages is false) and\n * assistant/tool messages per step. Emits the same seq-numbered SessionEvent log\n * as SessionRunner; engine-specific CLI telemetry (system_init, capabilities,\n * rate_limit, ...) is simply never emitted.\n */\nexport class AiSdkRunner implements Runner {\n readonly id: string\n readonly createdAt: number\n\n #config: AiSdkRunnerConfig\n #model: LanguageModel\n #events: SessionEvent[] = []\n #subscribers = new SubscriberSet()\n #seq = 0\n #activityCount = 0\n #status: SessionStatus = 'starting'\n #permissionMode: PermissionMode\n #messages: ModelMessage[] = []\n #pendingToolCalls = new Map<string, PendingToolCall>()\n /** Calls already handed to the executor, so a re-park never double-dispatches. */\n #dispatched = new Set<string>()\n #turnChain: Promise<void> = Promise.resolve()\n #abort: AbortController | undefined\n /** Accumulates across every leg of one turn. A turn that parks on external\n * tool calls spans several generate() calls; usage and elapsed time must\n * cover all of them, not just the leg that happens to finish. */\n #turnAccum: { startedAt: number; input: number; output: number; cacheWrite: number; cacheRead: number } | undefined\n #numTurns = 0\n #totalUsage = { input: 0, output: 0, cacheWrite: 0, cacheRead: 0 }\n #lastActivityAt: number | undefined\n #started = false\n #closed = false\n /** Parked: state has been snapshotted and this instance is inert. Not closed —\n * the session lives on in the snapshot and resumes as a new instance. */\n #parked = false\n /** Model alias as requested (not the resolved provider id) — what set_model was\n * given, so a rehydrated session can re-resolve the same choice. */\n #modelAlias: string | undefined\n\n constructor(config: AiSdkRunnerConfig, id: string = randomUUID()) {\n const mode = config.permissionMode ?? 'default'\n if (!SUPPORTED_PERMISSION_MODES.includes(mode)) {\n throw new Error(`permission mode '${mode}' is not supported by the AI SDK engine`)\n }\n this.#config = config\n this.#model = config.languageModel\n this.#permissionMode = mode\n this.#modelAlias = config.model\n // A rehydrated session keeps its identity: same id, same age, same event log.\n this.id = config.restore?.id ?? id\n this.createdAt = config.restore?.createdAt ?? Date.now()\n if (config.restore) this.#restore(config.restore)\n }\n\n /** Adopt a parked session's state. The event log and seq counter come back\n * verbatim: a client reattaching with `afterSeq` must see one unbroken stream\n * across the teardown, not a second session that restarts at 1. */\n #restore(snapshot: RunnerSnapshot): void {\n if (snapshot.engine !== 'provider') {\n throw new Error(`cannot restore a '${snapshot.engine}' snapshot into the AI SDK engine`)\n }\n const state = snapshot.state as AiSdkSessionState | undefined\n if (!state || !Array.isArray(state.messages)) {\n throw new Error('session snapshot is missing its provider-engine state')\n }\n this.#seq = snapshot.seq\n this.#events = [...snapshot.events]\n // Recomputed rather than carried in the snapshot: the log IS the count, and\n // deriving it here means a rehydrated session cannot disagree with itself.\n this.#activityCount = this.#events.reduce((total, event) => total + transcriptActivity(event), 0)\n this.#messages = [...state.messages]\n for (const call of state.pendingToolCalls) this.#pendingToolCalls.set(call.toolCallId, call)\n // Already handed to a backend before the teardown: re-dispatching would run\n // the work twice (and a deferred backend can only ever answer once).\n this.#dispatched = new Set(state.dispatched)\n this.#numTurns = state.numTurns\n this.#totalUsage = { ...state.totalUsage }\n this.#turnAccum = state.turnAccum ? { ...state.turnAccum } : undefined\n if (this.#turnAccum && state.parkedAt !== undefined) {\n // The turn's clock stops while parked: a run that waited two days for a\n // remote result did not take two days of turn time.\n this.#turnAccum.startedAt += Date.now() - state.parkedAt\n }\n this.#permissionMode = state.permissionMode\n this.#lastActivityAt = state.lastActivityAt\n this.#status = this.#pendingToolCalls.size > 0 ? 'parked' : 'idle'\n if (state.model !== undefined && state.model !== this.#modelAlias && this.#config.resolveModel) {\n this.#modelAlias = state.model\n this.#model = this.#config.resolveModel(state.model)\n }\n }\n\n get status(): SessionStatus {\n return this.#status\n }\n\n get lastSeq(): number {\n return this.#seq\n }\n\n /** The session's durable state — persist to park, replay to rehydrate. */\n get messages(): ModelMessage[] {\n return [...this.#messages]\n }\n\n /** External tool calls the loop is currently parked on. */\n get pendingToolCalls(): PendingToolCall[] {\n return [...this.#pendingToolCalls.values()]\n }\n\n get pendingApprovals(): PermissionRequest[] {\n return []\n }\n\n /** The session's scratch filesystem (see Runner.vfs) — the server's file\n * routes serve deliverables straight from it. */\n get vfs(): SandboxVfs | undefined {\n return this.#config.vfs\n }\n\n info(): SessionInfo {\n return {\n id: this.id,\n status: this.#status,\n // Never process.cwd(): this engine opens no directory, and reporting the\n // gateway's own deploy path to every client would leak host layout into\n // a surface that has no business seeing it.\n cwd: this.#config.cwd ?? '',\n profile: this.#config.profile,\n engine: 'provider',\n capabilities: ENGINE_CAPABILITIES.provider,\n model: this.#modelId(),\n permissionMode: this.#permissionMode,\n createdAt: this.createdAt,\n lastSeq: this.#seq,\n activityCount: this.#activityCount,\n pendingPermissionCount: 0,\n meta: this.#config.meta,\n scope: this.#config.scope,\n title: this.#title(),\n numTurns: this.#numTurns || undefined,\n lastActivityAt: this.#lastActivityAt,\n }\n }\n\n start(): Promise<void> {\n if (this.#started) return this.#turnChain\n this.#started = true\n if (this.#config.restore) {\n // Rehydrated: the prompt was consumed by the original run, and the history\n // is whatever the snapshot captured. **Schedule nothing.** Waiting is the\n // whole point — a parked session re-enters the loop when an execution is\n // settled, and an idle one when the user says something.\n //\n // There used to be a `if (#pendingToolCalls.size === 0) #scheduleTurn()`\n // here, and it was unreachable: `park()` only ever produced a snapshot\n // while resting on deferred calls, so the size was never 0. `snapshot()`\n // makes it reachable, and it would be a live bug — an *interrupted* turn\n // leaves the history ending on the user's message (the catch path flushes\n // a partial `assistant_message` for the transcript but never pushes the\n // model's response messages, since the throw skipped that), so\n // `#runTurn`'s \"already answered\" guard would pass and the restored\n // session would re-run the very turn the user killed, unprompted, on first\n // attach. Restoring behaves exactly as the live session did: the\n // interrupted turn stays interrupted, and the next message answers both.\n return this.#turnChain\n }\n this.#setStatus('idle')\n if (this.#config.prompt) this.sendMessage(this.#config.prompt)\n return this.#turnChain\n }\n\n /**\n * Snapshot durable state, release engine resources, and go inert — the session\n * continues in the snapshot, not in this object. Returns undefined when parking\n * would lose work or has nothing to wait for: a turn in flight, no parked call,\n * or an already-closed/parked runner.\n */\n park(): RunnerSnapshot | undefined {\n if (this.#closed || this.#parked) return undefined\n // A generate() in flight cannot be snapshotted — its messages are not in the\n // history yet. Parking is only ever correct once the loop has come to rest on\n // external calls, which is exactly when #abort has been cleared.\n if (this.#abort || !this.#restingOnDeferred()) return undefined\n // Emitted before the snapshot so the persisted log carries the transition and\n // still-attached listeners see it.\n this.#setStatus('parked')\n const snapshot = this.#buildSnapshot()\n this.#parked = true\n this.#subscribers.clear()\n try {\n void Promise.resolve(this.#config.onClose?.()).catch(() => {})\n } catch {\n // Disposer errors must not break the park — the snapshot is already taken.\n }\n return snapshot\n }\n\n /**\n * The same snapshot, taken without ending anything.\n *\n * `park()` and this are two operations that happen to produce the same value,\n * and the difference is the whole point: `park()` *ends* the live runner\n * (inert, listeners dropped, `onClose` called), which is right for deferred\n * execution — the session has nothing to do for possibly days — and wrong for\n * restart-survival, where the session is active and someone is mid-\n * conversation. This one changes nothing at all: no status emit, no listener\n * clear, no disposer. The host writes the value through to durable storage\n * after each turn and keeps the runner live and warm, so a restart rebuilds\n * from the last write through the existing `restore` path and the next message\n * costs no wake.\n *\n * The gate is `park()`'s minus the requirement that there be something parked:\n *\n * - `#abort` set is refused for the reason it always was — a `generate()` in\n * flight has produced messages that are not in the history yet, so the\n * snapshot would be of a turn that half-happened.\n * - Pending calls that are **not** all deferred are refused, which is\n * `park()`'s rule wearing a different hat. An in-process execution's result\n * is coming back to *this* runner and dies with the process; a restore would\n * wait on it forever, and `state.dispatched` is what would stop the rebuilt\n * runner from simply calling it again.\n * - Idle with nothing pending — the case `park()` exists to refuse — is\n * exactly the case this exists to allow.\n */\n snapshot(): RunnerSnapshot | undefined {\n if (this.#closed || this.#parked || this.#abort) return undefined\n if (this.#pendingToolCalls.size > 0 && !this.#restingOnDeferred()) return undefined\n return this.#buildSnapshot()\n }\n\n /**\n * The snapshot value itself, shared so a park and a write-through cannot\n * disagree about what a session *is*.\n *\n * The event log is filtered through {@link snapshotRetains} — the persisted\n * log drops stream deltas, which are superseded by the `assistant_message`\n * that flushes them and would otherwise be tens of times the size of the text\n * they spell. Parks get it too, and should: a park sits on disk for days.\n *\n * The `parked` list and `state.parkedAt` are honest under both callers. An\n * idle write-through has no pending calls, so `parked` is empty and the host\n * arms no watchdogs; `parkedAt` is \"when this was taken\", which is what\n * `#restore` needs to discount a turn's clock either way.\n */\n #buildSnapshot(): RunnerSnapshot {\n const parked: ParkedExecution[] = [...this.#pendingToolCalls.values()].map((call) => ({\n executionId: call.toolCallId,\n toolName: call.toolName,\n expiresAt: call.expiresAt,\n }))\n const state: AiSdkSessionState = {\n messages: this.#messages,\n pendingToolCalls: [...this.#pendingToolCalls.values()],\n dispatched: [...this.#dispatched],\n numTurns: this.#numTurns,\n totalUsage: { ...this.#totalUsage },\n turnAccum: this.#turnAccum ? { ...this.#turnAccum } : undefined,\n permissionMode: this.#permissionMode,\n model: this.#modelAlias,\n lastActivityAt: this.#lastActivityAt,\n parkedAt: Date.now(),\n }\n return {\n engine: 'provider',\n id: this.id,\n createdAt: this.createdAt,\n seq: this.#seq,\n events: this.#events.filter((event) => snapshotRetains(event)),\n vfs: this.#config.vfs?.snapshot(),\n parked,\n state,\n }\n }\n\n sendMessage(text: string, attachments?: readonly AttachmentInput[]): void {\n if (this.#parked) throw new Error('session is parked')\n if (this.#closed) throw new Error('session is closed')\n // AI SDK v7 has one part type for attached bytes: `file`, with the media type\n // telling the provider what it is. Parts lead, text follows — same order the\n // Claude engine uses, for the same reason.\n const content = attachments?.length\n ? [\n ...attachments.map((attachment) => ({\n type: 'file' as const,\n data: attachment.data,\n mediaType: normalizeMediaType(attachment.mediaType),\n filename: attachment.name,\n })),\n ...(text ? [{ type: 'text' as const, text }] : []),\n ]\n : text\n this.#messages.push({ role: 'user', content })\n this.#emit({\n type: 'user_message',\n message: { role: 'user', content: text },\n parentToolUseId: null,\n attachments: attachments?.length ? attachments.map(attachmentRef) : undefined,\n uuid: randomUUID(),\n })\n this.#scheduleTurn()\n }\n\n /**\n * Deliver the result of an external (execute-less) tool call. Appends the\n * tool-result message and, once no calls remain pending, re-enters the loop.\n * Idempotent per toolCallId: unknown/already-settled ids return false.\n */\n resolveToolCall(toolCallId: string, output: ToolCallOutput, options?: { isError?: boolean }): boolean {\n if (!this.#settlePendingCall(toolCallId, output, options?.isError === true)) return false\n if (this.#pendingToolCalls.size === 0) this.#scheduleTurn()\n return true\n }\n\n /** Record a parked call's outcome into the message history (so it stays\n * replayable — a dangling tool call without a result is invalid input for\n * providers) and the event log. Does NOT re-enter the loop. */\n #settlePendingCall(toolCallId: string, output: ToolCallOutput, isError: boolean): boolean {\n const pending = this.#pendingToolCalls.get(toolCallId)\n if (!pending || this.#closed || this.#parked) return false\n this.#pendingToolCalls.delete(toolCallId)\n // Keep the result adjacent to the assistant message that made the call:\n // user messages typed while the turn was parked must sort AFTER the tool\n // results, or providers reject the replayed history (a tool call whose\n // result is not in the directly following message).\n let insertAt = this.#messages.length\n while (insertAt > 0 && this.#messages[insertAt - 1]!.role === 'user') insertAt--\n this.#messages.splice(insertAt, 0, {\n role: 'tool',\n content: [\n {\n type: 'tool-result',\n toolCallId,\n toolName: pending.toolName,\n output: (isError ? { type: 'error-text', value: textValue(output) } : output) as never,\n },\n ],\n })\n this.#emit({\n type: 'user_message',\n message: {\n role: 'user',\n content: [\n {\n type: 'tool_result',\n tool_use_id: toolCallId,\n content: textValue(output),\n is_error: isError || undefined,\n },\n ],\n },\n parentToolUseId: null,\n synthetic: true,\n uuid: randomUUID(),\n })\n return true\n }\n\n resolvePermission(_requestId: string, _decision: PermissionDecision): boolean {\n return false\n }\n\n /** Emit `file_delivered` — the deliver_file tool's hand-over event (wired by\n * createEngineSession via ToolContextOptions.onFileDelivered). */\n emitFileDelivered(file: { path: string; bytes: number; description?: string }): void {\n if (this.#closed || this.#parked) return\n this.#emit({ type: 'file_delivered', ...file })\n }\n\n /**\n * One plain generateText over the session's current model, billed into the\n * running turn's usage accumulator — the web_fetch digest pass uses this so\n * its tokens are never lost from the turn's accounting.\n */\n async generateDigest(prompt: string): Promise<string> {\n const result = await generateText({\n model: this.#model,\n prompt,\n abortSignal: this.#abort?.signal,\n })\n const accum = this.#turnAccum\n if (accum) {\n accum.input += result.usage.inputTokens ?? 0\n accum.output += result.usage.outputTokens ?? 0\n accum.cacheWrite += result.usage.inputTokenDetails?.cacheWriteTokens ?? 0\n accum.cacheRead += result.usage.inputTokenDetails?.cacheReadTokens ?? 0\n }\n return result.text\n }\n\n async interrupt(): Promise<void> {\n if (this.#abort) {\n this.#abort.abort()\n } else if (this.#pendingToolCalls.size > 0) {\n // A parked turn has no generate() in flight to abort. Fail the parked\n // calls (recorded as error results so the history stays replayable) and\n // finish the turn — otherwise a park nobody answers is unrecoverable.\n const accum = this.#turnAccum ?? { startedAt: Date.now(), input: 0, output: 0, cacheWrite: 0, cacheRead: 0 }\n // Snapshot first: settling mutates the map we are iterating.\n for (const call of Array.from(this.#pendingToolCalls.values())) {\n this.#settlePendingCall(call.toolCallId, { type: 'text', value: 'interrupted' }, true)\n }\n this.#dispatched.clear()\n this.#numTurns += 1\n this.#emit({\n type: 'turn_result',\n subtype: 'error_during_execution',\n isError: true,\n durationMs: Date.now() - accum.startedAt,\n numTurns: this.#numTurns,\n totalCostUsd: 0,\n errors: ['interrupted'],\n usage: turnUsage(accum),\n })\n this.#turnAccum = undefined\n this.#setStatus('idle')\n }\n await this.#turnChain\n }\n\n async setPermissionMode(mode: PermissionMode): Promise<void> {\n if (!SUPPORTED_PERMISSION_MODES.includes(mode)) {\n throw new Error(`permission mode '${mode}' is not supported by the AI SDK engine`)\n }\n this.#permissionMode = mode\n this.#emit({ type: 'permission_mode_changed', mode })\n }\n\n async setModel(model?: string): Promise<void> {\n const resolve = this.#config.resolveModel\n if (!resolve) throw new Error('set_model is not supported by this session')\n this.#model = resolve(model)\n this.#modelAlias = model\n this.#emit({ type: 'model_changed', model })\n }\n\n fail(message: string): void {\n if (this.#closed) return\n this.#emit({ type: 'session_error', message })\n this.#setStatus('failed')\n this.close('error')\n }\n\n close(reason: 'client' | 'server' | 'error' = 'client'): void {\n // Parked instances are already handed off — the host drops them from its\n // registry, and that must not read as the session ending.\n if (this.#closed || this.#parked) return\n this.#closed = true\n this.#abort?.abort()\n this.#pendingToolCalls.clear()\n this.#dispatched.clear()\n this.#emit({ type: 'session_closed', reason })\n this.#setStatus('closed')\n try {\n void Promise.resolve(this.#config.onClose?.()).catch(() => {})\n } catch {\n // Disposer errors must not break teardown.\n }\n }\n\n /** See `Runner.eventAt`. A linear scan: the one caller is a reader pressing\n * \"show everything\" on one row, so a per-runner seq index would be a map\n * maintained on every emit to save a walk nobody makes twice a minute. */\n eventAt(seq: number): SessionEvent | undefined {\n return this.#events.find((event) => event.seq === seq)\n }\n\n subscribe(\n listener: SessionEventListener,\n afterSeq = 0,\n options?: SubscribeOptions,\n ): () => void {\n return this.#subscribers.subscribe(this.#events, listener, afterSeq, options)\n }\n\n #scheduleTurn(): void {\n this.#turnChain = this.#turnChain.then(() => this.#runTurn())\n }\n\n /**\n * Deliver the result of an execution this runner dispatched. Used by the host\n * when a backend settled out-of-band (a browser bridge answering later, a\n * deferred executor). Idempotent by executionId.\n */\n settleExecution(executionId: string, result: ToolExecutionResult): boolean {\n if (this.#closed || this.#parked) return false\n if (!this.#pendingToolCalls.has(executionId)) return false\n this.#applyExecutionResult(executionId, result)\n return true\n }\n\n /** Hand every parked call the executor owns to it. */\n #dispatchPending(): void {\n const executor = this.#config.executor\n if (!executor) return\n const executable = this.#config.executableTools\n const inFlight: Array<Promise<unknown>> = []\n let anyDeferred = false\n // Snapshot first: applying a result mutates the map we are iterating.\n for (const call of Array.from(this.#pendingToolCalls.values())) {\n if (executable && !executable.includes(call.toolName)) continue\n if (this.#dispatched.has(call.toolCallId)) continue\n this.#dispatched.add(call.toolCallId)\n const toolCall: ToolExecutionCall = {\n executionId: call.toolCallId,\n sessionId: this.id,\n tool: call.toolName,\n input: call.input,\n vfs: this.#config.vfs,\n limits: this.#config.executionLimits,\n signal: this.#abort?.signal,\n }\n // Per call, not per executor: a routing executor may keep one tool in\n // process and defer another, and only the deferred one may park us.\n const profile = executor.describe?.(toolCall) ?? {}\n call.deferred = profile.deferred === true ? true : undefined\n call.expiresAt = profile.timeoutMs === undefined ? undefined : Date.now() + profile.timeoutMs\n anyDeferred ||= call.deferred === true\n this.#emit({\n type: 'execution_dispatched',\n executionId: call.toolCallId,\n toolName: call.toolName,\n backend: profile.backend ?? this.#config.executionBackend ?? 'server',\n deferred: call.deferred,\n expiresAt: call.expiresAt,\n })\n inFlight.push(\n executor\n .dispatch(toolCall)\n .then((dispatch) => {\n // 'pending' means the result arrives later via settleExecution().\n if (dispatch.status === 'settled') {\n this.#applyExecutionResult(call.toolCallId, dispatch.result)\n }\n })\n .catch((error: unknown) => {\n this.#applyExecutionResult(call.toolCallId, {\n status: 'failed',\n reason: 'dispatch_error',\n error: error instanceof Error ? error.message : String(error),\n })\n }),\n )\n }\n // Announce the park only once every dispatch of this batch has been handed\n // over: a host that parks on the first announcement would snapshot a session\n // whose remaining calls are still being dispatched — and dispatch them into a\n // runner it had already discarded.\n if (anyDeferred) void Promise.allSettled(inFlight).then(() => this.#announceParked())\n }\n\n /**\n * The turn has come to rest on deferred executions: nothing is in flight, and\n * only a host-delivered result can move it. `status_changed: 'parked'` is the\n * host's cue to snapshot via {@link park} — a single, correctly-timed signal\n * rather than an inference from individual dispatch events.\n */\n #announceParked(): void {\n if (this.#closed || this.#parked || this.#abort) return\n if (this.#restingOnDeferred()) this.#setStatus('parked')\n }\n\n /** The loop is waiting, and everything it waits on can only be answered from\n * outside this process. One still-live in-process execution means a result is\n * coming back to THIS runner, and tearing it down would strand it. */\n #restingOnDeferred(): boolean {\n if (this.#pendingToolCalls.size === 0) return false\n for (const call of this.#pendingToolCalls.values()) {\n if (call.deferred !== true) return false\n }\n return true\n }\n\n /** Fold an execution's outcome back into the loop, whichever way it went. */\n #applyExecutionResult(executionId: string, result: ToolExecutionResult): void {\n // A parked instance is not the session any more: its rehydrated successor owns\n // the pending call, and applying here would write into a discarded history.\n if (this.#closed || this.#parked) return\n this.#dispatched.delete(executionId)\n if (result.status === 'ok') {\n this.#emit({\n type: 'execution_result',\n executionId,\n output: { type: 'json', value: result.output },\n logs: result.logs,\n })\n this.resolveToolCall(executionId, { type: 'json', value: result.output })\n return\n }\n this.#emit({\n type: 'execution_failed',\n executionId,\n reason: result.reason,\n error: result.error,\n logs: result.logs,\n })\n // A failed execution is ordinary tool output: the agent gets to adapt.\n this.resolveToolCall(\n executionId,\n { type: 'text', value: `${result.reason}: ${result.error}` },\n { isError: true },\n )\n }\n\n async #runTurn(): Promise<void> {\n if (this.#closed || this.#parked || this.#pendingToolCalls.size > 0) return\n // Nothing to respond to: the history already ends with the assistant.\n // Happens when several triggers queued turns for the same input (a message\n // typed mid-park + the park resolving) — one turn answers all of it, the\n // stragglers must not burn a generate() on an already-answered history.\n if (this.#messages.at(-1)?.role === 'assistant') return\n this.#setStatus('running')\n const agent = new ToolLoopAgent({\n model: this.#model,\n tools: this.#config.tools ?? {},\n instructions: this.#config.instructions,\n stopWhen: isStepCount(this.#config.maxSteps ?? 20),\n })\n const abort = new AbortController()\n this.#abort = abort\n const accum = (this.#turnAccum ??= {\n startedAt: Date.now(),\n input: 0,\n output: 0,\n cacheWrite: 0,\n cacheRead: 0,\n })\n // Completed blocks of the step in progress, flushed as an assistant\n // message at each tool call (its result may follow immediately and the\n // transcript needs the call first) and at every step boundary. Declared\n // outside the try: the catch flushes what an interrupted turn had produced.\n let blocks: ContentBlock[] = []\n const textBuf = new Map<string, string>()\n const reasoningBuf = new Map<string, string>()\n const flush = (): void => {\n if (blocks.length === 0) return\n this.#emit({\n type: 'assistant_message',\n message: { role: 'assistant', content: blocks, model: this.#modelId() },\n parentToolUseId: null,\n uuid: randomUUID(),\n })\n blocks = []\n }\n try {\n // Streamed, not generate(): a multi-step turn must reach the transcript\n // as it happens — token deltas while text is produced, each step's\n // messages the moment the step completes — not as one blob at the end.\n const result = await agent.stream({\n messages: [...this.#messages],\n abortSignal: abort.signal,\n })\n const partials = this.#config.includePartialMessages !== false\n const emitToolResult = (toolCallId: string, content: string, isError?: boolean): void => {\n flush()\n this.#emit({\n type: 'user_message',\n message: {\n role: 'user',\n content: [{ type: 'tool_result', tool_use_id: toolCallId, content, is_error: isError }],\n },\n parentToolUseId: null,\n synthetic: true,\n uuid: randomUUID(),\n })\n }\n let streamError: unknown\n for await (const part of result.fullStream) {\n if (this.#closed) break\n switch (part.type) {\n case 'text-delta':\n textBuf.set(part.id, (textBuf.get(part.id) ?? '') + part.text)\n if (partials) {\n this.#emit({\n type: 'stream_delta',\n event: { type: 'content_block_delta', delta: { type: 'text_delta', text: part.text } },\n parentToolUseId: null,\n uuid: randomUUID(),\n })\n }\n break\n case 'text-end': {\n const text = textBuf.get(part.id)\n textBuf.delete(part.id)\n if (text) blocks.push({ type: 'text', text })\n break\n }\n case 'reasoning-delta':\n reasoningBuf.set(part.id, (reasoningBuf.get(part.id) ?? '') + part.text)\n if (partials) {\n this.#emit({\n type: 'stream_delta',\n event: { type: 'content_block_delta', delta: { type: 'thinking_delta', thinking: part.text } },\n parentToolUseId: null,\n uuid: randomUUID(),\n })\n }\n break\n case 'reasoning-end': {\n const thinking = reasoningBuf.get(part.id)\n reasoningBuf.delete(part.id)\n if (thinking) blocks.push({ type: 'thinking', thinking })\n break\n }\n case 'tool-call':\n blocks.push({\n type: 'tool_use',\n id: part.toolCallId,\n name: part.toolName,\n input: part.input,\n })\n flush()\n break\n case 'tool-result':\n emitToolResult(\n part.toolCallId,\n typeof part.output === 'string' ? part.output : JSON.stringify(part.output),\n )\n break\n case 'tool-error':\n emitToolResult(part.toolCallId, errorText(part.error), true)\n break\n case 'finish-step':\n flush()\n break\n case 'error':\n streamError ??= part.error\n break\n default:\n break\n }\n }\n flush()\n if (streamError !== undefined) throw streamError\n if (abort.signal.aborted) throw new Error('interrupted')\n const [responseMessages, usage, toolCalls, text] = await Promise.all([\n result.responseMessages,\n result.totalUsage,\n result.toolCalls,\n result.text,\n ])\n if (this.#closed) return\n // v7's totalUsage is already cumulative across THIS call's steps — add it\n // once per leg, never per step.\n accum.input += usage.inputTokens ?? 0\n accum.output += usage.outputTokens ?? 0\n accum.cacheWrite += usage.inputTokenDetails?.cacheWriteTokens ?? 0\n accum.cacheRead += usage.inputTokenDetails?.cacheReadTokens ?? 0\n this.#messages.push(...(responseMessages as ModelMessage[]))\n // Tool calls the SDK did not execute locally (no `execute`) park the loop.\n // Settled = every call with a tool message in the response — NOT\n // `result.toolResults`, which omits errored executions (`tool-error`\n // parts). An errored call was already fed back to the model by the SDK;\n // parking on it would hang the session forever (nobody owns it).\n const settled = new Set<string>()\n for (const message of responseMessages as ModelMessage[]) {\n if (message.role !== 'tool' || !Array.isArray(message.content)) continue\n for (const part of message.content) {\n if (part.type === 'tool-result') settled.add(part.toolCallId)\n }\n }\n for (const call of toolCalls) {\n if (settled.has(call.toolCallId)) continue\n this.#pendingToolCalls.set(call.toolCallId, {\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n input: call.input,\n })\n }\n if (this.#pendingToolCalls.size > 0) {\n // Parked: no turn_result yet. With an executor wired in, drive the\n // executions ourselves; otherwise the host answers via resolveToolCall.\n this.#dispatchPending()\n return\n }\n this.#finishTurn(text)\n } catch (error) {\n if (this.#closed) return\n // What the turn produced before it died is part of the record: without a\n // durable assistant_message the partial text exists only as stream\n // deltas, which the client holds in a singleton streaming item — wiped\n // by the *next* turn's message and glued onto by its deltas. An\n // interrupted minute of output must not vanish on the next question or\n // the next attach. Buffers still holding text mean the abort cut a block\n // mid-stream (no `text-end` came); completed-but-unflushed blocks are in\n // `blocks` already.\n for (const [, thinking] of reasoningBuf) {\n if (thinking) blocks.push({ type: 'thinking', thinking })\n }\n for (const [, text] of textBuf) {\n if (text) blocks.push({ type: 'text', text })\n }\n flush()\n const message = error instanceof Error ? error.message : String(error)\n this.#numTurns += 1\n this.#emit({\n type: 'turn_result',\n subtype: 'error_during_execution',\n isError: true,\n durationMs: Date.now() - accum.startedAt,\n numTurns: this.#numTurns,\n totalCostUsd: 0,\n errors: [abort.signal.aborted ? 'interrupted' : message],\n usage: turnUsage(accum),\n })\n this.#turnAccum = undefined\n this.#setStatus('idle')\n } finally {\n if (this.#abort === abort) this.#abort = undefined\n }\n }\n\n /** Emit the turn's result from the whole-turn accumulator, so a turn that\n * parked on external tool calls reports every leg's tokens and the full\n * elapsed time (including the time spent executing those tools). */\n #finishTurn(text: string): void {\n const accum = this.#turnAccum ?? { startedAt: Date.now(), input: 0, output: 0, cacheWrite: 0, cacheRead: 0 }\n this.#numTurns += 1\n this.#totalUsage.input += accum.input\n this.#totalUsage.output += accum.output\n this.#totalUsage.cacheWrite += accum.cacheWrite\n this.#totalUsage.cacheRead += accum.cacheRead\n this.#emit({\n type: 'turn_result',\n subtype: 'success',\n isError: false,\n durationMs: Date.now() - accum.startedAt,\n numTurns: this.#numTurns,\n totalCostUsd: 0,\n result: text,\n usage: turnUsage(accum),\n })\n this.#turnAccum = undefined\n this.#setStatus('idle')\n }\n\n #modelId(): string | undefined {\n const model = this.#model\n if (typeof model === 'string') return model\n return (model as { modelId?: string }).modelId\n }\n\n #title(): string | undefined {\n const metaTitle = this.#config.meta?.title\n if (typeof metaTitle === 'string' && metaTitle.length > 0) return metaTitle\n const prompt = this.#config.prompt\n if (!prompt) return undefined\n return prompt.length > 80 ? prompt.slice(0, 77) + '…' : prompt\n }\n\n /**\n * This session's MCP servers, as the host assembled them.\n *\n * Always answers — an empty list when no MCP was wired — because the\n * alternative (undefined, which the server turns into a 501) says \"this\n * engine cannot tell you\", and this engine can: the host that built the\n * session is the only party who knows, and it has been asked.\n */\n async mcpServers(): Promise<McpServerStatusInfo[] | undefined> {\n return (await this.#config.reportMcpServers?.()) ?? []\n }\n\n /** Host-facing rename: writes `meta.title`, which `#title()` prefers. Clearing\n * it (undefined) restores the derived title. The engine is never told. */\n setTitle(title: string | undefined): void {\n const meta = { ...this.#config.meta }\n if (title) meta.title = title\n else delete meta.title\n this.#config = { ...this.#config, meta }\n }\n\n #setStatus(status: SessionStatus, detail?: string): void {\n if (this.#status === status) return\n if (this.#status === 'closed' || this.#status === 'failed') return\n this.#status = status\n this.#emit({ type: 'status_changed', status, detail })\n }\n\n #emit(body: SessionEventBody): void {\n const event: SessionEvent = { ...body, seq: ++this.#seq, ts: Date.now() }\n this.#lastActivityAt = event.ts\n // Rows, not events: what a client diffs to know how much it missed.\n this.#activityCount += transcriptActivity(body)\n this.#events.push(event)\n this.#subscribers.emit(event)\n }\n}\n\nfunction turnUsage(accum: { input: number; output: number; cacheWrite: number; cacheRead: number }) {\n return {\n input_tokens: accum.input,\n output_tokens: accum.output,\n cache_creation_input_tokens: accum.cacheWrite,\n cache_read_input_tokens: accum.cacheRead,\n }\n}\n\nfunction textValue(output: ToolCallOutput): string {\n return output.type === 'text' ? output.value : JSON.stringify(output.value)\n}\n\nfunction errorText(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n","import { execFile } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\n\n/**\n * Credential presence for one Claude Code environment, as the CLI itself reports\n * it. 'unknown' means the check could not run at all (no binary, a CLI too old\n * for `auth status`, unparseable output) — which is NOT evidence of a missing\n * login and must never be surfaced as one.\n */\nexport type ClaudeAuthStatus = 'logged_in' | 'logged_out' | 'unknown'\n\n/** Injectable form of {@link checkClaudeAuth} (tests, custom probes). */\nexport type ClaudeAuthProbe = (\n env: Record<string, string | undefined>,\n) => Promise<ClaudeAuthStatus>\n\n/**\n * The native Claude Code binary the Agent SDK itself spawns, resolved the way\n * the SDK resolves it: the platform-specific optional dependency installed next\n * to the SDK package (`@anthropic-ai/claude-agent-sdk-<platform>-<arch>/claude`).\n * Probing this binary rather than whatever `claude` is on PATH means an auth\n * check answers for the executable sessions will actually run — the two can be\n * different versions logged into different places. Returns undefined when it\n * can't be found (optional dep skipped, unsupported platform); callers degrade\n * to 'unknown', and the SDK surfaces its own error if a session is created.\n */\nexport function resolveBundledClaudeExecutable(): string | undefined {\n try {\n // Two hops on purpose: the platform package is a dependency of the SDK, not\n // of this package, so under pnpm's strict layout it only resolves from the\n // SDK's own location.\n const fromHere = createRequire(import.meta.url)\n const fromSdk = createRequire(fromHere.resolve('@anthropic-ai/claude-agent-sdk'))\n const suffix = process.platform === 'win32' ? '.exe' : ''\n // On linux only the matching libc variant installs (os/cpu/libc on the\n // optional deps), so trying both flavours needs no musl detection.\n const platforms =\n process.platform === 'linux'\n ? [`linux-${process.arch}`, `linux-${process.arch}-musl`]\n : [`${process.platform}-${process.arch}`]\n for (const platform of platforms) {\n try {\n const path = fromSdk.resolve(`@anthropic-ai/claude-agent-sdk-${platform}/claude${suffix}`)\n if (existsSync(path)) return path\n } catch {\n // not installed — try the next candidate\n }\n }\n } catch {\n // the SDK itself doesn't resolve here; nothing to probe\n }\n return undefined\n}\n\n/**\n * Ask the CLI whether `env` holds usable credentials: `claude auth status`\n * prints a JSON verdict covering every source the CLI itself consults for that\n * environment — `<CLAUDE_CONFIG_DIR>/.credentials.json`, the macOS login\n * Keychain, ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, Bedrock/Vertex\n * (verified against 2.1.217). Only the `loggedIn` boolean is ever read; the\n * identity fields in the payload (email, org, subscription) never leave the\n * parse. The exit code is deliberately ignored — 2.1.217 exits 1 on a\n * logged-out verdict where other versions exit 0 — and anything that doesn't\n * parse to a `loggedIn` boolean is 'unknown', because `auth status` is not a\n * stable contract. Never rejects.\n */\nexport function checkClaudeAuth(\n env: Record<string, string | undefined>,\n options: { executable?: string; timeoutMs?: number } = {},\n): Promise<ClaudeAuthStatus> {\n const executable = options.executable ?? resolveBundledClaudeExecutable()\n if (!executable) return Promise.resolve('unknown')\n return new Promise((resolve) => {\n execFile(\n executable,\n ['auth', 'status'],\n // The timeout kills a hung CLI rather than leaking it; the killed child's\n // partial output then fails the parse below, which is the right verdict.\n { env: env as NodeJS.ProcessEnv, timeout: options.timeoutMs ?? 10_000 },\n (_error, stdout) => {\n try {\n const parsed = JSON.parse(stdout) as { loggedIn?: unknown }\n if (typeof parsed.loggedIn === 'boolean') {\n resolve(parsed.loggedIn ? 'logged_in' : 'logged_out')\n return\n }\n } catch {\n // not this CLI's JSON — fall through\n }\n resolve('unknown')\n },\n )\n })\n}\n","import { runScript, type SandboxEngine } from '@workerdeck/sandbox'\nimport type {\n ToolExecutionCall,\n ToolExecutionDispatch,\n ToolExecutionResult,\n ToolExecutor,\n} from './tool-executor.ts'\n\n/** Resolve a URL to text for the guest. Runs host-side with host authority —\n * this is where a credential may be attached, never inside the sandbox. */\nexport type HostFetch = (url: string, signal: AbortSignal) => Promise<string>\n\nexport type QuickJsExecutorOptions = {\n engine: SandboxEngine\n /**\n * Hostnames the guest may reach, exact or `*.example.com`. Empty/unset =\n * no network at all (the guest's fetchText throws). Matched host-side; the\n * guest is never told the allowlist and never holds a credential.\n */\n allowedHosts?: string[]\n /** Performs the actual request. Unset = global fetch, text body. */\n hostFetch?: HostFetch\n /** Per-fetch cap. The guest deadline does NOT cover host-function time, so\n * every capability needs its own bound. Default 10000. */\n fetchTimeoutMs?: number\n /** Default guest wall-clock limit when the call doesn't set one. Default 5000. */\n defaultTimeoutMs?: number\n /** Default guest allocator cap when the call doesn't set one. Default 64 MiB. */\n defaultMemoryLimitBytes?: number\n}\n\n/** Tool input for `eval_script`. */\ntype EvalScriptInput = { script?: unknown }\n\n/**\n * In-process execution backend: runs a tool's untrusted script in the QuickJS\n * WASM guest. Always settles inline — nothing downstream assumes that, which is\n * what lets a deferred backend replace it behind the same seam.\n */\nexport class QuickJsExecutor implements ToolExecutor {\n #options: QuickJsExecutorOptions\n\n constructor(options: QuickJsExecutorOptions) {\n this.#options = options\n }\n\n async dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch> {\n return {\n executionId: call.executionId,\n status: 'settled',\n result: await this.#execute(call),\n }\n }\n\n async #execute(call: ToolExecutionCall): Promise<ToolExecutionResult> {\n if (call.tool !== 'eval_script') {\n return {\n status: 'failed',\n reason: 'unsupported_tool',\n error: `tool '${call.tool}' is not executable by the QuickJS backend`,\n }\n }\n const script = (call.input as EvalScriptInput | undefined)?.script\n if (typeof script !== 'string') {\n return {\n status: 'failed',\n reason: 'invalid_input',\n error: 'eval_script requires a string `script` input',\n }\n }\n const result = await runScript(this.#options.engine, {\n script,\n vfs: call.vfs,\n signal: call.signal,\n timeoutMs: call.limits?.timeoutMs ?? this.#options.defaultTimeoutMs ?? 5000,\n memoryLimitBytes:\n call.limits?.memoryLimitBytes ?? this.#options.defaultMemoryLimitBytes ?? 64 * 1024 * 1024,\n fetchText: this.#allowsNetwork() ? (url) => this.#fetchText(url, call.signal) : undefined,\n })\n const logs = result.logs.map((l) => `[${l.level}] ${l.text}`)\n return result.ok\n ? { status: 'ok', output: result.value, logs }\n : { status: 'failed', reason: result.reason, error: result.error, logs }\n }\n\n #allowsNetwork(): boolean {\n return (this.#options.allowedHosts?.length ?? 0) > 0\n }\n\n async #fetchText(url: string, outer: AbortSignal | undefined): Promise<string> {\n if (!isHostAllowed(url, this.#options.allowedHosts ?? [])) {\n throw new Error(`host not allowed: ${safeHost(url) ?? url}`)\n }\n // The guest's interrupt deadline cannot preempt a host call — bound it here.\n const controller = new AbortController()\n const onOuterAbort = () => controller.abort()\n outer?.addEventListener('abort', onOuterAbort)\n const timer = setTimeout(() => controller.abort(), this.#options.fetchTimeoutMs ?? 10_000)\n try {\n const fetchImpl = this.#options.hostFetch ?? defaultHostFetch\n return await fetchImpl(url, controller.signal)\n } finally {\n clearTimeout(timer)\n outer?.removeEventListener('abort', onOuterAbort)\n }\n }\n}\n\nasync function defaultHostFetch(url: string, signal: AbortSignal): Promise<string> {\n const response = await fetch(url, { signal })\n if (!response.ok) throw new Error(`request failed: ${response.status}`)\n return await response.text()\n}\n\nfunction safeHost(url: string): string | undefined {\n try {\n return new URL(url).hostname\n } catch {\n return undefined\n }\n}\n\n/** Exact hostname match, or a single leading `*.` wildcard covering subdomains\n * (not the bare parent). Only http(s) — no file:, data:, or other schemes. */\nexport function isHostAllowed(url: string, allowedHosts: string[]): boolean {\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n return false\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') return false\n const host = parsed.hostname.toLowerCase()\n return allowedHosts.some((entry) => {\n const pattern = entry.trim().toLowerCase()\n if (!pattern) return false\n if (pattern.startsWith('*.')) return host.endsWith(pattern.slice(1))\n return host === pattern\n })\n}\n","/**\n * One registry for every request that leaves the runner and must come back:\n * permission approvals, browser-bridged tool calls, and deferred executions.\n * They differ only in who answers and how long that takes — the correlation,\n * timeout, idempotent settle, and provenance tagging are identical, so they\n * live here once.\n */\n\n/** What kind of async request this is. Purely descriptive — the mechanics are shared. */\nexport type PendingKind = 'approval' | 'tool_call' | 'execution'\n\n/** Who settled a request. Mirrors the existing approval vocabulary. */\nexport type SettledBy = 'client' | 'timeout' | 'policy' | 'server'\n\nexport type PendingOutcome<T> =\n | { ok: true; value: T; settledBy: SettledBy }\n | { ok: false; reason: string; error: string; settledBy: SettledBy }\n\nexport type PendingEntry = {\n id: string\n kind: PendingKind\n createdAt: number\n /** Epoch ms the timeout policy fires at, when one was set. */\n expiresAt?: number\n /** Caller-supplied descriptor for display/rehydration (tool name, request, ...). */\n meta?: Record<string, unknown>\n}\n\ntype Slot<T> = PendingEntry & {\n resolve: (outcome: PendingOutcome<T>) => void\n timer?: ReturnType<typeof setTimeout>\n}\n\nexport type RegisterOptions<T> = {\n id: string\n kind: PendingKind\n /** Fail the request automatically after this long. Omit for no deadline\n * (deferred executions whose watchdog lives elsewhere). */\n timeoutMs?: number\n meta?: Record<string, unknown>\n /** Called when the entry settles, however it settled. For emitting events. */\n onSettle?: (outcome: PendingOutcome<T>, entry: PendingEntry) => void\n}\n\nexport class PendingRequestRegistry {\n #slots = new Map<string, Slot<unknown>>()\n\n get size(): number {\n return this.#slots.size\n }\n\n /**\n * Register a request and get a promise for its outcome. The promise **never\n * rejects**: a timeout or cancellation resolves with `ok: false` so callers\n * feed the failure back into the agent loop instead of unwinding it.\n *\n * Re-registering a live id throws — silently replacing it would strand the\n * first waiter forever.\n */\n register<T>(options: RegisterOptions<T>): Promise<PendingOutcome<T>> {\n if (this.#slots.has(options.id)) {\n throw new Error(`pending request '${options.id}' is already registered`)\n }\n const entry: PendingEntry = {\n id: options.id,\n kind: options.kind,\n createdAt: Date.now(),\n expiresAt: options.timeoutMs === undefined ? undefined : Date.now() + options.timeoutMs,\n meta: options.meta,\n }\n return new Promise<PendingOutcome<T>>((resolve) => {\n const slot: Slot<T> = {\n ...entry,\n resolve: (outcome) => {\n options.onSettle?.(outcome, entry)\n resolve(outcome)\n },\n }\n if (options.timeoutMs !== undefined) {\n slot.timer = setTimeout(() => {\n this.#settle(options.id, {\n ok: false,\n reason: 'timeout',\n error: `request timed out after ${options.timeoutMs}ms`,\n settledBy: 'timeout',\n })\n }, options.timeoutMs)\n slot.timer.unref?.()\n }\n this.#slots.set(options.id, slot as Slot<unknown>)\n })\n }\n\n /** Deliver a result. Returns false for unknown or already-settled ids —\n * duplicate and late deliveries are no-ops, never a second application. */\n settle<T>(id: string, value: T, settledBy: SettledBy = 'client'): boolean {\n return this.#settle(id, { ok: true, value, settledBy })\n }\n\n /** Fail a request. Same idempotence guarantee as {@link settle}. */\n fail(id: string, reason: string, error: string, settledBy: SettledBy = 'server'): boolean {\n return this.#settle(id, { ok: false, reason, error, settledBy })\n }\n\n has(id: string): boolean {\n return this.#slots.has(id)\n }\n\n get(id: string): PendingEntry | undefined {\n const slot = this.#slots.get(id)\n return slot && toEntry(slot)\n }\n\n list(kind?: PendingKind): PendingEntry[] {\n const entries = [...this.#slots.values()].map(toEntry)\n return kind ? entries.filter((e) => e.kind === kind) : entries\n }\n\n /** Fail everything (optionally of one kind) — session close, turn interrupt. */\n cancelAll(reason: string, error: string, kind?: PendingKind): number {\n let canceled = 0\n // Snapshot ids first: settling mutates the map we would be iterating.\n for (const slot of Array.from(this.#slots.values())) {\n if (kind && slot.kind !== kind) continue\n if (this.#settle(slot.id, { ok: false, reason, error, settledBy: 'server' })) canceled += 1\n }\n return canceled\n }\n\n #settle(id: string, outcome: PendingOutcome<unknown>): boolean {\n const slot = this.#slots.get(id)\n if (!slot) return false\n clearTimeout(slot.timer)\n this.#slots.delete(id)\n slot.resolve(outcome)\n return true\n }\n}\n\nfunction toEntry(slot: Slot<unknown>): PendingEntry {\n return {\n id: slot.id,\n kind: slot.kind,\n createdAt: slot.createdAt,\n expiresAt: slot.expiresAt,\n meta: slot.meta,\n }\n}\n","import type { ToolCallRequestFrame, ToolExecutionOutput } from '@workerdeck/protocol'\nimport { PendingRequestRegistry, type PendingOutcome } from '../lib/pending-registry.ts'\nimport type {\n ToolExecutionCall,\n ToolExecutionDispatch,\n ToolExecutionResult,\n ToolExecutor,\n} from './tool-executor.ts'\n\n/** Answer a bridged call, as delivered by the client over the wire. */\nexport type BridgeAnswer =\n | { output: ToolExecutionOutput; logs?: string[] }\n | { reason: string; error: string; logs?: string[] }\n\nexport type BrowserBridgeExecutorOptions = {\n /**\n * Put a `tool_call_request` on the wire to the attached client. Returning\n * false means nobody is attached — the execution fails immediately rather\n * than hanging until its deadline.\n */\n send: (frame: ToolCallRequestFrame) => boolean\n /** Tell the client to abandon a call the server gave up on. */\n cancel?: (executionId: string, reason: string) => void\n /** How long to wait for the client before failing the execution. Default 60000. */\n timeoutMs?: number\n /**\n * Called once per dispatched execution when it reaches a terminal result,\n * however it got there (client answer, timeout, abort, no client). This is\n * the wire back into the agent loop — the host feeds it to the runner's\n * `resolveToolCall`. A timeout arrives here as a failed result, not silence.\n */\n onResult?: (executionId: string, result: ToolExecutionResult) => void\n /** Share the session's registry so approvals, bridged calls, and deferred\n * executions live in one table. Omit to get a private one. */\n registry?: PendingRequestRegistry\n}\n\n/**\n * Executes tool calls in the attached client's own sandbox. The first backend\n * that genuinely returns `pending`: dispatch puts a request on the wire and\n * returns, and the result arrives later through {@link resolve}.\n *\n * Data locality is the point — documents can stay in the browser and never\n * reach the server. The tradeoff is trust: whatever comes back is untrusted\n * input, fine for the user's own data but never a source for authoritative\n * server state (that is why MCP and secret-bearing tools are never bridged).\n */\nexport class BrowserBridgeExecutor implements ToolExecutor {\n readonly registry: PendingRequestRegistry\n #options: BrowserBridgeExecutorOptions\n /** Results that arrive before dispatch registers them (fast client, slow\n * bookkeeping) would otherwise be dropped — hold them briefly. */\n #early = new Map<string, BridgeAnswer>()\n\n constructor(options: BrowserBridgeExecutorOptions) {\n this.#options = options\n this.registry = options.registry ?? new PendingRequestRegistry()\n }\n\n async dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch> {\n const timeoutMs = call.limits?.timeoutMs ?? this.#options.timeoutMs ?? 60_000\n const expiresAt = Date.now() + timeoutMs\n const frame: ToolCallRequestFrame = {\n type: 'tool_call_request',\n executionId: call.executionId,\n toolName: call.tool,\n input: call.input,\n vfsSeed: call.vfs?.snapshot(),\n limits: call.limits,\n expiresAt,\n }\n\n const settled = this.registry.register<BridgeAnswer>({\n id: call.executionId,\n kind: 'tool_call',\n timeoutMs,\n meta: { toolName: call.tool, sessionId: call.sessionId },\n })\n\n if (!this.#options.send(frame)) {\n this.registry.fail(call.executionId, 'no_client', 'no client is attached to execute this call')\n // Nobody can ever answer this one — settle it inline rather than making\n // the caller wait out a deadline for a result that cannot come.\n return {\n executionId: call.executionId,\n status: 'settled',\n result: toExecutionResult(await settled),\n }\n }\n\n // Drain an answer that beat the registration.\n const early = this.#early.get(call.executionId)\n if (early) {\n this.#early.delete(call.executionId)\n this.#applyAnswer(call.executionId, early)\n }\n\n // Only fail it here — the settle handler below owns sending the cancel, so\n // every non-client failure notifies the client exactly once.\n const onAbort = () => {\n this.registry.fail(call.executionId, 'aborted', 'the turn was interrupted')\n }\n call.signal?.addEventListener('abort', onAbort, { once: true })\n void settled.then((outcome) => {\n call.signal?.removeEventListener('abort', onAbort)\n // Let the client stop working on anything it can no longer answer.\n if (!outcome.ok && outcome.settledBy !== 'client') {\n this.#options.cancel?.(call.executionId, outcome.reason)\n }\n this.#options.onResult?.(call.executionId, toExecutionResult(outcome))\n })\n\n return { executionId: call.executionId, status: 'pending' }\n }\n\n /**\n * Apply a client's answer. Returns false when the id is unknown or already\n * settled — a late result after a timeout must not re-open a settled call.\n */\n resolve(executionId: string, answer: BridgeAnswer): boolean {\n if (!this.registry.has(executionId)) {\n // Racing a dispatch still in flight; hold it briefly for the drain above.\n this.#early.set(executionId, answer)\n setTimeout(() => this.#early.delete(executionId), 5000).unref?.()\n return false\n }\n return this.#applyAnswer(executionId, answer)\n }\n\n #applyAnswer(executionId: string, answer: BridgeAnswer): boolean {\n return 'output' in answer\n ? this.registry.settle(executionId, answer, 'client')\n : this.registry.fail(executionId, answer.reason, answer.error, 'client')\n }\n}\n\n/** Map a registry outcome onto the executor's result contract. */\nexport function toExecutionResult(outcome: PendingOutcome<BridgeAnswer>): ToolExecutionResult {\n if (outcome.ok && 'output' in outcome.value) {\n const { output, logs } = outcome.value\n return { status: 'ok', output: output.type === 'text' ? output.value : output.value, logs }\n }\n if (outcome.ok) {\n const failure = outcome.value as { reason: string; error: string; logs?: string[] }\n return { status: 'failed', reason: failure.reason, error: failure.error, logs: failure.logs }\n }\n return { status: 'failed', reason: outcome.reason, error: outcome.error }\n}\n","import type { ToolExecutionBackend } from '@workerdeck/protocol'\nimport type {\n ToolExecutionCall,\n ToolExecutionDispatch,\n ToolExecutionProfile,\n ToolExecutor,\n} from './tool-executor.ts'\n\n/** A dispatched execution, as handed to the backend that will run it. */\nexport type DeferredDispatch = {\n /** Correlation id. The result is delivered under it — `POST\n * {basePath}/executions/:executionId/result` — and applied idempotently. */\n executionId: string\n sessionId: string\n tool: string\n input: unknown\n /** The session's scratch filesystem at dispatch time, by value. */\n vfsSeed?: Record<string, string>\n limits?: { timeoutMs?: number; memoryLimitBytes?: number }\n /** Epoch ms the host's execution watchdog fires at, when a timeout was configured. */\n expiresAt?: number\n}\n\nexport type DeferredExecutorOptions = {\n /**\n * Hand the call to whatever actually runs it — enqueue it, POST it to a worker,\n * page a human. Called synchronously during dispatch; throwing fails the\n * execution (the failure reaches the agent as ordinary tool output).\n */\n onDispatch: (call: DeferredDispatch) => void | Promise<void>\n /** How long the result may take before the host's watchdog fails the execution.\n * Unset = no deadline; the execution then relies on the job's parked cap. */\n timeoutMs?: number\n /** Reported on `execution_dispatched`. Default 'remote'. */\n backend?: ToolExecutionBackend\n}\n\n/**\n * The executor for work that outlives the session's process residency: dispatch\n * hands the call off and returns `pending` **without holding a promise**, because\n * the runner it would resolve into is about to be torn down. The result can only\n * come back through the host — the execution-result route → `settleExecution` on a\n * rehydrated runner — which is exactly what makes a park durable rather than a\n * long in-memory await.\n *\n * Contrast {@link BrowserBridgeExecutor}, which is also `pending` but keeps its\n * answer in memory for the ~60s the tab has to reply.\n */\nexport class DeferredExecutor implements ToolExecutor {\n readonly backend: ToolExecutionBackend\n readonly timeoutMs: number | undefined\n #options: DeferredExecutorOptions\n\n constructor(options: DeferredExecutorOptions) {\n this.#options = options\n this.backend = options.backend ?? 'remote'\n this.timeoutMs = options.timeoutMs\n }\n\n /** Every call this executor takes is deferred — route only the tools that\n * belong on the remote side to it. */\n describe(): ToolExecutionProfile {\n return { backend: this.backend, deferred: true, timeoutMs: this.timeoutMs }\n }\n\n async dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch> {\n await this.#options.onDispatch({\n executionId: call.executionId,\n sessionId: call.sessionId,\n tool: call.tool,\n input: call.input,\n vfsSeed: call.vfs?.snapshot(),\n limits: call.limits,\n expiresAt: this.timeoutMs === undefined ? undefined : Date.now() + this.timeoutMs,\n })\n return { executionId: call.executionId, status: 'pending' }\n }\n}\n","import { tool, type Tool, type ToolSet } from 'ai'\nimport { z } from 'zod'\nimport { createVfs, type SandboxVfs } from '@workerdeck/sandbox'\nimport type { ToolExecutionResult, ToolExecutor } from '../../executors/tool-executor.ts'\nimport type { WebFetchFn } from './web-fetch.ts'\n\n/**\n * How much authority a tool carries, which decides where it may run.\n *\n * - `sandboxed` — no ambient authority; safe to execute anywhere, including an\n * untrusted browser tab. Its results are untrusted input.\n * - `authoritative` — runs server-side with server credentials (MCP, secret-bearing\n * APIs). **Never bridged to a client**: bridging it would hand a browser the\n * ability to forge authoritative results.\n */\nexport type ToolTrust = 'sandboxed' | 'authoritative'\n\nexport type ToolDefinition = {\n name: string\n trust: ToolTrust\n /** The AI SDK tool. Sandboxed tools are declared WITHOUT `execute` so the loop\n * hands them to the ToolExecutor seam rather than running them inline. */\n tool: Tool\n}\n\nexport type ToolContextOptions = {\n /** Executor for sandboxed tools. Selected per call by the host (browser bridge\n * when a client is attached, server QuickJS otherwise). */\n executor: ToolExecutor\n sessionId: string\n /** Scratch filesystem shared by this session's sandboxed tools. */\n vfs?: SandboxVfs\n /** Search backend. Omitted = `web_search` is not granted at all. */\n search?: (query: string, limit: number) => Promise<Array<{ title: string; url: string; snippet?: string }>>\n /** Document fetcher for `download`. Omitted = the tool is not granted. */\n download?: (url: string) => Promise<{ contentType?: string; text: string }>\n /** Page digester for `web_fetch` (see {@link createWebFetch}). Omitted = the\n * tool is not granted. */\n webFetch?: WebFetchFn\n /** Notified when the agent hands over a VFS file via `deliver_file`, so the\n * host can emit the `file_delivered` session event. The tool is only granted\n * when this is set — a delivery nobody hears is not a delivery. */\n onFileDelivered?: (file: { path: string; bytes: number; description?: string }) => void\n /** Per-call sandbox limits. */\n limits?: { timeoutMs?: number; memoryLimitBytes?: number }\n /** Notified when a sandboxed execution is dispatched and when it settles, so\n * the host can emit execution_* events. */\n onDispatch?: (executionId: string, toolName: string) => void\n onSettle?: (executionId: string, result: ToolExecutionResult) => void\n}\n\n/** Everything a session's tools need, plus the tool set to hand the runner. */\nexport type ToolContext = {\n vfs: SandboxVfs\n tools: ToolSet\n definitions: ToolDefinition[]\n /** Names the loop must not execute inline (they go through the executor). */\n sandboxedToolNames: string[]\n}\n\nconst MAX_FILE_BYTES = 1024 * 1024\n\n/**\n * Build the capability-scoped tool set for a session.\n *\n * The agent's authority is exactly what is granted here — there are no built-in\n * filesystem or shell tools, and nothing reaches the host filesystem: `fs_*`\n * operate on an in-memory scratch VFS. Tools whose backend is not supplied are\n * simply absent rather than present-and-failing, so a model cannot be tempted\n * by a capability the operator did not grant.\n */\nexport function createToolContext(options: ToolContextOptions): ToolContext {\n const vfs = options.vfs ?? createVfs()\n const definitions: ToolDefinition[] = []\n\n // --- Scratch filesystem (server-side, in-memory; never the host disk) -----\n definitions.push({\n name: 'fs_list',\n trust: 'authoritative',\n tool: tool({\n description: 'List files in the scratch filesystem.',\n inputSchema: z.object({ dir: z.string().default('/').describe('Directory to list') }),\n execute: async ({ dir }) => ({ files: vfs.list(dir) }),\n }),\n })\n definitions.push({\n name: 'fs_read',\n trust: 'authoritative',\n tool: tool({\n description: 'Read a file from the scratch filesystem.',\n inputSchema: z.object({ path: z.string() }),\n execute: async ({ path }) => {\n const content = vfs.read(path)\n if (content === undefined) return { error: `no such file: ${path}` }\n return { content: truncate(content) }\n },\n }),\n })\n definitions.push({\n name: 'fs_write',\n trust: 'authoritative',\n tool: tool({\n description: 'Write a file to the scratch filesystem.',\n inputSchema: z.object({ path: z.string(), content: z.string() }),\n execute: async ({ path, content }) => {\n vfs.write(path, content)\n return { path, bytes: content.length }\n },\n }),\n })\n\n // --- File hand-over: only when the host listens for deliveries ------------\n if (options.onFileDelivered) {\n const onFileDelivered = options.onFileDelivered\n definitions.push({\n name: 'deliver_file',\n trust: 'authoritative',\n tool: tool({\n description:\n 'Hand a file from the scratch filesystem over to the user as a deliverable. ' +\n 'Write it with fs_write first, then deliver it.',\n inputSchema: z.object({\n path: z.string().describe('Path of an existing file in the scratch filesystem'),\n description: z.string().optional().describe('What this file is, for the recipient'),\n }),\n execute: async ({ path, description }) => {\n const content = vfs.read(path)\n if (content === undefined) return { error: `no such file: ${path}` }\n const file = { path, bytes: content.length, description }\n onFileDelivered(file)\n return { delivered: true, ...file }\n },\n }),\n })\n }\n\n // --- Network capabilities: only when the host supplied a backend ----------\n if (options.search) {\n const search = options.search\n definitions.push({\n name: 'web_search',\n trust: 'authoritative',\n tool: tool({\n description: 'Search the web for pages relevant to a query.',\n inputSchema: z.object({\n query: z.string(),\n limit: z.number().int().min(1).max(25).default(5),\n }),\n execute: async ({ query, limit }) => ({ results: await search(query, limit) }),\n }),\n })\n }\n if (options.download) {\n const download = options.download\n definitions.push({\n name: 'download',\n trust: 'authoritative',\n tool: tool({\n description:\n 'Fetch a URL and store its text in the scratch filesystem for later evaluation.',\n inputSchema: z.object({\n url: z.string().describe('Absolute http(s) URL'),\n path: z.string().describe('Where to store it in the scratch filesystem'),\n }),\n execute: async ({ url, path }) => {\n try {\n const { text, contentType } = await download(url)\n const stored = truncate(text)\n vfs.write(path, stored)\n return { path, bytes: stored.length, contentType }\n } catch (error) {\n // A failed fetch is data the agent can react to, not a turn-ending throw.\n return { error: error instanceof Error ? error.message : String(error) }\n }\n },\n }),\n })\n }\n\n if (options.webFetch) {\n const webFetch = options.webFetch\n definitions.push({\n name: 'web_fetch',\n trust: 'authoritative',\n tool: tool({\n description:\n 'Fetch a web page and process its content against a prompt. Returns the answer ' +\n '(or the page as markdown). Distinct from download: use web_fetch to answer a ' +\n 'question about a page, download to store raw text for eval_script.',\n inputSchema: z.object({\n url: z.string().describe('Absolute http(s) URL'),\n prompt: z.string().describe('What to extract or answer from the page'),\n }),\n execute: async ({ url, prompt }) => {\n try {\n return await webFetch(url, prompt)\n } catch (error) {\n // A failed fetch is data the agent can react to, not a turn-ending throw.\n return { error: error instanceof Error ? error.message : String(error) }\n }\n },\n }),\n })\n }\n\n // --- Untrusted evaluation: no `execute`, so it rides the executor seam ----\n definitions.push({\n name: 'eval_script',\n trust: 'sandboxed',\n tool: tool({\n description:\n 'Evaluate a JavaScript snippet in a sandbox to parse, score, or extract from files. ' +\n 'Globals: vfs.read(path), vfs.write(path, text), vfs.list(dir), console.log. ' +\n 'The value of the last expression is returned. No network or host access.',\n inputSchema: z.object({ script: z.string() }),\n }),\n })\n\n const tools: ToolSet = {}\n for (const definition of definitions) tools[definition.name] = definition.tool\n\n return {\n vfs,\n tools,\n definitions,\n sandboxedToolNames: definitions.filter((d) => d.trust === 'sandboxed').map((d) => d.name),\n }\n}\n\n/** Add host-side MCP tools to a context. They are ALWAYS authoritative: they run\n * server-side with server credentials, and must never be handed to a browser. */\nexport function withMcpTools(context: ToolContext, mcpTools: ToolSet): ToolContext {\n return withHostTools(\n context,\n Object.fromEntries(\n Object.entries(mcpTools).map(([name, mcpTool]) => [\n name,\n { tool: mcpTool, trust: 'authoritative' as const },\n ]),\n ),\n 'MCP tool',\n )\n}\n\n/** A tool the host supplies, with the trust level it is to run at. */\nexport type HostToolDefinition = {\n tool: Tool\n /**\n * Where this tool may run. `authoritative` tools execute inline in the\n * gateway and MUST declare `execute`; `sandboxed` ones must NOT, because the\n * loop hands them to the {@link ToolExecutor} seam instead — which is what\n * makes them bridgeable to an untrusted tab.\n */\n trust: ToolTrust\n}\n\n/**\n * Add host-supplied tools to a context at an explicit trust level.\n *\n * The trust level is the whole point of the seam: {@link withMcpTools} can only\n * produce authoritative tools, so a host tool that *should* be sandboxed — and\n * therefore executable in the browser tab that asked for it — had no way to be\n * expressed at all. Here the host says which it is, and the contradictions are\n * refused rather than silently resolved:\n *\n * - a `sandboxed` tool carrying `execute` would run inline in this process with\n * the gateway's ambient authority, which is exactly what sandboxing it was\n * meant to prevent;\n * - an `authoritative` tool *without* `execute` would park the turn on a call no\n * executor claims, and the session would simply stop.\n */\nexport function withHostTools(\n context: ToolContext,\n hostTools: Record<string, HostToolDefinition>,\n /** What to call these in error messages ('MCP tool', 'host tool'). */\n kind = 'host tool',\n): ToolContext {\n const entries = Object.entries(hostTools)\n if (entries.length === 0) return context\n const definitions = [...context.definitions]\n const tools: ToolSet = { ...context.tools }\n const sandboxedToolNames = [...context.sandboxedToolNames]\n for (const [name, { tool: hostTool, trust }] of entries) {\n if (name in tools) {\n // Silently overwriting would let a host tool shadow `eval_script` — or an\n // MCP name promote untrusted execution to authoritative. Refuse instead.\n throw new Error(`${kind} '${name}' collides with an existing tool of the same name`)\n }\n const executes = typeof (hostTool as { execute?: unknown }).execute === 'function'\n if (trust === 'sandboxed' && executes) {\n throw new Error(\n `${kind} '${name}' is declared sandboxed but has an \\`execute\\` — it would run in ` +\n 'this process with full authority. Drop `execute` so it rides the ToolExecutor seam.',\n )\n }\n if (trust === 'authoritative' && !executes) {\n throw new Error(\n `${kind} '${name}' is declared authoritative but has no \\`execute\\` — nothing would ` +\n 'ever answer its calls and the turn would stall.',\n )\n }\n definitions.push({ name, trust, tool: hostTool })\n tools[name] = hostTool\n if (trust === 'sandboxed') sandboxedToolNames.push(name)\n }\n return { ...context, tools, definitions, sandboxedToolNames }\n}\n\nfunction truncate(text: string): string {\n return text.length > MAX_FILE_BYTES ? text.slice(0, MAX_FILE_BYTES) : text\n}\n","import { lookup } from 'node:dns/promises'\n\n/**\n * `web_fetch` backend, close to Claude Code's original WebFetch: fetch a URL,\n * convert HTML to markdown, and (optionally) digest it with a model against the\n * caller's prompt. Server-side only — this runs with server egress, which is\n * exactly why it is an authoritative capability the operator grants explicitly.\n */\n\nexport type WebFetchResult = {\n /** The URL that was fetched (after same-host redirects). */\n url: string\n /** Model digest of the page against the prompt (when a digest fn is wired). */\n digest?: string\n /** Page content as markdown (when no digest fn is wired, or digesting failed). */\n markdown?: string\n /** True when the markdown was cut at the size cap. */\n truncated?: boolean\n /** Redirect-to-a-different-host notice: the redirect is surfaced, not followed\n * (the agent can decide to fetch `redirectUrl` itself). */\n notice?: string\n redirectUrl?: string\n error?: string\n}\n\nexport type WebFetchFn = (url: string, prompt: string) => Promise<WebFetchResult>\n\n/** Runs the digest pass over the fetched markdown. Wire the session's own model\n * here (see createEngineSession) so its tokens land in the turn's usage. */\nexport type WebFetchDigest = (markdown: string, prompt: string) => Promise<string>\n\nexport type WebFetchOptions = {\n fetchImpl?: typeof fetch\n /** Raw-body cap, enforced while streaming (before any conversion). Default 1 MiB. */\n maxContentBytes?: number\n /** Markdown cap handed to the model. Default 50 KB. */\n maxMarkdownBytes?: number\n /** Fetched-page cache TTL (keyed by URL; the digest is per-prompt and never\n * cached). Default 15 minutes. */\n cacheTtlMs?: number\n /** Optional hostname allowlist on top of the SSRF guard (exact or `*.example.com`).\n * Unset = any public host. */\n allowedHosts?: string[]\n /** Per-request timeout. Default 30000. */\n timeoutMs?: number\n digest?: WebFetchDigest\n}\n\nconst MAX_CACHE_ENTRIES = 64\nconst MAX_REDIRECTS = 5\n\ntype CacheEntry = { expiresAt: number; page: WebFetchResult }\n\nexport function createWebFetch(options: WebFetchOptions = {}): WebFetchFn {\n const fetchImpl = options.fetchImpl ?? fetch\n const maxContentBytes = options.maxContentBytes ?? 1024 * 1024\n const maxMarkdownBytes = options.maxMarkdownBytes ?? 50 * 1024\n const cacheTtlMs = options.cacheTtlMs ?? 15 * 60 * 1000\n const cache = new Map<string, CacheEntry>()\n\n const fetchPage = async (rawUrl: string): Promise<WebFetchResult> => {\n const cached = cache.get(rawUrl)\n if (cached && cached.expiresAt > Date.now()) return cached.page\n\n let url = parseUrl(rawUrl)\n if (!url) return { url: rawUrl, error: 'only absolute http(s) URLs are supported' }\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 30_000)\n try {\n let response: Response\n for (let hop = 0; ; hop++) {\n const denied = await denyReason(url, options.allowedHosts)\n if (denied) return { url: url.href, error: denied }\n response = await fetchImpl(url.href, {\n redirect: 'manual',\n signal: controller.signal,\n })\n if (response.status < 300 || response.status >= 400) break\n const location = response.headers.get('location')\n if (!location) return { url: url.href, error: `redirect (${response.status}) without a location` }\n const target = parseUrl(new URL(location, url).href)\n if (!target) return { url: url.href, error: `redirect to unsupported URL: ${location}` }\n if (target.host !== url.host) {\n // Like the original: surface a cross-host redirect instead of silently\n // following it — the agent may fetch the new URL explicitly.\n return {\n url: url.href,\n redirectUrl: target.href,\n notice: `redirected to a different host (${target.host}); not followed automatically`,\n }\n }\n if (hop >= MAX_REDIRECTS) return { url: url.href, error: 'too many redirects' }\n url = target\n }\n if (!response.ok) {\n return { url: url.href, error: `request failed: ${response.status}` }\n }\n const declared = Number(response.headers.get('content-length') ?? '')\n if (declared > maxContentBytes) {\n return { url: url.href, error: `response too large (${declared} bytes)` }\n }\n const body = await readCapped(response, maxContentBytes)\n if (body === undefined) {\n return { url: url.href, error: `response too large (> ${maxContentBytes} bytes)` }\n }\n const contentType = response.headers.get('content-type') ?? ''\n const text =\n contentType.includes('html') || looksLikeHtml(body) ? htmlToMarkdown(body) : body\n const truncated = text.length > maxMarkdownBytes\n const page: WebFetchResult = {\n url: url.href,\n markdown: truncated ? text.slice(0, maxMarkdownBytes) : text,\n truncated: truncated || undefined,\n }\n if (cache.size >= MAX_CACHE_ENTRIES) {\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n cache.set(rawUrl, { expiresAt: Date.now() + cacheTtlMs, page })\n return page\n } catch (error) {\n const message = controller.signal.aborted\n ? 'request timed out'\n : error instanceof Error\n ? error.message\n : String(error)\n return { url: url.href, error: message }\n } finally {\n clearTimeout(timer)\n }\n }\n\n return async (rawUrl, prompt) => {\n const page = await fetchPage(rawUrl)\n if (page.error || page.notice || !options.digest || page.markdown === undefined) return page\n try {\n const digest = await options.digest(page.markdown, prompt)\n return { url: page.url, digest, truncated: page.truncated }\n } catch {\n // Digest is best-effort sugar over the fetch: fall back to the markdown.\n return page\n }\n }\n}\n\nfunction parseUrl(raw: string): URL | undefined {\n try {\n const url = new URL(raw)\n return url.protocol === 'https:' || url.protocol === 'http:' ? url : undefined\n } catch {\n return undefined\n }\n}\n\n/** SSRF guard: resolve the hostname and refuse private, loopback, and link-local\n * destinations. Checked per redirect hop. Resolution happens once here and again\n * inside fetch (a DNS-rebinding TOCTOU); this tier accepts that — operators who\n * need pinning can supply `fetchImpl` with a pinned agent. */\nasync function denyReason(url: URL, allowedHosts: string[] | undefined): Promise<string | null> {\n const host = url.hostname.toLowerCase()\n if (allowedHosts && allowedHosts.length > 0 && !hostMatches(host, allowedHosts)) {\n return `host not allowed: ${host}`\n }\n if (host === 'localhost' || host.endsWith('.localhost')) return `host not allowed: ${host}`\n const literal = host.replace(/^\\[|\\]$/g, '')\n if (isPrivateAddress(literal)) return `address not allowed: ${literal}`\n if (/^[\\d.]+$/.test(literal) || literal.includes(':')) return null // public literal IP\n let addresses: Array<{ address: string }>\n try {\n addresses = await lookup(literal, { all: true })\n } catch {\n return `cannot resolve host: ${host}`\n }\n for (const { address } of addresses) {\n if (isPrivateAddress(address)) return `host resolves to a private address: ${host}`\n }\n return null\n}\n\nfunction hostMatches(host: string, allowedHosts: string[]): boolean {\n return allowedHosts.some((entry) => {\n const pattern = entry.trim().toLowerCase()\n if (!pattern) return false\n if (pattern.startsWith('*.')) return host.endsWith(pattern.slice(1))\n return host === pattern\n })\n}\n\n/** Private / loopback / link-local / unspecified, IPv4 and IPv6 (incl. v4-mapped). */\nexport function isPrivateAddress(address: string): boolean {\n const ip = address.toLowerCase()\n if (ip.includes(':')) {\n if (ip === '::' || ip === '::1') return true\n const mapped = /^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/.exec(ip)\n if (mapped) return isPrivateAddress(mapped[1]!)\n return ip.startsWith('fc') || ip.startsWith('fd') || /^fe[89ab]/.test(ip)\n }\n const parts = ip.split('.').map(Number)\n if (parts.length !== 4 || parts.some((n) => Number.isNaN(n) || n < 0 || n > 255)) return false\n const [a, b] = parts as [number, number, number, number]\n if (a === 0 || a === 10 || a === 127) return true\n if (a === 100 && b! >= 64 && b! <= 127) return true // CGNAT\n if (a === 169 && b === 254) return true\n if (a === 172 && b! >= 16 && b! <= 31) return true\n if (a === 192 && b === 168) return true\n return a >= 224 // multicast + reserved\n}\n\nasync function readCapped(response: Response, maxBytes: number): Promise<string | undefined> {\n if (!response.body) {\n const text = await response.text()\n return text.length > maxBytes ? undefined : text\n }\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n let out = ''\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n out += decoder.decode(value, { stream: true })\n if (out.length > maxBytes) {\n await reader.cancel().catch(() => {})\n return undefined\n }\n }\n return out + decoder.decode()\n}\n\nfunction looksLikeHtml(body: string): boolean {\n return /<(!doctype|html|head|body)[\\s>]/i.test(body.slice(0, 1024))\n}\n\n/**\n * Dependency-free HTML → markdown, tuned for \"give the model readable text\":\n * drops non-content subtrees, keeps headings/lists/links/emphasis/code, strips\n * everything else. Not a spec-grade converter on purpose — a small predictable\n * transform beats dragging a DOM into core.\n */\nexport function htmlToMarkdown(html: string): string {\n let text = html\n .replace(/<!--[\\s\\S]*?-->/g, '')\n .replace(/<(script|style|noscript|svg|template|iframe)\\b[\\s\\S]*?<\\/\\1>/gi, '')\n .replace(/<(head)\\b[\\s\\S]*?<\\/\\1>/gi, '')\n text = text\n .replace(/<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi, (_, level: string, body: string) => {\n return `\\n\\n${'#'.repeat(Number(level))} ${stripTags(body).trim()}\\n\\n`\n })\n .replace(/<pre[^>]*>([\\s\\S]*?)<\\/pre>/gi, (_, body: string) => {\n return `\\n\\n\\`\\`\\`\\n${decodeEntities(body.replace(/<[^>]+>/g, ''))}\\n\\`\\`\\`\\n\\n`\n })\n .replace(/<a\\s[^>]*href=[\"']([^\"']+)[\"'][^>]*>([\\s\\S]*?)<\\/a>/gi, (_, href: string, body: string) => {\n const label = stripTags(body).trim()\n // Skip anchors/scripts and empty labels; keep the label when it IS the URL.\n if (!label || href.startsWith('#') || href.startsWith('javascript:')) return label\n return label === href ? label : `[${label}](${href})`\n })\n .replace(/<li[^>]*>/gi, '\\n- ')\n .replace(/<\\/(p|div|section|article|tr|table|ul|ol|blockquote|figure)>/gi, '\\n\\n')\n .replace(/<(br|hr)\\s*\\/?>/gi, '\\n')\n .replace(/<(strong|b)>([\\s\\S]*?)<\\/\\1>/gi, '**$2**')\n .replace(/<(em|i)>([\\s\\S]*?)<\\/\\1>/gi, '*$2*')\n .replace(/<code[^>]*>([\\s\\S]*?)<\\/code>/gi, '`$1`')\n text = decodeEntities(text.replace(/<[^>]+>/g, ''))\n return text\n .replace(/[ \\t]+\\n/g, '\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .replace(/[ \\t]{2,}/g, ' ')\n .trim()\n}\n\nfunction stripTags(html: string): string {\n return decodeEntities(html.replace(/<[^>]+>/g, ''))\n}\n\nfunction decodeEntities(text: string): string {\n return text\n .replace(/&#(\\d+);/g, (_, code: string) => String.fromCodePoint(Number(code)))\n .replace(/&#x([\\da-f]+);/gi, (_, code: string) => String.fromCodePoint(parseInt(code, 16)))\n .replace(/&nbsp;/g, ' ')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;|&apos;/g, \"'\")\n .replace(/&amp;/g, '&')\n}\n","import type { LanguageModel, ToolSet } from 'ai'\nimport type {\n McpServerConfigWire,\n McpServerStatusInfo,\n McpServerToolInfo,\n ProfileInfo,\n SessionCapability,\n} from '@workerdeck/protocol'\nimport { createVfs } from '@workerdeck/sandbox'\nimport { AiSdkRunner, type AiSdkRunnerConfig } from './runner.ts'\nimport {\n createToolContext,\n withHostTools,\n withMcpTools,\n type HostToolDefinition,\n type ToolContextOptions,\n} from './tools.ts'\nimport type { ToolExecutor } from '../../executors/tool-executor.ts'\nimport { createWebFetch, type WebFetchFn, type WebFetchOptions } from './web-fetch.ts'\n\nexport type EngineSessionOptions = {\n /** Resolved session config (profile defaults already applied). */\n config: AiSdkRunnerConfig\n /** The profile that selected this engine, when there was one. */\n profile?: ProfileInfo\n /**\n * Resolve the profile's provider config into a model instance. The host owns\n * this so core never imports a provider SDK and never reads credentials —\n * they come from the operator's environment, exactly like the Claude chain.\n */\n resolveModel: (profile: ProfileInfo | undefined, config: AiSdkRunnerConfig) => LanguageModel\n /**\n * Executor for sandboxed tools. Return the browser bridge when a client is\n * attached and the server sandbox otherwise; the seam makes them\n * interchangeable, so this is the only place the choice is made.\n */\n selectExecutor: () => ToolExecutor\n /** Which backend `selectExecutor` returned, for the execution_* events. */\n backend?: 'server' | 'browser' | 'managed' | 'remote'\n /** Backends for the granted capabilities. Omitted ones are simply not granted. */\n capabilities?: {\n search?: ToolContextOptions['search']\n download?: ToolContextOptions['download']\n /**\n * Grants `web_fetch`. Pass options (or `{}`) to use the built-in\n * {@link createWebFetch} backend — its digest pass then runs on the\n * session's own model, billed into the turn's usage. Pass `digest: false`\n * to skip the digest (the tool returns page markdown), a custom digest fn\n * to bring your own model, or a complete {@link WebFetchFn} to replace the\n * backend outright.\n */\n webFetch?: WebFetchFn | (Omit<WebFetchOptions, 'digest'> & { digest?: WebFetchOptions['digest'] | false })\n /** Grants `deliver_file`: the agent can hand VFS files over to the user\n * (emitting `file_delivered`, downloadable via the server's file routes).\n * Default true — set false to withhold it. */\n deliverFiles?: boolean\n }\n /**\n * A live MCP connection from {@link connectMcpTools} — the preferred way to\n * hand MCP to a session, and the only one that can fail loudly.\n *\n * With this set, the session knows *which servers connected*, so two things\n * that were previously silent become impossible: a profile naming a server\n * that never connected refuses to build (see {@link mcpTools} for what that\n * used to look like), and `runner.mcpServers()` answers `GET\n * /sessions/:id/mcp` with the real per-server status instead of 501.\n */\n mcp?: McpConnection\n /** Authoritative tools that run server-side with server credentials (MCP).\n * Never bridged to a client. Namespaced `<server>__<tool>` by\n * {@link connectMcpTools}, which is how a profile grants servers by name.\n *\n * The bare tool set, for a host assembling one itself. Prefer {@link mcp}:\n * a tool set alone cannot distinguish \"this server connected and exposes no\n * tools\" from \"this server never connected\", so the check here has to be the\n * cruder one — a declared server contributing no tools is refused. */\n mcpTools?: ToolSet\n /**\n * Extra host tools, each at an explicit trust level (see\n * {@link withHostTools}). This is the seam for a tool that is neither one of\n * the built-in capabilities nor MCP — including a **sandboxed** one, which\n * `mcpTools` cannot express because everything in it is authoritative by\n * construction.\n *\n * A sandboxed tool here rides the same {@link ToolExecutor} seam\n * `eval_script` does, so it executes wherever `selectExecutor` points — an\n * in-process QuickJS guest, or the browser tab that asked the question.\n */\n tools?: Record<string, HostToolDefinition>\n /** Extra instructions prepended to the session's system prompt. Overridden by\n * the profile's `session.instructions` when it declares one. */\n instructions?: string\n executionLimits?: { timeoutMs?: number; memoryLimitBytes?: number }\n /**\n * Initial scratch-filesystem contents for a **new** session, and the safe way\n * to seed one: it is ignored outright when `config.restore` is set, because a\n * rehydrated session brings back the files its parked turn already wrote and\n * seeding over them destroys exactly the work that was preserved.\n *\n * (Hand-building `config.vfs` still works and still wins — but then the\n * `restore ? undefined : createVfs(...)` dance is yours to get right.)\n */\n seedVfs?: Record<string, string>\n /**\n * Build the session under this id rather than minting one.\n *\n * Forward the server's `EngineRunnerContext.id` here, always: it is set when\n * the gateway is rehydrating a session across a restart, and a runner that\n * ignores it comes back as a *different* session — the rebuild is refused,\n * and every client's route and unread watermark is stranded. Ignored when\n * `config.restore` is present, which carries its own id.\n */\n id?: string\n}\n\n/** Which capability a wired backend yields, for grant filtering. */\nconst CAPABILITY_TOOLS = {\n search: 'web_search',\n download: 'download',\n webFetch: 'web_fetch',\n deliverFiles: 'deliver_file',\n} as const satisfies Record<string, SessionCapability>\n\n/**\n * Assemble a model-agnostic session: provider model, capability-scoped tools,\n * a scratch VFS, and the executor that runs the sandboxed ones.\n *\n * This is the piece an operator wires into the server's `createEngineRunner`.\n *\n * The host wires the *backends*; the profile and the session request decide which\n * of them are actually granted (`profile.session`, `config.capabilities`). A\n * backend that isn't granted is simply not built into the tool set, so withholding\n * a capability costs the host no branching. No declaration anywhere = everything\n * the host wired, which is what a host that ignores profiles gets.\n */\nexport function createEngineSession(options: EngineSessionOptions): AiSdkRunner {\n // A rehydrated session brings its scratch filesystem back with it — the\n // deliverables and working files the parked turn already produced. `seedVfs`\n // is for a *new* session only, which is the whole reason it exists here\n // rather than at each call site.\n const vfs =\n options.config.vfs ?? createVfs(options.config.restore ? options.config.restore.vfs : options.seedVfs)\n const executor = options.selectExecutor()\n // Narrowing only: the gateway has already refused a request naming a capability\n // its profile doesn't grant, so the request value wins when present.\n const granted = options.config.capabilities ?? options.profile?.session?.capabilities\n const isGranted = (key: keyof typeof CAPABILITY_TOOLS): boolean =>\n granted === undefined || granted.includes(CAPABILITY_TOOLS[key])\n // The runner doesn't exist yet while the tools are being built; these\n // capabilities reach back into it lazily (they only ever run mid-turn).\n let runner: AiSdkRunner | undefined\n const webFetchCap = isGranted('webFetch') ? options.capabilities?.webFetch : undefined\n const webFetch =\n typeof webFetchCap === 'function'\n ? webFetchCap\n : webFetchCap\n ? createWebFetch({\n ...webFetchCap,\n digest:\n webFetchCap.digest === false\n ? undefined\n : (webFetchCap.digest ??\n ((markdown, prompt) =>\n runner!.generateDigest(\n 'Answer the request below using ONLY this web page content.\\n\\n' +\n `<page>\\n${markdown}\\n</page>\\n\\nRequest: ${prompt}`,\n ))),\n })\n : undefined\n const base = createToolContext({\n executor,\n sessionId: 'pending',\n vfs,\n search: isGranted('search') ? options.capabilities?.search : undefined,\n download: isGranted('download') ? options.capabilities?.download : undefined,\n webFetch,\n onFileDelivered:\n options.capabilities?.deliverFiles === false || !isGranted('deliverFiles')\n ? undefined\n : (file) => runner?.emitFileDelivered(file),\n })\n const declaredServers = options.profile?.session?.mcpServers\n const connected = options.mcp?.tools ?? options.mcpTools\n requireDeclaredServers(options.profile?.name ?? '(unnamed)', declaredServers, options.mcp, connected)\n const mcpTools = selectMcpTools(connected, declaredServers)\n const withMcp = mcpTools ? withMcpTools(base, mcpTools) : base\n const context = options.tools ? withHostTools(withMcp, options.tools) : withMcp\n\n runner = new AiSdkRunner({\n ...options.config,\n languageModel: options.resolveModel(options.profile, options.config),\n instructions:\n options.profile?.session?.instructions ?? options.instructions ?? options.config.instructions,\n tools: context.tools,\n vfs,\n executor,\n executableTools: context.sandboxedToolNames,\n executionBackend: options.backend ?? 'server',\n executionLimits: options.executionLimits,\n // Only the servers this profile was granted: the /mcp screen must not\n // report a connection the session cannot actually reach.\n reportMcpServers: options.mcp\n ? () =>\n Promise.resolve(\n declaredServers === undefined\n ? options.mcp!.servers\n : options.mcp!.servers.filter((s) => declaredServers.includes(s.name)),\n )\n : undefined,\n }, options.id)\n return runner\n}\n\n/**\n * Refuse to build a session whose profile names an MCP server that isn't there.\n *\n * A profile's `mcpServers` list is a **declaration**, not a filter: an embedder\n * who wrote it meant the agent to have those tools. Honouring it partially is\n * the worst failure mode this engine has — the session starts, reports healthy,\n * and the agent apologises its way through every request that needed the server,\n * with one warning line in a log nobody is reading.\n *\n * With a {@link McpConnection} the check is exact (did this server connect?).\n * With a bare tool set all we can see is whether any tool carries the server's\n * namespace, so a genuinely tool-less server would trip it — the fix there is to\n * pass `mcp` rather than to weaken this.\n */\nfunction requireDeclaredServers(\n profileName: string,\n declared: string[] | undefined,\n mcp: McpConnection | undefined,\n tools: ToolSet | undefined,\n): void {\n if (!declared || declared.length === 0) return\n const missing = declared.filter((name) => {\n if (mcp) {\n const server = mcp.servers.find((s) => s.name === name)\n return !server || server.status !== 'connected'\n }\n return !Object.keys(tools ?? {}).some((tool) => tool.split('__')[0] === name)\n })\n if (missing.length === 0) return\n const reasons = missing\n .map((name) => {\n const error = mcp?.servers.find((s) => s.name === name)?.error\n return error ? `${name} (${error})` : name\n })\n .join(', ')\n throw new Error(\n `profile '${profileName}' declares MCP server(s) that are not connected: ${reasons}. ` +\n 'A session missing a declared server is a session whose agent silently cannot do its job.',\n )\n}\n\n/**\n * Restrict a connected tool set to the MCP servers a profile grants, by the\n * `<server>__<tool>` namespace {@link connectMcpTools} assigns. Undefined `servers`\n * = no declaration, so every connected server passes through.\n *\n * This is how one process-wide MCP connection serves a mixed fleet: the host\n * connects everything once, each profile grants a subset. The transport configs —\n * and any credentials in their headers — never leave the host for a profile.\n */\nfunction selectMcpTools(tools: ToolSet | undefined, servers: string[] | undefined): ToolSet | undefined {\n if (!tools || servers === undefined) return tools\n const allowed = new Set(servers)\n return Object.fromEntries(\n Object.entries(tools).filter(([name]) => allowed.has(name.split('__')[0]!)),\n )\n}\n\nexport type McpConnection = {\n tools: ToolSet\n /**\n * One entry per configured server, connected or not — the truth a session was\n * assembled against. Handed to {@link createEngineSession} as `mcp`, it is\n * what `GET /sessions/:id/mcp` answers with and what makes a half-connected\n * session refuse to build rather than run degraded.\n */\n servers: McpServerStatusInfo[]\n close: () => Promise<void>\n}\n\n/**\n * Connect to MCP servers and return their tools, ready for {@link withMcpTools}.\n *\n * Server-side only, with server credentials: these tools are authoritative and\n * must never be bridged to a browser. `@ai-sdk/mcp` is imported lazily and is an\n * optional dependency — an operator who wires no MCP servers never needs it.\n *\n * **A stateless MCP server must answer `GET` with 405.** The client opens the\n * SSE stream with a `GET` before it sends anything, and a POST-only server\n * mounted under a framework's default 404 makes the whole connect fail with an\n * error that names neither the method nor the route. This is the single most\n * common way an otherwise-correct MCP mount fails.\n */\nexport async function connectMcpTools(\n servers: Record<string, McpServerConfigWire>,\n options: {\n /** `onError` may fire more than once for a single server: transport-level\n * failures surface through the client's own uncaught-error channel as well as\n * the connect failure. Treat it as a report, not a count. */\n onError?: (name: string, error: unknown) => void\n /**\n * Reject if any server fails to connect, after closing the ones that did.\n *\n * Off by default, which is right for an operator's fleet — one unreachable\n * server should not take a whole gateway's sessions down. Turn it **on**\n * when the servers are the app's own: an embedder who mounts one wiki server\n * and gets a session without it has a session that cannot do its job, and\n * finding that out at connect time beats finding it out from a transcript\n * where the agent apologises.\n */\n required?: boolean\n } = {},\n): Promise<McpConnection> {\n const entries = Object.entries(servers)\n if (entries.length === 0) return { tools: {}, servers: [], close: async () => {} }\n\n const { createMCPClient } = await import('@ai-sdk/mcp')\n const clients: Array<{ close: () => Promise<void> }> = []\n const tools: ToolSet = {}\n const statuses: McpServerStatusInfo[] = []\n const closeAll = async (): Promise<void> => {\n await Promise.allSettled(clients.map((c) => c.close()))\n }\n\n for (const [name, server] of entries) {\n const identity = describeServer(server)\n try {\n const client = await createMCPClient({\n transport: toTransport(server),\n onUncaughtError: (error) => options.onError?.(name, error),\n })\n clients.push(client as unknown as { close: () => Promise<void> })\n const connected = await client.tools()\n // Namespaced so two servers exposing the same tool name cannot collide\n // (and so a tool's origin stays legible in the transcript).\n for (const [toolName, mcpTool] of Object.entries(connected)) {\n tools[`${name}__${toolName}`] = mcpTool as ToolSet[string]\n }\n statuses.push({\n name,\n status: 'connected',\n ...identity,\n // Unnamespaced here: this is the server's own view of itself, and the\n // `<server>__` prefix is this engine's routing detail.\n tools: Object.entries(connected).map(([toolName, mcpTool]) => toToolInfo(toolName, mcpTool)),\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n statuses.push({ name, status: 'failed', error: message, ...identity })\n options.onError?.(name, error)\n if (options.required) {\n // Nothing is half-open: the clients already connected are closed before\n // this leaves, or an embedder's failed create leaks a socket per attempt.\n await closeAll()\n throw new Error(`MCP server '${name}' failed to connect: ${message}`)\n }\n // Otherwise one unreachable server must not take down the session; the\n // agent simply does not get those tools.\n }\n }\n\n return { tools, servers: statuses, close: closeAll }\n}\n\n/** The connection's identity, minus its secrets — `headers` never travel. */\nfunction describeServer(\n server: McpServerConfigWire,\n): Pick<McpServerStatusInfo, 'transport' | 'url' | 'command' | 'args'> {\n if ('url' in server) return { transport: server.type === 'sse' ? 'sse' : 'http', url: server.url }\n return { transport: 'stdio', command: server.command, args: server.args }\n}\n\n/**\n * The AI SDK hands back its own `Tool`, whose `inputSchema` may be a zod schema\n * or a `jsonSchema()` wrapper. Only the latter carries a JSON Schema document,\n * so that is the only case where parameters are reported — `McpServerToolInfo`\n * models the absence deliberately, and inventing one here would be worse.\n */\nfunction toToolInfo(name: string, mcpTool: unknown): McpServerToolInfo {\n const { description, inputSchema } = (mcpTool ?? {}) as {\n description?: unknown\n inputSchema?: { jsonSchema?: unknown }\n }\n return {\n name,\n description: typeof description === 'string' ? description : undefined,\n inputSchema: inputSchema?.jsonSchema,\n }\n}\n\n/**\n * Only http/sse: the AI SDK's built-in transports are the remote ones, and its\n * own docs mark stdio local-only and not deployable. A stdio server here is a\n * misconfiguration worth surfacing rather than silently dropping — the Claude\n * engine still supports stdio, since the CLI spawns those itself.\n */\nfunction toTransport(server: McpServerConfigWire) {\n if (!('url' in server)) {\n throw new Error(\n 'stdio MCP servers are not supported by the model-agnostic engine (use an http or sse ' +\n 'server, or run this session under a Claude profile)',\n )\n }\n return server.type === 'sse'\n ? { type: 'sse' as const, url: server.url, headers: server.headers }\n : { type: 'http' as const, url: server.url, headers: server.headers }\n}\n","import type { ModelCatalog } from '../adapter.ts'\n\n/**\n * The Claude engine's model catalog — what a create form offers before any\n * session has run.\n *\n * **Refresh procedure** (release checklist): run `supportedModels()` on a\n * throwaway SDK query (no tokens spent) and re-apply the shaping rules of\n * `modelOptionsFromSdk` (`src/normalize.ts`) at authoring time: drop the\n * `default` sentinel row, derive display names from resolved ids where\n * unambiguous, mark the newest of each family `primary`, sort by family rank.\n * A unit test replays the raw extraction through `modelOptionsFromSdk` and\n * asserts these rows match, so the rules cannot drift.\n *\n * Two things the live `capabilities` event can never offer:\n * - rows for **older models** the CLI no longer reports (hand-maintained, the\n * accepted cost of a static catalog; the CLI silently downgrades an effort a\n * model doesn't support, so `reasoningEfforts` is omitted on them and the\n * engine default set applies);\n * - an answer on a **cold server**. The live event still exists and remains\n * the in-session truth for the model switcher; this catalog is the\n * create-form truth.\n *\n * `defaultModel` is deliberately NOT here: a claude profile's default is the\n * operator's CLI config, unknowable statically.\n */\nexport const CLAUDE_CATALOG: ModelCatalog = {\n provenance:\n 'supportedModels() of @anthropic-ai/claude-agent-sdk 0.3.221 (Claude Code CLI), ' +\n 'extracted 2026-08-05; older-model rows hand-maintained',\n models: [\n {\n value: 'claude-fable-5[1m]',\n resolvedModel: 'claude-fable-5',\n displayName: 'Fable 5',\n description: 'Fable 5 · Most capable for your hardest and longest-running tasks',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],\n },\n {\n value: 'opus[1m]',\n resolvedModel: 'claude-opus-5[1m]',\n displayName: 'Opus 5',\n description: 'Opus 5 with 1M context · Best for everyday, complex tasks',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],\n },\n // Older, still-servable ids the CLI no longer lists (\"more models\").\n {\n value: 'claude-opus-4-8',\n resolvedModel: 'claude-opus-4-8',\n displayName: 'Opus 4.8',\n description: 'Opus 4.8 · Previous Opus generation',\n },\n {\n value: 'sonnet',\n resolvedModel: 'claude-sonnet-5',\n displayName: 'Sonnet 5',\n description: 'Sonnet 5 · Efficient for routine tasks',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],\n },\n {\n value: 'claude-sonnet-4-6',\n resolvedModel: 'claude-sonnet-4-6',\n displayName: 'Sonnet 4.6',\n description: 'Sonnet 4.6 · Previous Sonnet generation',\n },\n {\n value: 'haiku',\n resolvedModel: 'claude-haiku-4-5-20251001',\n displayName: 'Haiku 4.5',\n description: 'Haiku 4.5 · Fastest for quick answers',\n primary: true,\n // Explicitly none: the CLI reports no effort support for Haiku 4.5, and\n // an absent field would wrongly imply the engine's default set.\n reasoningEfforts: [],\n },\n ],\n}\n","import { listSessions as sdkListSessions } from '@anthropic-ai/claude-agent-sdk'\nimport { ENGINE_CAPABILITIES } from '@workerdeck/protocol'\nimport { checkClaudeAuth } from './auth.ts'\nimport { SessionRunner } from './runner.ts'\nimport type { EngineAdapter } from '../adapter.ts'\nimport { CLAUDE_CATALOG } from './catalog.ts'\n\n/**\n * The Claude engine as an adapter — a thin, behaviourally inert wrapper:\n * `SessionRunner` unchanged, `checkClaudeAuth` as the probe, the static\n * catalog for create forms. Exists so catalogs, capabilities and availability\n * have one shape across engines; the runner itself is exactly what\n * `registry.prepare()` builds.\n */\nexport const claudeAdapter: EngineAdapter = {\n engine: 'claude',\n capabilities: ENGINE_CAPABILITIES.claude,\n catalog: CLAUDE_CATALOG,\n async checkAvailability(profile, env) {\n const status = await checkClaudeAuth(env)\n if (status === 'logged_in') return { available: true }\n if (status === 'logged_out') {\n return {\n available: false,\n reason:\n `no usable Claude credentials for this profile's environment — log in under its ` +\n `config dir (CLAUDE_CONFIG_DIR=${profile.configDir ?? '~/.claude'} claude auth login) ` +\n 'or set ANTHROPIC_API_KEY',\n }\n }\n return { available: 'unknown' }\n },\n createRunner({ config, restore, id }) {\n if (restore) throw new Error('the Claude engine cannot rebuild a parked session')\n return new SessionRunner(config, id)\n },\n /**\n * The Agent SDK's on-disk session store, mapped browser-safe. The SDK reads\n * the store of the *process* environment — it takes no config dir — so a\n * profile pin cannot narrow this listing; that matches the route's\n * pre-adapter behavior exactly (the listing was always process-global).\n */\n async listSessions({ dir, limit, offset }) {\n const sessions = await sdkListSessions({ dir, limit, offset })\n return sessions.map((s) => ({\n sessionId: s.sessionId,\n summary: s.summary,\n lastModified: s.lastModified,\n createdAt: s.createdAt,\n customTitle: s.customTitle,\n firstPrompt: s.firstPrompt,\n gitBranch: s.gitBranch,\n cwd: s.cwd,\n }))\n },\n}\n","import type { Readable, Writable } from 'node:stream'\n\n/**\n * A JSON-RPC error response from the peer, or one we return to it. `code`\n * follows the JSON-RPC 2.0 reserved ranges (-32601 = method not found).\n */\nexport class JsonRpcError extends Error {\n readonly code: number\n constructor(code: number, message: string) {\n super(message)\n this.name = 'JsonRpcError'\n this.code = code\n }\n}\n\ntype Pending = {\n method: string\n resolve: (result: unknown) => void\n reject: (error: Error) => void\n}\n\n/**\n * JSON-RPC over a `codex app-server` child's stdio: **newline-delimited JSON**,\n * one message per line, and — verified against 0.146.0 — an envelope *without*\n * the `jsonrpc: \"2.0\"` field (`{id, method, params}` / `{id, result}` /\n * `{id, error}`; the binary's own schema marks only those required). Server→\n * client notifications additionally carry a top-level `emittedAtMs`, ignored\n * here.\n *\n * Transport only: no method knowledge, no process ownership. The process\n * wrapper (`process.ts`) owns the child and calls {@link fail} when it dies so\n * every in-flight request rejects instead of hanging.\n */\nexport class JsonRpcStdioConnection {\n #output: Writable\n #nextId = 1\n #pending = new Map<number, Pending>()\n #buffer = ''\n #closed = false\n #notificationHandler: ((method: string, params: unknown) => void) | undefined\n #requestHandler:\n | ((method: string, params: unknown, id: string | number) => Promise<unknown>)\n | undefined\n\n constructor(options: { input: Readable; output: Writable }) {\n this.#output = options.output\n options.input.on('data', (chunk: Buffer | string) => this.#feed(String(chunk)))\n // Stream errors surface via the process wrapper's exit handling; swallowing\n // here just prevents an unhandled 'error' crash between the two.\n options.input.on('error', () => {})\n options.output.on('error', () => {})\n }\n\n request(method: string, params?: unknown): Promise<unknown> {\n if (this.#closed) return Promise.reject(new Error(`codex app-server is closed (${method})`))\n const id = this.#nextId++\n return new Promise((resolve, reject) => {\n this.#pending.set(id, { method, resolve, reject })\n this.#write({ id, method, ...(params === undefined ? {} : { params }) })\n })\n }\n\n notify(method: string, params?: unknown): void {\n if (this.#closed) return\n this.#write({ method, ...(params === undefined ? {} : { params }) })\n }\n\n onNotification(handler: (method: string, params: unknown) => void): void {\n this.#notificationHandler = handler\n }\n\n onRequest(handler: (method: string, params: unknown, id: string | number) => Promise<unknown>): void {\n this.#requestHandler = handler\n }\n\n /** Reject everything in flight and refuse new traffic — the child is gone\n * (or the session is over). Idempotent. */\n fail(message: string): void {\n if (this.#closed) return\n this.#closed = true\n const pending = [...this.#pending.values()]\n this.#pending.clear()\n for (const entry of pending) {\n entry.reject(new Error(`${message} (awaiting ${entry.method})`))\n }\n }\n\n #write(payload: object): void {\n try {\n this.#output.write(JSON.stringify(payload) + '\\n')\n } catch {\n // A broken pipe races the exit event; the wrapper's fail() explains it.\n }\n }\n\n #feed(chunk: string): void {\n this.#buffer += chunk\n let newline: number\n while ((newline = this.#buffer.indexOf('\\n')) >= 0) {\n const line = this.#buffer.slice(0, newline).trim()\n this.#buffer = this.#buffer.slice(newline + 1)\n if (!line) continue\n let message: Record<string, unknown>\n try {\n message = JSON.parse(line) as Record<string, unknown>\n } catch {\n continue // never let one garbled line kill the session\n }\n this.#dispatch(message)\n }\n }\n\n #dispatch(message: Record<string, unknown>): void {\n const { id, method } = message\n if (typeof method === 'string') {\n if (id === undefined || id === null) {\n this.#notificationHandler?.(method, message.params)\n return\n }\n // Server→client request: the handler's resolution is the response. No\n // handler (or a throw) becomes a JSON-RPC error, never a hang — an\n // unanswered approval would wedge the turn.\n const respond = (payload: object) => this.#write({ id: id as string | number, ...payload })\n const handler = this.#requestHandler\n if (!handler) {\n respond({ error: { code: -32601, message: `no handler for server request '${method}'` } })\n return\n }\n handler(method, message.params, id as string | number).then(\n (result) => respond({ result: result ?? {} }),\n (error: unknown) =>\n respond({\n error: {\n code: error instanceof JsonRpcError ? error.code : -32603,\n message: error instanceof Error ? error.message : String(error),\n },\n }),\n )\n return\n }\n if (id === undefined || id === null) return\n const pending = this.#pending.get(id as number)\n if (!pending) return\n this.#pending.delete(id as number)\n if (message.error !== undefined && message.error !== null) {\n const error = message.error as { code?: number; message?: string }\n pending.reject(\n new JsonRpcError(error.code ?? -32603, error.message ?? `request '${pending.method}' failed`),\n )\n return\n }\n pending.resolve(message.result)\n }\n}\n","import { createHash, randomUUID } from 'node:crypto'\nimport { mkdirSync, rmSync, statSync, writeFileSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport {\n ENGINE_CAPABILITIES,\n PROTOCOL_VERSION,\n transcriptActivity,\n type ContentBlock,\n type CreateSessionRequest,\n type FilePatch,\n type McpServerStatusInfo,\n type PermissionDecisionSource,\n type PermissionMode,\n type PermissionRequest,\n type SessionEvent,\n type SessionEventBody,\n type SessionInfo,\n type SessionStatus,\n type SkillInfo,\n type UserQuestion,\n} from '@workerdeck/protocol'\nimport {\n attachmentKind,\n attachmentRef,\n normalizeMediaType,\n type AttachmentInput,\n} from '../../lib/attachments.ts'\nimport { parseUnifiedDiff } from '../../lib/patch.ts'\nimport type { PermissionDecision, Runner, SessionEventListener } from '../../runner-interface.ts'\nimport { SubscriberSet, type SubscribeOptions } from '../../lib/subscribers.ts'\nimport { JsonRpcError } from './jsonrpc.ts'\nimport type {\n AppServerCommandApprovalParams,\n AppServerConnection,\n AppServerConnectFn,\n AppServerElicitationParams,\n AppServerFileChangeApprovalParams,\n AppServerHistoryTurn,\n AppServerImageGenerationItem,\n AppServerItem,\n AppServerMcpServerStatus,\n AppServerMcpServerStatusResponse,\n AppServerMcpStatusUpdate,\n AppServerPermissionsApprovalParams,\n AppServerPlanUpdate,\n AppServerRateLimits,\n AppServerSkillMetadata,\n AppServerSkillsListResponse,\n AppServerTokenUsage,\n AppServerTokenUsageUpdate,\n AppServerTurn,\n AppServerUnknownItem,\n AppServerUserInput,\n AppServerUserInputParams,\n AppServerUserInputQuestion,\n AppServerUserMessageItem,\n} from './types.ts'\n\n/**\n * thread/start's sandbox axis (string form) — our permission modes as codex\n * sandbox modes: `default` → read-only (reads run; any mutation is refused by\n * the OS sandbox and — with the ask policy below — escalates to a real\n * question), `acceptEdits` → workspace-write (in-workspace writes sail\n * through, the acceptEdits grant), `bypassPermissions` → danger-full-access.\n */\nconst THREAD_SANDBOX_BY_MODE: Partial<Record<PermissionMode, string>> = {\n default: 'read-only',\n acceptEdits: 'workspace-write',\n bypassPermissions: 'danger-full-access',\n}\n\n/** turn/start's sandboxPolicy axis (object form — same policy, second shape). */\nconst TURN_SANDBOX_BY_MODE: Partial<Record<PermissionMode, { type: string }>> = {\n default: { type: 'readOnly' },\n acceptEdits: { type: 'workspaceWrite' },\n bypassPermissions: { type: 'dangerFullAccess' },\n}\n\n/**\n * The approval axis, stated as the GRANULAR object on both thread/start and\n * turn/start — never the string vocabulary, deliberately and unconditionally:\n * measured against 0.146.0, plain `'untrusted'` never asked anything (a\n * sandbox-violating write was silently refused, a safe echo auto-approved),\n * while the granular flags make a blocked action a real server→client\n * question. Granular policies are gated on `capabilities.experimentalApi` at\n * initialize; WorkerDeck declares it always and keeps NO non-experimental\n * fallback — a future binary that rejects either gate fails loudly (see\n * {@link CodexRunner.#ensureThread}) instead of quietly not asking.\n *\n * `default`/`acceptEdits` ask (all flags on — the sandbox axis above already\n * decides *what needs asking*); `bypassPermissions` asks nothing, same shape.\n */\nconst GRANULAR_ASK = {\n granular: {\n sandbox_approval: true,\n rules: true,\n mcp_elicitations: true,\n request_permissions: true,\n skill_approval: true,\n },\n}\nconst GRANULAR_NEVER = {\n granular: {\n sandbox_approval: false,\n rules: false,\n mcp_elicitations: false,\n request_permissions: false,\n skill_approval: false,\n },\n}\nconst APPROVAL_POLICY_BY_MODE: Partial<Record<PermissionMode, object>> = {\n default: GRANULAR_ASK,\n acceptEdits: GRANULAR_ASK,\n bypassPermissions: GRANULAR_NEVER,\n}\n\n/** Fallback timeout for a pending approval nobody answers — the SessionRunner\n * default, so unattended codex sessions land the same way Claude ones do. */\nconst DEFAULT_APPROVAL_TIMEOUT_MS = 300_000\n\n/**\n * Tool name for codex's built-in `image_gen`. A stable string because it is a\n * rendering contract: both clients key an icon (and, where they can reach the\n * host filesystem, an inline preview) off it.\n */\nexport const CODEX_IMAGE_TOOL = 'CodexImageGeneration'\n\n/** Longest `result` worth putting in a tool card. The field is free-form and\n * undocumented; anything past this is assumed to be an encoded image rather\n * than a sentence, and encoded images do not go in the event log. */\nconst MAX_IMAGE_RESULT_CHARS = 512\n\nconst shortResult = (result: string): boolean =>\n result.length > 0 && result.length <= MAX_IMAGE_RESULT_CHARS && !result.startsWith('data:')\n\n/**\n * `file_produced.fileId` — derived from the path, not minted fresh.\n *\n * Two properties fall out of that and both are load-bearing: codex reports the\n * same `savedPath` on the progress item and again on the completed one, so a\n * derived id makes the second emission a no-op instead of a duplicate row; and\n * a session rebuilt from a snapshot re-derives the same ids, so a client's\n * cached URL still resolves after a park/restore.\n */\nfunction producedFileId(path: string): string {\n return createHash('sha256').update(path).digest('hex').slice(0, 32)\n}\n\n/** Media type from the extension, for the handful a client renders inline.\n * Undefined for everything else — the route sniffs, and guessing here is how a\n * text file ends up labelled `image/png`. */\nfunction producedMediaType(path: string): string | undefined {\n const extension = path.slice(path.lastIndexOf('.') + 1).toLowerCase()\n return PRODUCED_MEDIA_TYPES[extension]\n}\n\nconst PRODUCED_MEDIA_TYPES: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n pdf: 'application/pdf',\n}\n\n/**\n * Codex's `SkillMetadata` as the protocol states it. `interface.shortDescription`\n * beats the legacy top-level one (codex's own comment says to prefer it), and\n * `enabled` defaults to true — an entry codex listed without the field is one it\n * considers live, and defaulting to false would hide working skills.\n */\nfunction skillInfo(skill: AppServerSkillMetadata): SkillInfo {\n return {\n name: skill.name,\n ...(skill.description ? { description: skill.description } : {}),\n ...(skill.interface?.shortDescription ?? skill.shortDescription\n ? { shortDescription: skill.interface?.shortDescription ?? skill.shortDescription }\n : {}),\n ...(skill.interface?.displayName ? { displayName: skill.interface.displayName } : {}),\n ...(skill.interface?.defaultPrompt ? { defaultPrompt: skill.interface.defaultPrompt } : {}),\n ...(skill.scope ? { scope: skill.scope } : {}),\n enabled: skill.enabled !== false,\n }\n}\n\n/**\n * Codex's MCP status → the protocol's, which is Claude Code's vocabulary\n * ('connected' | 'failed' | 'needs-auth' | 'pending' | 'disabled').\n *\n * Two inputs, and the auth one wins where it applies: a server that started\n * fine but has no credential is *needs-auth*, not connected, because that is\n * the thing the operator has to act on. `notLoggedIn` is the only auth value\n * that means \"unusable\" — `unsupported` is the normal answer for a stdio server\n * that has no auth concept at all.\n *\n * A server with no startup notification yet is 'pending', not 'connected':\n * `mcpServerStatus/list` alone only proves it is *configured*.\n */\nfunction mcpStatusOf(\n authStatus: string | undefined,\n update: { status: string; failureReason?: string } | undefined,\n hasTools: boolean,\n): string {\n if (update?.status === 'failed') {\n return update.failureReason === 'reauthenticationRequired' ? 'needs-auth' : 'failed'\n }\n // codex's 'cancelled' has no Claude equivalent; it means the startup was\n // abandoned, which for a reader is the same actionable state as failed.\n if (update?.status === 'cancelled') return 'failed'\n if (authStatus === 'notLoggedIn') return 'needs-auth'\n if (update?.status === 'ready') return 'connected'\n // **Tools imply connected, and this branch is not a nicety.** The startup\n // notifications only fire for servers that come up *while we are attached*;\n // a session whose child already had its servers running receives none at all\n // (measured against the real binary — a working server with three tools and\n // no notification). Tools can only have been enumerated over a completed\n // handshake, so their presence is direct evidence the server is up, and\n // without this a healthy server would read as 'pending' forever.\n if (hasTools) return 'connected'\n // No notification and nothing exposed. Genuinely ambiguous: not started yet,\n // or switched off in config — and `mcpServerStatus/list` cannot tell the two\n // apart (it lists disabled servers too, also toolless). 'pending' is the\n // honest one of the two; claiming 'disabled' would be a guess.\n return 'pending'\n}\n\n/** One `mcpServerStatus/list` entry as the protocol states it. */\nfunction mcpServerInfo(\n server: AppServerMcpServerStatus,\n update: { status: string; error?: string; failureReason?: string } | undefined,\n): McpServerStatusInfo {\n // A map keyed by tool name, not an array — and the key is authoritative when\n // the value omits its own `name`.\n const tools = Object.entries(server.tools ?? {}).flatMap(([key, tool]) => {\n if (!tool) return []\n const annotations = tool.annotations\n return [\n {\n name: tool.name ?? key,\n ...(tool.description ? { description: tool.description } : {}),\n ...(tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {}),\n ...(annotations\n ? {\n annotations: {\n ...(annotations.readOnlyHint != null ? { readOnly: annotations.readOnlyHint } : {}),\n ...(annotations.destructiveHint != null\n ? { destructive: annotations.destructiveHint }\n : {}),\n ...(annotations.openWorldHint != null\n ? { openWorld: annotations.openWorldHint }\n : {}),\n },\n }\n : {}),\n },\n ]\n })\n return {\n name: server.name,\n status: mcpStatusOf(server.authStatus ?? undefined, update, tools.length > 0),\n ...(update?.error ? { error: update.error } : {}),\n ...(server.serverInfo?.name\n ? { serverInfo: { name: server.serverInfo.name, version: server.serverInfo.version ?? '' } }\n : {}),\n // Deliberately no `transport`/`command`/`args`/`url`: the list response\n // carries none of them. Inventing a transport from the server's name would\n // be a guess rendered as a fact, and the panel already omits what is absent.\n ...(tools.length > 0 ? { tools } : {}),\n }\n}\n\n/** What the card shows while the picture is being made, and after. `savedPath`\n * only exists once it lands — a client keys its preview off it, so it is a\n * field rather than a sentence in the result text. */\nfunction imageGenerationInput(item: AppServerImageGenerationItem): Record<string, unknown> {\n return {\n ...(item.revisedPrompt ? { prompt: item.revisedPrompt } : {}),\n ...(item.savedPath ? { savedPath: item.savedPath } : {}),\n }\n}\n\n/**\n * The experimental per-request decision list, normalized to names: a string\n * entry is its own name, a structured entry (`{acceptWithExecpolicyAmendment:\n * …}`) is named by its key. Undefined = the request stated no list and the\n * channel's schema enum applies. Present only under `experimentalApi: true` —\n * which WorkerDeck always declares.\n */\nfunction offeredDecisions(params: unknown): Set<string> | undefined {\n const raw = (params as { availableDecisions?: unknown })?.availableDecisions\n if (!Array.isArray(raw)) return undefined\n const names = new Set<string>()\n for (const entry of raw) {\n if (typeof entry === 'string') names.add(entry)\n else if (entry && typeof entry === 'object') {\n for (const key of Object.keys(entry)) names.add(key)\n }\n }\n return names.size > 0 ? names : undefined\n}\n\n/**\n * Decision picking for the `{decision: …}` channels (commandExecution,\n * fileChange), honoring the request's own `availableDecisions`:\n *\n * - allow → 'accept' when offered (or when no list was stated). A request\n * offering only the broader accepts ('acceptForSession',\n * 'acceptWithExecpolicyAmendment') yields undefined: a one-shot allow must\n * not be silently widened into a session-wide or persistent policy grant, so\n * the caller answers with the denial and says why.\n * - deny → 'decline', always: the response schema declares it unconditionally,\n * and it was verified live against 0.146.0 answering a request whose\n * availableDecisions omitted it — the turn completed cleanly. The list's job\n * is to gate the accept variants, not to take \"no, but keep going\" away\n * (its own alternative, 'cancel', would interrupt the whole turn).\n * - deny+interrupt → 'cancel' (codex's deny-and-interrupt) when offered;\n * otherwise 'decline', and the caller interrupts the turn itself.\n */\nfunction pickDecision(\n behavior: 'allow' | 'deny',\n interrupt: boolean,\n offered: Set<string> | undefined,\n): string | undefined {\n const has = (name: string) => !offered || offered.has(name)\n if (behavior === 'allow') return has('accept') ? 'accept' : undefined\n if (interrupt && has('cancel')) return 'cancel'\n return 'decline'\n}\n\n/** Codex `requestUserInput` questions in the AskUserQuestion wire shape both\n * clients already render (QuestionPrompt / QuestionPromptView). */\nfunction userQuestionsFromCodex(questions: readonly AppServerUserInputQuestion[]): UserQuestion[] {\n return questions.map((question) => ({\n question: question.question,\n header: question.header ?? '',\n options: (question.options ?? []).map((option) => ({\n label: option.label,\n description: option.description,\n })),\n }))\n}\n\n/**\n * The text of a history `userMessage` item: its content entries' text parts\n * joined.\n *\n * Image parts have no replayable representation — the bytes went to the model,\n * not into the rollout we can render from — so they are named rather than\n * dropped. A prompt that was *only* an image used to produce an empty string,\n * which the caller read as \"nothing to replay\" and skipped: the turn lost its\n * user row and, with it, the prompt mark the scrubber navigates by, so a resumed\n * thread had answers with no visible question. A word in place of the picture is\n * a smaller lie than a turn that never happened.\n */\nfunction historyUserText(item: AppServerUserMessageItem): string {\n if (!Array.isArray(item.content)) return ''\n let images = 0\n const text = item.content\n .map((part) => {\n const candidate = part as { type?: string; text?: unknown } | null\n if (candidate?.type === 'text' && typeof candidate.text === 'string') return candidate.text\n // By name, because the part vocabulary is codex's and open ('image',\n // 'localImage', …). Anything else unnamed stays unrepresented rather than\n // counted as a picture it may not be.\n if (typeof candidate?.type === 'string' && candidate.type.toLowerCase().includes('image')) {\n images += 1\n }\n return ''\n })\n .filter(Boolean)\n .join('\\n')\n if (text) return text\n return images > 0 ? `[${images === 1 ? 'image' : `${images} images`}]` : ''\n}\n\n/** The AskUserQuestion answer convention (question text → chosen label(s),\n * comma-joined) mapped back to codex's id-keyed shape. Questions the client\n * did not answer are absent, not empty. */\nfunction codexAnswers(\n questions: readonly AppServerUserInputQuestion[],\n answers: Record<string, unknown> | undefined,\n): Record<string, { answers: string[] }> {\n const out: Record<string, { answers: string[] }> = {}\n for (const question of questions) {\n const value = answers?.[question.question] ?? answers?.[question.id]\n if (typeof value === 'string' && value.length > 0) out[question.id] = { answers: [value] }\n }\n return out\n}\n\ntype ApprovalSurface = Pick<\n PermissionRequest,\n 'toolName' | 'input' | 'title' | 'displayName' | 'description' | 'decisionReason'\n>\n\n/**\n * One server→client ask channel: how it surfaces as a {@link PermissionRequest}\n * and what its wire responses are. `allow` may return undefined — the request\n * offered no plain accept — in which case the caller answers with `deny` and\n * says so. `decision` names the wire decision when the channel has one, so the\n * caller knows whether a deny+interrupt still needs an explicit\n * `turn/interrupt` ('cancel' carries the interrupt itself).\n */\ntype ApprovalChannel = {\n describe(params: unknown): ApprovalSurface\n itemId(params: unknown): string | undefined\n allow(\n params: unknown,\n updatedInput: Record<string, unknown> | undefined,\n offered: Set<string> | undefined,\n ): { response: unknown; decision?: string } | undefined\n deny(\n params: unknown,\n interrupt: boolean,\n offered: Set<string> | undefined,\n ): { response: unknown; decision?: string }\n}\n\n/** The two channels whose response is `{decision: …}` share their pick logic. */\nfunction decisionChannel(\n describe: (params: unknown) => ApprovalSurface,\n itemId: (params: unknown) => string | undefined,\n): ApprovalChannel {\n return {\n describe,\n itemId,\n allow: (_params, _updatedInput, offered) => {\n const decision = pickDecision('allow', false, offered)\n return decision ? { response: { decision }, decision } : undefined\n },\n deny: (_params, interrupt, offered) => {\n const decision = pickDecision('deny', interrupt, offered)!\n return { response: { decision }, decision }\n },\n }\n}\n\n/**\n * The ask channels, wired to the permission surface. Anything not listed here\n * still gets a JSON-RPC -32601 — never a hang (an unanswered server request\n * wedges the turn).\n */\nconst APPROVAL_CHANNELS: Record<string, ApprovalChannel> = {\n 'item/commandExecution/requestApproval': decisionChannel(\n (raw) => {\n const params = raw as AppServerCommandApprovalParams\n const command = params.command ?? undefined\n return {\n toolName: 'CodexCommand',\n input: {\n ...(command !== undefined ? { command } : {}),\n ...(params.cwd ? { cwd: params.cwd } : {}),\n ...(params.reason ? { reason: params.reason } : {}),\n },\n // Codex's own sentence is the truth of what is being asked: for a\n // sandbox escalation it reads \"command failed; retry without sandbox?\"\n // — an after-the-refusal question, NOT a pre-execution gate — and the\n // clients render `title` verbatim, so the tense stays honest.\n title:\n params.reason ??\n (command ? `Codex wants to run: ${command}` : 'Codex wants to run a command'),\n displayName: 'Run command',\n description: params.reason && command ? command : (params.cwd ?? undefined),\n decisionReason: params.reason ?? undefined,\n }\n },\n (raw) => (raw as AppServerCommandApprovalParams).itemId,\n ),\n 'item/fileChange/requestApproval': decisionChannel(\n (raw) => {\n const params = raw as AppServerFileChangeApprovalParams\n return {\n toolName: 'CodexFileChange',\n input: {\n ...(params.grantRoot ? { grantRoot: params.grantRoot } : {}),\n ...(params.reason ? { reason: params.reason } : {}),\n },\n title: params.reason ?? 'Codex wants to apply file changes',\n displayName: 'Apply file changes',\n description: params.grantRoot ? `write access under ${params.grantRoot}` : undefined,\n decisionReason: params.reason ?? undefined,\n }\n },\n (raw) => (raw as AppServerFileChangeApprovalParams).itemId,\n ),\n 'item/permissions/requestApproval': {\n describe: (raw) => {\n const params = raw as AppServerPermissionsApprovalParams\n return {\n toolName: 'CodexPermissions',\n input: {\n ...(params.permissions ? { permissions: params.permissions } : {}),\n ...(params.cwd ? { cwd: params.cwd } : {}),\n ...(params.reason ? { reason: params.reason } : {}),\n },\n title: params.reason ?? 'Codex requests additional permissions',\n displayName: 'Grant permissions',\n description: undefined,\n decisionReason: params.reason ?? undefined,\n }\n },\n itemId: (raw) => (raw as AppServerPermissionsApprovalParams).itemId,\n // Allow grants exactly what was asked (or the client's narrowed rewrite via\n // `updatedInput.permissions`), scoped to the turn — the response's default\n // scope, never 'session'.\n allow: (raw, updatedInput) => ({\n response: {\n permissions:\n (updatedInput?.permissions as Record<string, unknown> | undefined) ??\n (raw as AppServerPermissionsApprovalParams).permissions ??\n {},\n },\n }),\n // This channel's \"no\" is an empty grant.\n deny: () => ({ response: { permissions: {} } }),\n },\n 'item/tool/requestUserInput': {\n describe: (raw) => ({\n toolName: 'AskUserQuestion',\n input: {\n questions: userQuestionsFromCodex((raw as AppServerUserInputParams).questions ?? []),\n },\n title: 'Codex asks a question',\n displayName: 'Answer questions',\n description: undefined,\n decisionReason: undefined,\n }),\n itemId: (raw) => (raw as AppServerUserInputParams).itemId,\n allow: (raw, updatedInput) => ({\n response: {\n answers: codexAnswers(\n (raw as AppServerUserInputParams).questions ?? [],\n updatedInput?.answers as Record<string, unknown> | undefined,\n ),\n },\n }),\n deny: () => ({ response: { answers: {} } }),\n },\n 'mcpServer/elicitation/request': {\n describe: (raw) => {\n const params = raw as AppServerElicitationParams\n return {\n toolName: 'CodexMcpElicitation',\n input: {\n ...(params.serverName ? { serverName: params.serverName } : {}),\n ...(params.message ? { message: params.message } : {}),\n ...(params.mode ? { mode: params.mode } : {}),\n ...(params.requestedSchema !== undefined\n ? { requestedSchema: params.requestedSchema }\n : {}),\n ...(params.url ? { url: params.url } : {}),\n },\n title: params.serverName\n ? `MCP server '${params.serverName}' requests input`\n : 'An MCP server requests input',\n displayName: 'MCP elicitation',\n description: params.message ?? undefined,\n decisionReason: undefined,\n }\n },\n itemId: () => undefined,\n // An allow's `updatedInput` IS the elicitation content (the filled form);\n // content is nullable in the schema, so an allow without one is an accept\n // with no content and the MCP server judges it.\n allow: (_raw, updatedInput) => ({\n response: {\n action: 'accept',\n ...(updatedInput !== undefined ? { content: updatedInput } : {}),\n },\n }),\n // 'cancel' here cancels the ELICITATION, not the codex turn — no\n // `decision` is reported, so a deny+interrupt still interrupts the turn\n // explicitly.\n deny: (_raw, interrupt) => ({ response: { action: interrupt ? 'cancel' : 'decline' } }),\n },\n}\n\n/** One pending server→client approval: the surfaced request, the channel that\n * knows its wire vocabulary, and the resolver that answers the JSON-RPC\n * request when a decision lands. */\ntype PendingCodexApproval = {\n request: PermissionRequest\n channel: ApprovalChannel\n params: unknown\n offered: Set<string> | undefined\n /** JSON-RPC wire id — `serverRequest/resolved` names it when codex settles\n * the request itself. */\n wireId: string | number | undefined\n timer: ReturnType<typeof setTimeout>\n respond: (response: unknown) => void\n}\n\nexport type CodexRunnerConfig = CreateSessionRequest & {\n /** The injectable connection factory. The codex adapter passes\n * `connectAppServer` under the resolved binary; unit tests pass a scripted\n * peer. Required — this class never spawns anything itself. */\n connectFn: AppServerConnectFn\n /** Base environment for the codex child. Defaults to process.env. Passed to\n * spawn **complete** — a child env replaces, never merges. */\n env?: Record<string, string | undefined>\n /** CODEX_HOME pin from the profile (auth, config.toml, thread storage). */\n codexHome?: string\n /** Timeout for pending approvals when the request itself doesn't set one.\n * Default 300000 — the SessionRunner default. */\n defaultApprovalTimeoutMs?: number\n /** With `resume`: replay the thread's prior turns as `replay: true` events\n * before anything else, so late-attaching clients get a full transcript —\n * the SessionRunner option, same name, same default (true). */\n backfillHistory?: boolean\n}\n\n/** One queued user message: the input for exactly one turn. */\ntype QueuedTurn = { input: AppServerUserInput[] }\n\n/**\n * Name a subscription window by its measured length, so codex's positional\n * windows land in the protocol's named vocabulary. The two names clients\n * already understand are exact matches for codex's durations (300 min = 5h,\n * 10080 min = 7d); anything else keeps a self-describing key rather than\n * borrowing a name that would size it wrongly.\n */\nfunction rateLimitWindowName(minutes: number | null | undefined): string | undefined {\n if (typeof minutes !== 'number' || !Number.isFinite(minutes) || minutes <= 0) return undefined\n if (minutes === 300) return 'five_hour'\n if (minutes === 10_080) return 'seven_day'\n return `window_${minutes}m`\n}\n\n/** Everything one in-flight turn accumulates between `turn/start` and its\n * terminal `turn/completed`. */\ntype ActiveTurn = {\n /** Per-turn namespace for item-derived ids, kept unconditionally (the retired\n * exec transport's id-collision bug, b026e70): app-server item-id uniqueness\n * across turns (and across a respawned child) is not something we rely on. */\n nonce: string\n turnId?: string\n interrupted: boolean\n finalText?: string\n /** Last `error` notification, explaining a turn that fails without a message. */\n lastError?: string\n usage: AppServerTokenUsage\n sawUsage: boolean\n /** Context occupancy from the most recent model request, with the window it\n * was measured against. NOT `total` — see {@link CodexRunner.emitContextUsage}. */\n contextTokens?: number\n contextWindow?: number\n toolUseEmitted: Set<string>\n /** Last seen reasoning section index per item+kind, for '\\n\\n' separators. */\n sectionIndex: Map<string, number>\n settled: boolean\n resolve: (outcome: AppServerTurn) => void\n reject: (error: Error) => void\n}\n\n/**\n * The Codex engine, over the binary's `app-server` JSON-RPC surface: ONE\n * `codex app-server` child per *session* (spawned lazily, held across turns),\n * streaming `item/agentMessage/delta` and the reasoning deltas token-by-token\n * (`streaming: 'token'`). Follows `SessionRunner`'s event-log/seq/status\n * discipline with `AiSdkRunner`'s turn-chain (one turn at a time; sendMessage\n * queues). The first codex transport was `codex exec --experimental-json` (one\n * child per turn) — retired because its JSONL carries no partial messages, so\n * a turn could never stream.\n *\n * A dead child is a failed *turn*, not a failed session: the thread persists\n * on disk, the connection is dropped, and the next message spawns a fresh\n * child that `thread/resume`s the same thread id.\n */\nexport class CodexRunner implements Runner {\n readonly id: string\n readonly createdAt: number\n\n #config: CodexRunnerConfig\n /** {@link CodexRunnerConfig.cwd}, checked once in the constructor. */\n readonly #cwd: string\n #events: SessionEvent[] = []\n #subscribers = new SubscriberSet()\n #seq = 0\n #activityCount = 0\n #status: SessionStatus = 'starting'\n #sdkSessionId: string | undefined\n #model: string | undefined\n #permissionMode: PermissionMode\n #reasoningEffort: string | undefined\n /** What the binary said the profile's defaults resolve to (thread/start\n * response) — lets `setModel(undefined)` mean \"back to the default\" even\n * though a turn/start override persists for subsequent turns. */\n #resolvedModel: string | undefined\n /** Last reported ChatGPT plan, so `plan_info` is emitted once per change. */\n #planType: string | undefined\n #resolvedEffort: string | undefined\n #queue: QueuedTurn[] = []\n #turnChain: Promise<void> = Promise.resolve()\n #activeTurn: ActiveTurn | undefined\n #connection: AppServerConnection | undefined\n #threadLoaded = false\n #numTurns = 0\n #totalCostUsd: number | undefined\n #lastActivityAt: number | undefined\n #started = false\n #closed = false\n /** Session temp dir for image attachments (`localImage` takes host paths). */\n #imageDir: string | undefined\n /** Pending server→client approvals, keyed by the surfaced request id. */\n #approvals = new Map<string, PendingCodexApproval>()\n /** True from start() until the resume backfill (the turn chain's first link)\n * settles — while set, sendMessage defers its user_message echo behind the\n * chain so a new turn can never precede or interleave the replayed history. */\n #backfillPending = false\n /** The resumed thread's prior turns, stashed by {@link #ensureThread} from\n * the ONE thread/resume the backfill consumes (`partial` = the response's\n * turnsBackwardsCursor said older turns exist beyond this page). A mid-life\n * reconnect also goes through thread/resume, but with no backfill pending\n * nothing is stashed — history is never replayed twice. */\n #resumedHistory: { turns: AppServerHistoryTurn[]; partial: boolean } | undefined\n /** Set around history replay: {@link #emit} stamps `replay: true` onto the\n * message events the live item mapping produces. */\n #replayingHistory = false\n /** Last `skills` payload emitted, serialized — the comparison that keeps a\n * `skills/changed` storm (the watcher fires per touched file) from filling\n * the event log with identical lists. */\n #skillsFingerprint: string | undefined\n /** In-flight `skills/list`, so a burst of `skills/changed` makes one call.\n * The pending promise is reused rather than queued: the request has no\n * arguments, so a second one would ask the same question. */\n #skillsRefresh: Promise<void> | undefined\n /** Host paths already announced via `file_produced`, so the same picture\n * reported on both the progress and the completed item registers once. */\n #producedPaths = new Set<string>()\n /** Per-server liveness, accumulated from `mcpServer/startupStatus/updated`.\n * `mcpServerStatus/list` does not carry a status field at all, so without\n * this every server would read as \"configured\" and never as up or down. */\n #mcpStatus = new Map<string, { status: string; error?: string; failureReason?: string }>()\n\n constructor(config: CodexRunnerConfig, id: string = randomUUID()) {\n const mode = config.permissionMode ?? 'default'\n if (!ENGINE_CAPABILITIES.codex.permissionModes.includes(mode)) {\n throw new Error(`permission mode '${mode}' is not supported by the codex engine`)\n }\n if (config.forkSession) {\n throw new Error('the codex engine cannot fork a resumed thread')\n }\n // Optional on the wire, required here — the codex binary runs in a real\n // directory (see the same check in `SessionRunner`).\n if (!config.cwd) throw new Error('the codex engine requires a cwd')\n this.#cwd = config.cwd\n this.#config = config\n this.#permissionMode = mode\n this.#model = config.model\n this.#reasoningEffort = config.reasoningEffort\n this.#sdkSessionId = config.resume\n this.id = id\n this.createdAt = Date.now()\n }\n\n /** The complete child environment — spawn env replaces process.env wholesale,\n * so this must carry everything a shell would, with the profile's CODEX_HOME\n * pin winning over operator env. */\n #childEnv(): Record<string, string> {\n const base = this.#config.env ?? process.env\n const env: Record<string, string> = {}\n for (const [key, value] of Object.entries(base)) {\n if (value !== undefined) env[key] = value\n }\n if (this.#config.codexHome) env.CODEX_HOME = this.#config.codexHome\n return env\n }\n\n get status(): SessionStatus {\n return this.#status\n }\n\n get sdkSessionId(): string | undefined {\n return this.#sdkSessionId\n }\n\n get lastSeq(): number {\n return this.#seq\n }\n\n get pendingApprovals(): PermissionRequest[] {\n return [...this.#approvals.values()].map((pending) => pending.request)\n }\n\n info(): SessionInfo {\n return {\n id: this.id,\n sdkSessionId: this.#sdkSessionId,\n status: this.#status,\n cwd: this.#cwd,\n profile: this.#config.profile,\n engine: 'codex',\n capabilities: ENGINE_CAPABILITIES.codex,\n model: this.#model ?? this.#resolvedModel,\n permissionMode: this.#permissionMode,\n canBypassPermissions: true,\n createdAt: this.createdAt,\n lastSeq: this.#seq,\n activityCount: this.#activityCount,\n pendingPermissionCount: this.#approvals.size,\n meta: this.#config.meta,\n scope: this.#config.scope,\n title: this.#title(),\n totalCostUsd: this.#totalCostUsd,\n numTurns: this.#numTurns || undefined,\n lastActivityAt: this.#lastActivityAt,\n }\n }\n\n #title(): string | undefined {\n const metaTitle = this.#config.meta?.title\n if (typeof metaTitle === 'string' && metaTitle.length > 0) return metaTitle\n const prompt = this.#config.prompt\n if (!prompt) return undefined\n return prompt.length > 80 ? prompt.slice(0, 77) + '…' : prompt\n }\n\n /** Host-facing rename: writes `meta.title`, which `#title()` prefers. Clearing\n * it (undefined) restores the derived title. The engine is never told. */\n setTitle(title: string | undefined): void {\n const meta = { ...this.#config.meta }\n if (title) meta.title = title\n else delete meta.title\n this.#config = { ...this.#config, meta }\n }\n\n start(): Promise<void> {\n if (this.#started) return this.#turnChain\n this.#started = true\n if (this.#config.resume && this.#config.backfillHistory !== false) {\n // First link of the turn chain: connect, thread/resume, and replay the\n // thread's prior turns as `replay: true` events before any queued turn\n // runs (and before its echo — see sendMessage). This is also why a\n // promptless resume now connects eagerly rather than on first message:\n // its history is the whole point of attaching to it.\n this.#backfillPending = true\n this.#turnChain = this.#turnChain.then(() => this.#backfillHistory())\n } else {\n this.#setStatus('idle')\n }\n if (this.#config.prompt) this.sendMessage(this.#config.prompt)\n // A session that is about to connect anyway (a prompt to run, or a resume\n // to backfill) gets its skills from that connection a moment later. Only\n // the promptless, non-resume case — the dashboard's \"create, then type\" —\n // would otherwise sit with no child and therefore no skill list at all,\n // which is the one place codex's own TUI has them and we did not.\n if (!this.#config.prompt && !this.#config.resume) void this.#probeSkills()\n return this.#turnChain\n }\n\n /**\n * List skills over a **throwaway** connection, for a session with nothing else\n * to do yet.\n *\n * `skills/list` needs a live child but not a thread, so this spawns one, asks,\n * and closes it — rather than bringing up the session's own child early and\n * leaving a codex process parked behind every session someone created and\n * never typed into. The session's real connection re-lists when it arrives;\n * the fingerprint compare in {@link #refreshSkills} makes that a no-op.\n *\n * Entirely best-effort and never awaited: a missing binary, a failed spawn or\n * a rejected handshake here must not turn a session that has not started into\n * a session that failed.\n */\n async #probeSkills(): Promise<void> {\n let connection: AppServerConnection | undefined\n try {\n connection = await this.#openScratchConnection()\n if (this.#closed) return\n await this.#refreshSkills(connection)\n } catch {\n // The session is fine; it simply has no skill list until its own child\n // comes up and asks again.\n } finally {\n connection?.close()\n }\n }\n\n /**\n * A handshaken child that is **not** the session's — for the questions a\n * client can ask before the session has anything to run (its skills, its MCP\n * servers). The caller owns it and must close it.\n *\n * No onNotification/onRequest/onClose wiring on purpose: this child answers\n * one question and goes away, so its notifications are noise and its death is\n * not the session's problem. The alternative — bringing the session's real\n * child up early — would park a codex process behind every session someone\n * created and never typed into.\n */\n async #openScratchConnection(): Promise<AppServerConnection> {\n const connection = this.#config.connectFn({ env: this.#childEnv() })\n try {\n await connection.request('initialize', {\n clientInfo: {\n name: 'workerdeck',\n title: 'WorkerDeck',\n version: `protocol-${PROTOCOL_VERSION}`,\n },\n capabilities: { experimentalApi: true },\n })\n connection.notify('initialized')\n return connection\n } catch (error) {\n connection.close()\n throw error\n }\n }\n\n sendMessage(text: string, attachments?: readonly AttachmentInput[]): void {\n if (this.#closed) throw new Error('session is closed')\n const input = this.#buildInput(text, attachments ?? [])\n const echo = () =>\n this.#emit({\n type: 'user_message',\n message: { role: 'user', content: text },\n parentToolUseId: null,\n attachments: attachments?.length ? attachments.map(attachmentRef) : undefined,\n uuid: randomUUID(),\n })\n // While a resume's history replay is still pending, the echo rides the\n // turn chain (which the replay heads), so the new turn's user message can\n // never precede the history it follows. Otherwise it is immediate — a\n // message queued behind a running turn still echoes right away.\n if (this.#backfillPending) this.#turnChain = this.#turnChain.then(echo)\n else echo()\n this.#queue.push({ input })\n this.#scheduleTurn()\n }\n\n /**\n * App-server input for a message with attachments: images land in a session\n * temp dir and travel as `localImage` host paths, text files inline into the\n * prompt in the shared named envelope, PDF has no representation (the\n * gateway's 415 normally refuses it first).\n */\n #buildInput(text: string, attachments: readonly AttachmentInput[]): AppServerUserInput[] {\n const parts: AppServerUserInput[] = []\n for (const attachment of attachments) {\n const mediaType = normalizeMediaType(attachment.mediaType)\n switch (attachmentKind(mediaType)) {\n case 'image': {\n this.#imageDir ??= join(tmpdir(), `workerdeck-codex-${this.id}`)\n mkdirSync(this.#imageDir, { recursive: true })\n const ext = mediaType.split('/')[1] ?? 'bin'\n const path = join(this.#imageDir, `${attachment.id}.${ext}`)\n writeFileSync(path, Buffer.from(attachment.data, 'base64'))\n parts.push({ type: 'localImage', path })\n break\n }\n case 'text':\n parts.push({\n type: 'text',\n text:\n `<attachment name=\"${attachment.name}\" type=\"${mediaType}\">\\n` +\n `${Buffer.from(attachment.data, 'base64').toString('utf8')}\\n</attachment>`,\n })\n break\n default:\n throw new Error(\n `unsupported attachment media type for the codex engine: ${attachment.mediaType}`,\n )\n }\n }\n if (text) parts.push({ type: 'text', text })\n return parts\n }\n\n /** Resolve a pending approval. Returns false if the id is unknown (e.g.\n * timed out, or already settled by codex itself). */\n resolvePermission(requestId: string, decision: PermissionDecision): boolean {\n const pending = this.#approvals.get(requestId)\n if (!pending) return false\n this.#settleApproval(requestId, pending, decision, 'client')\n return true\n }\n\n async interrupt(): Promise<void> {\n // A pending approval is what's holding the turn open — settle each as a\n // denied interrupt first (codex's 'cancel' where the request offers it,\n // which itself ends the turn).\n for (const [id, pending] of this.#approvals) {\n this.#settleApproval(\n id,\n pending,\n { behavior: 'deny', message: 'interrupted', interrupt: true },\n 'policy',\n )\n }\n await this.#interruptTurn()\n await this.#turnChain\n }\n\n /** Address the in-flight turn only (no approval sweep) — also the follow-up\n * for a deny+interrupt whose wire decision couldn't carry the interrupt. */\n async #interruptTurn(): Promise<void> {\n const active = this.#activeTurn\n const connection = this.#connection\n if (active && !active.settled) {\n active.interrupted = true\n if (connection && active.turnId && this.#sdkSessionId) {\n try {\n await connection.request('turn/interrupt', {\n threadId: this.#sdkSessionId,\n turnId: active.turnId,\n })\n // The terminal turn/completed (status 'interrupted') settles the turn.\n } catch {\n // The turn may already be over, or the child gone — both settle it.\n }\n } else if (connection) {\n // No turn id yet (interrupted before turn/started): there is nothing\n // to address the request to, so end the child — the thread survives on\n // disk and the next message respawns into it.\n // The onClose rejection settles the turn; `interrupted` explains it.\n connection.close()\n if (this.#connection === connection) this.#connection = undefined\n active.reject(new Error('interrupted'))\n }\n }\n }\n\n async setPermissionMode(mode: PermissionMode): Promise<void> {\n if (!ENGINE_CAPABILITIES.codex.permissionModes.includes(mode)) {\n throw new Error(`permission mode '${mode}' is not supported by the codex engine`)\n }\n if (this.#activeTurn) {\n throw new Error(\"cannot change the permission mode mid-turn (the running turn's sandbox is fixed)\")\n }\n this.#permissionMode = mode\n this.#emit({ type: 'permission_mode_changed', mode })\n }\n\n async setModel(model?: string): Promise<void> {\n if (this.#activeTurn) {\n throw new Error(\"cannot change the model mid-turn (the running turn's model is fixed)\")\n }\n this.#model = model\n this.#emit({ type: 'model_changed', model })\n }\n\n fail(message: string): void {\n if (this.#closed) return\n this.#emit({ type: 'session_error', message })\n this.#setStatus('failed')\n this.close('error')\n }\n\n close(reason: 'client' | 'server' | 'error' = 'client'): void {\n if (this.#closed) return\n this.#closed = true\n this.#queue.length = 0\n // Settle pending approvals before the connection goes: each gets its\n // channel's own \"no\" on the wire and a permission_resolved in the log.\n for (const [id, pending] of this.#approvals) {\n this.#settleApproval(id, pending, { behavior: 'deny', message: 'Session closed' }, 'policy')\n }\n this.#connection?.close()\n this.#connection = undefined\n this.#activeTurn?.reject(new Error('session closed'))\n if (this.#imageDir) {\n try {\n rmSync(this.#imageDir, { recursive: true, force: true })\n } catch {\n // Temp-dir cleanup must never break teardown.\n }\n }\n this.#emit({ type: 'session_closed', reason })\n this.#setStatus('closed')\n }\n\n /** See `Runner.eventAt`. A linear scan: the one caller is a reader pressing\n * \"show everything\" on one row, so a per-runner seq index would be a map\n * maintained on every emit to save a walk nobody makes twice a minute. */\n eventAt(seq: number): SessionEvent | undefined {\n return this.#events.find((event) => event.seq === seq)\n }\n\n subscribe(\n listener: SessionEventListener,\n afterSeq = 0,\n options?: SubscribeOptions,\n ): () => void {\n return this.#subscribers.subscribe(this.#events, listener, afterSeq, options)\n }\n\n #scheduleTurn(): void {\n this.#turnChain = this.#turnChain.then(() => this.#runTurn())\n }\n\n /**\n * The session's live connection with its thread loaded, (re)building both as\n * needed: spawn + `initialize`/`initialized` on a fresh child, then\n * `thread/start` (new) or `thread/resume` (a create-request `resume`, or a\n * thread orphaned by a dead child). The response's resolved model/effort are\n * kept so per-turn overrides can name \"the profile default\" explicitly.\n */\n async #ensureThread(): Promise<AppServerConnection> {\n if (this.#closed) throw new Error('session is closed')\n let connection = this.#connection\n if (!connection) {\n connection = this.#config.connectFn({ env: this.#childEnv() })\n this.#connection = connection\n this.#threadLoaded = false\n connection.onNotification((method, params) => this.#handleNotification(method, params))\n connection.onRequest((method, params, id) => this.#answerServerRequest(method, params, id))\n connection.onClose((message) => {\n if (this.#connection === connection) {\n this.#connection = undefined\n this.#threadLoaded = false\n }\n // Approvals pending against a dead child can never be answered on the\n // wire — retire their cards and timers.\n for (const [id, pending] of this.#approvals) {\n this.#settleApproval(id, pending, { behavior: 'deny', message }, 'policy')\n }\n // A child dying mid-turn fails that turn (with the exit diagnostic);\n // idle, there is nothing to settle and the next turn respawns.\n this.#activeTurn?.reject(new Error(message))\n })\n try {\n // `experimentalApi` is load-bearing, not a nicety: granular approval\n // policies are rejected without it, and WorkerDeck ships ONE code path\n // (no string-policy fallback). A binary that rejects the capability\n // must fail loudly here — a session that quietly stops asking for\n // approvals is worse than one that refuses to start and says why.\n await connection.request('initialize', {\n clientInfo: {\n name: 'workerdeck',\n title: 'WorkerDeck',\n version: `protocol-${PROTOCOL_VERSION}`,\n },\n capabilities: { experimentalApi: true },\n })\n } catch (error) {\n // Don't leave a half-initialized child around — the next message must\n // respawn from scratch, not talk to a child that refused the handshake.\n connection.close()\n if (this.#connection === connection) this.#connection = undefined\n if (error instanceof JsonRpcError) {\n throw new Error(\n 'codex app-server rejected initialize (capabilities.experimentalApi: true — required ' +\n 'for the granular approval policy, and WorkerDeck has no non-experimental fallback): ' +\n error.message,\n )\n }\n throw error\n }\n connection.notify('initialized')\n }\n if (!this.#threadLoaded) {\n const options: Record<string, unknown> = {\n cwd: this.#cwd,\n approvalPolicy: APPROVAL_POLICY_BY_MODE[this.#permissionMode],\n sandbox: THREAD_SANDBOX_BY_MODE[this.#permissionMode],\n }\n if (this.#model) options.model = this.#model\n const resuming = this.#sdkSessionId !== undefined\n const result = (resuming\n ? await connection.request('thread/resume', { threadId: this.#sdkSessionId, ...options })\n : await connection.request('thread/start', options)) as {\n thread?: { id?: string; turns?: AppServerHistoryTurn[] }\n model?: string | null\n reasoningEffort?: string | null\n /** Non-null: `thread.turns` is one PAGE and older turns exist beyond it. */\n turnsBackwardsCursor?: string | null\n }\n if (typeof result?.thread?.id === 'string') this.#sdkSessionId = result.thread.id\n if (typeof result?.model === 'string') this.#resolvedModel = result.model\n if (typeof result?.reasoningEffort === 'string') this.#resolvedEffort = result.reasoningEffort\n // The resume that backfill is waiting on carries the thread's prior\n // turns — stash them for it. A reconnect after a dead child resumes the\n // same thread but has no backfill pending, so nothing is stashed and\n // history is never replayed twice.\n if (resuming && this.#backfillPending && !this.#resumedHistory) {\n this.#resumedHistory = {\n turns: Array.isArray(result?.thread?.turns) ? result.thread.turns : [],\n partial: typeof result?.turnsBackwardsCursor === 'string',\n }\n }\n this.#threadLoaded = true\n }\n // Fire and forget, and only now: `skills/list` needs a live child, and a\n // codex session does not spawn one until it has something to do. So the\n // skill list arrives with the first turn rather than at create time —\n // which is why clients gate the affordance on having received a `skills`\n // event, not on the capability flag alone.\n void this.#refreshSkills(connection)\n return connection\n }\n\n /**\n * Re-read `skills/list` and publish it, if it changed.\n *\n * **`cwds` is passed explicitly, and must be.** The schema documents the empty\n * case as \"the current session working directory\", which reads like the\n * thread's — it is not. Measured against 0.146.0: with no `cwds`, and *after*\n * a `thread/start` carrying this session's cwd, the response comes back keyed\n * to the app-server child's own process directory (for WorkerDeck, wherever\n * the gateway was launched) and reports no repo-scoped skills at all. So a\n * project's own `.codex/skills/**` were invisible until this argument existed.\n *\n * Best-effort throughout. A binary too old to know the method, a broken\n * manifest, a child that died mid-call — none of that is worth failing a\n * session over, and the panel simply stays absent.\n */\n async #refreshSkills(connection: AppServerConnection): Promise<void> {\n if (this.#skillsRefresh) return this.#skillsRefresh\n const run = (async () => {\n try {\n const result = (await connection.request('skills/list', {\n cwds: [this.#cwd],\n })) as AppServerSkillsListResponse\n if (this.#closed) return\n const entries = Array.isArray(result?.data) ? result.data : []\n const seen = new Set<string>()\n const skills: SkillInfo[] = []\n for (const entry of entries) {\n for (const skill of entry?.skills ?? []) {\n // The same skill can be reported under several cwds; the first\n // wins, matching how codex itself resolves a name collision.\n if (typeof skill?.name !== 'string' || seen.has(skill.name)) continue\n seen.add(skill.name)\n skills.push(skillInfo(skill))\n }\n }\n skills.sort((a, b) => a.name.localeCompare(b.name))\n const fingerprint = JSON.stringify(skills)\n if (fingerprint === this.#skillsFingerprint) return\n this.#skillsFingerprint = fingerprint\n this.#emit({ type: 'skills', skills })\n } catch {\n // Nothing to say: an engine that cannot list its skills is an engine\n // whose skills panel does not appear.\n } finally {\n this.#skillsRefresh = undefined\n }\n })()\n this.#skillsRefresh = run\n return run\n }\n\n /**\n * The session's MCP servers, live from the binary.\n *\n * Two sources merged, because codex splits them: `mcpServerStatus/list` says\n * what is configured and what each server exposes (including every tool's\n * full JSON Schema, which the Agent SDK does not give us), and the\n * `mcpServer/startupStatus/updated` notifications say which of them are\n * actually up.\n *\n * Answers **before the session has connected**, over a throwaway child, for\n * the same reason the skill list does: a codex session spawns nothing until\n * it has work, and a panel that said \"no MCP servers configured\" until the\n * first turn would be stating something false about the operator's config.\n * The request blocks until the servers are enumerated (measured: complete on\n * the very first call), so there is no half-populated answer to race.\n *\n * Resolves undefined only when there is genuinely nothing to say — the\n * session is closed, or the child could not be spoken to. The route turns\n * that into a 501.\n *\n * **Listing only.** There is no per-server reconnect or toggle on this\n * transport — hence no `reconnectMcpServer`/`setMcpServerEnabled` here, and\n * `ENGINE_CAPABILITIES.codex.mcpServerActions: false` so clients render the\n * panel read-only instead of offering buttons that cannot work.\n */\n async mcpServers(): Promise<McpServerStatusInfo[] | undefined> {\n if (this.#closed) return undefined\n const live = this.#connection\n let scratch: AppServerConnection | undefined\n try {\n // The session's own child when it has one — its accumulated\n // `#mcpStatus` makes the answer sharper — and a throwaway otherwise.\n const connection = live ?? (scratch = await this.#openScratchConnection())\n const result = (await connection.request(\n 'mcpServerStatus/list',\n {},\n )) as AppServerMcpServerStatusResponse\n return (result?.data ?? []).map((server) =>\n mcpServerInfo(server, this.#mcpStatus.get(server.name)),\n )\n } catch {\n return undefined\n } finally {\n scratch?.close()\n }\n }\n\n /**\n * Announce a file the ENGINE wrote on the host, so a client can fetch it\n * without the operator having declared its directory as a host-file root.\n *\n * Deliberately narrow: only paths codex reports as *written by its own tool*\n * belong here. A path the model merely read (`imageView`) is an agent-chosen\n * claim, and those keep going through `/fs/*` and its root allowlist — see\n * the note on `file_produced` in the protocol.\n */\n #emitFileProduced(path: string, toolUseId: string): void {\n if (this.#producedPaths.has(path)) return\n this.#producedPaths.add(path)\n let bytes: number | undefined\n try {\n const stat = statSync(path)\n if (stat.isFile()) bytes = stat.size\n // A `savedPath` that is not a regular file is still announced: the route\n // re-checks before serving, and a client showing the path it was given\n // beats one silently dropping it.\n } catch {\n // Reported but not there (yet, or at all) — announce it anyway and let\n // the fetch be the thing that fails.\n }\n this.#emit({\n type: 'file_produced',\n fileId: producedFileId(path),\n path,\n ...(producedMediaType(path) ? { mediaType: producedMediaType(path) } : {}),\n ...(bytes !== undefined ? { bytes } : {}),\n toolUseId,\n })\n }\n\n /**\n * On resume, replay the thread's prior turns as `replay: true` events,\n * seq'd before any live turn — the SessionRunner backfill contract, fed\n * from `thread/resume`'s own `thread.turns`. When the resume response says\n * that page is partial (`turnsBackwardsCursor`), the FULL rollout history\n * is fetched via `thread/read {includeTurns: true}` instead — and if even\n * that fails, the partial page is replayed under a visible notice rather\n * than silently posing as the whole thread. Best-effort like the Claude\n * backfill: an unreadable history never blocks the resume itself.\n */\n async #backfillHistory(): Promise<void> {\n try {\n if (this.#closed) return\n const connection = await this.#ensureThread()\n const resumed = this.#resumedHistory\n this.#resumedHistory = undefined\n let turns = resumed?.turns ?? []\n let partialReason: string | undefined\n if (resumed?.partial) {\n try {\n const read = (await connection.request('thread/read', {\n threadId: this.#sdkSessionId,\n includeTurns: true,\n })) as { thread?: { turns?: AppServerHistoryTurn[] } }\n const full = read?.thread?.turns\n if (Array.isArray(full) && full.length >= turns.length) turns = full\n else partialReason = 'thread/read returned less history than the resume page'\n } catch (error) {\n partialReason = error instanceof Error ? error.message : String(error)\n }\n }\n if (partialReason) {\n // Rendered as an inline notice by both clients (the session keeps\n // running) — a truthful-but-partial transcript must say so, above the\n // part it does show.\n this.#emit({\n type: 'session_error',\n message: `Resumed thread history is incomplete — older turns could not be loaded (${partialReason})`,\n })\n }\n this.#replayTurns(turns)\n } catch {\n // A missing/unreadable thread must not block the resume: the next real\n // turn retries the connection and surfaces its own failure loudly.\n } finally {\n this.#backfillPending = false\n this.#setStatus('idle')\n }\n }\n\n /** Replay historical turns through the SAME item mapping the live path uses. */\n #replayTurns(turns: readonly AppServerHistoryTurn[]): void {\n for (const turn of turns) {\n if (this.#closed) return\n // \"Per turn\" means per HISTORICAL turn: each replayed turn gets its own\n // nonce exactly as each live turn does — codex item ids restart per turn\n // (\"item-1\", …), so one shared namespace would fold turn N's items into\n // turn 1's bubbles (b026e70), and a fresh random nonce per turn also\n // keeps replayed ids disjoint from every future live turn's.\n const state = this.#newTurnState()\n this.#replayingHistory = true\n try {\n for (const item of turn.items ?? []) {\n if (item.type === 'userMessage') {\n // Dropped on the live path (sendMessage already echoed it); in\n // history this IS the turn's user message.\n const text = historyUserText(item)\n if (!text) continue\n this.#emit({\n type: 'user_message',\n message: { role: 'user', content: text },\n parentToolUseId: null,\n uuid: `${state.nonce}:${item.id}`,\n })\n continue\n }\n this.#handleItemCompleted(item, state)\n }\n } finally {\n this.#replayingHistory = false\n }\n }\n }\n\n /** Fresh per-turn state — one per live turn, and one per REPLAYED turn (the\n * nonce is the item-id namespace, and its per-turn-ness is the invariant). */\n #newTurnState(): ActiveTurn {\n return {\n nonce: randomUUID(),\n interrupted: false,\n usage: {\n inputTokens: 0,\n cachedInputTokens: 0,\n cacheWriteInputTokens: 0,\n outputTokens: 0,\n reasoningOutputTokens: 0,\n totalTokens: 0,\n },\n sawUsage: false,\n toolUseEmitted: new Set(),\n sectionIndex: new Map(),\n settled: false,\n resolve: () => {},\n reject: () => {},\n }\n }\n\n async #runTurn(): Promise<void> {\n if (this.#closed) return\n const turn = this.#queue.shift()\n if (!turn) return\n this.#setStatus('running')\n const startedAt = Date.now()\n const active: ActiveTurn = this.#newTurnState()\n const outcome = new Promise<AppServerTurn>((resolve, reject) => {\n active.resolve = (turnResult) => {\n if (active.settled) return\n active.settled = true\n resolve(turnResult)\n }\n active.reject = (error) => {\n if (active.settled) return\n active.settled = true\n reject(error)\n }\n })\n this.#activeTurn = active\n try {\n const connection = await this.#ensureThread()\n const params: Record<string, unknown> = {\n threadId: this.#sdkSessionId,\n input: turn.input,\n cwd: this.#cwd,\n approvalPolicy: APPROVAL_POLICY_BY_MODE[this.#permissionMode],\n sandboxPolicy: TURN_SANDBOX_BY_MODE[this.#permissionMode],\n }\n // Overrides persist \"for this turn and subsequent turns\", so name the\n // model/effort explicitly every turn — the resolved default when no\n // override is set, which is what makes setModel(undefined) a real reset.\n const model = this.#model ?? this.#resolvedModel\n if (model) params.model = model\n const effort = this.#reasoningEffort ?? this.#resolvedEffort\n if (effort) params.effort = effort\n // The terminal signal is the turn/completed NOTIFICATION; the response's\n // timing is unspecified, so it only contributes its turn id, a JSON-RPC\n // error (no turn ran → fail now), or — defensively — a terminal status.\n connection.request('turn/start', params).then(\n (result) => {\n const started = (result as { turn?: AppServerTurn })?.turn\n if (!started) return\n active.turnId ??= started.id\n if (started.status && started.status !== 'inProgress') active.resolve(started)\n },\n (error: unknown) => active.reject(error instanceof Error ? error : new Error(String(error))),\n )\n const result = await outcome\n if (this.#closed) return\n if (result.status === 'completed') {\n this.#finishTurn('success', startedAt, active)\n } else {\n const reason =\n result.status === 'interrupted'\n ? 'interrupted'\n : (result.error?.message ??\n active.lastError ??\n 'codex app-server ended the turn without a result')\n this.#finishTurn('failure', startedAt, active, [reason])\n }\n } catch (error) {\n if (this.#closed) return\n // A failed turn is not a failed session: the thread persists on disk and\n // the next message reconnects and resumes it.\n const message = error instanceof Error ? error.message : String(error)\n this.#finishTurn('failure', startedAt, active, [active.interrupted ? 'interrupted' : message])\n } finally {\n if (this.#activeTurn === active) this.#activeTurn = undefined\n }\n }\n\n // -------------------------------------------------------------------------\n // Server→client traffic\n // -------------------------------------------------------------------------\n\n #handleNotification(method: string, params: unknown): void {\n if (this.#closed) return\n // The app-server surface is wide (mcpServer/*, account/*, thread\n // housekeeping…) — everything unmapped is deliberately dropped.\n this.#notifications[method]?.(params)\n }\n\n /** Reasoning deltas arrive on two methods that differ only in which section\n * counter they advance; the section key carries the method so the two streams\n * never share a boundary. Section boundaries (a new summary/content entry)\n * render as paragraph breaks — the completed item joins sections with '\\n\\n'. */\n #reasoningDelta(method: string): (params: unknown) => void {\n return (params) => {\n const active = this.#activeTurn\n if (!active) return\n const payload = params as {\n delta?: string\n itemId?: string\n contentIndex?: number\n summaryIndex?: number\n }\n if (typeof payload?.delta !== 'string' || !payload.delta) return\n const index = payload.contentIndex ?? payload.summaryIndex ?? 0\n const key = `${payload.itemId ?? ''}:${method}`\n const previous = active.sectionIndex.get(key)\n active.sectionIndex.set(key, index)\n const separator = previous !== undefined && index > previous ? '\\n\\n' : ''\n this.#emitDelta({ type: 'thinking_delta', thinking: separator + payload.delta })\n }\n }\n\n /** One item-progress handler serves `item/started` and `item/updated`. */\n #itemProgress = (params: unknown): void => {\n const active = this.#activeTurn\n if (!active) return\n const item = (params as { item?: AppServerItem })?.item\n if (item) this.#handleItemProgress(item, active)\n }\n\n /** The notification dispatch table — every method the child emits that this\n * runner maps, in one place. Handlers read `this.#activeTurn` themselves:\n * dispatch is synchronous, so the read is the same one the old switch made. */\n readonly #notifications: Record<string, (params: unknown) => void> = {\n 'thread/started': (params) => {\n const thread = (params as { thread?: { id?: string } })?.thread\n if (typeof thread?.id === 'string') this.#sdkSessionId = thread.id\n },\n 'turn/started': (params) => {\n const active = this.#activeTurn\n const turn = (params as { turn?: AppServerTurn })?.turn\n if (active && turn && !active.turnId) active.turnId = turn.id\n },\n 'turn/completed': (params) => {\n const active = this.#activeTurn\n const turn = (params as { turn?: AppServerTurn })?.turn\n if (active && turn) active.resolve(turn)\n },\n 'item/started': this.#itemProgress,\n 'item/updated': this.#itemProgress,\n 'item/completed': (params) => {\n const active = this.#activeTurn\n if (!active) return\n const item = (params as { item?: AppServerItem })?.item\n if (item) this.#handleItemCompleted(item, active)\n },\n 'item/agentMessage/delta': (params) => {\n if (!this.#activeTurn) return\n const delta = (params as { delta?: string })?.delta\n if (typeof delta === 'string' && delta) {\n this.#emitDelta({ type: 'text_delta', text: delta })\n }\n },\n 'item/reasoning/textDelta': this.#reasoningDelta('item/reasoning/textDelta'),\n 'item/reasoning/summaryTextDelta': this.#reasoningDelta('item/reasoning/summaryTextDelta'),\n 'thread/tokenUsage/updated': (params) => {\n const active = this.#activeTurn\n if (!active) return\n const last = (params as AppServerTokenUsageUpdate)?.tokenUsage?.last\n if (!last) return\n // `last` is one model request; a tool-looping turn makes several. The\n // per-turn number the Anthropic convention wants is their sum.\n active.sawUsage = true\n active.usage.inputTokens += last.inputTokens ?? 0\n active.usage.cachedInputTokens += last.cachedInputTokens ?? 0\n active.usage.cacheWriteInputTokens =\n (active.usage.cacheWriteInputTokens ?? 0) + (last.cacheWriteInputTokens ?? 0)\n active.usage.outputTokens += last.outputTokens ?? 0\n active.usage.reasoningOutputTokens += last.reasoningOutputTokens ?? 0\n // Context occupancy is the OPPOSITE choice from the accounting above:\n // `last` (overwritten, not summed) against the window, because a request's\n // input already contains the whole conversation. `total` is cumulative\n // billing — it grows every turn while the context stays where it is, so a\n // meter built on it would climb to 100% on an almost-empty thread\n // (measured: total 13931 → 27878 across two trivial turns, last 13931 →\n // 13947, window 258400).\n const update = params as AppServerTokenUsageUpdate\n active.contextTokens = last.totalTokens ?? undefined\n active.contextWindow = update.tokenUsage?.modelContextWindow ?? undefined\n },\n 'mcpServer/startupStatus/updated': (params) => {\n // The ONLY place a server's liveness comes from — `mcpServerStatus/list`\n // reports what is configured and what it exposes, never whether it is\n // up. Not gated on `active`: servers start with the child, well before\n // any turn.\n const update = params as AppServerMcpStatusUpdate\n if (typeof update?.name !== 'string') return\n this.#mcpStatus.set(update.name, {\n status: typeof update.status === 'string' ? update.status : 'starting',\n ...(update.error ? { error: update.error } : {}),\n ...(update.failureReason ? { failureReason: update.failureReason } : {}),\n })\n },\n 'skills/changed': () => {\n // An invalidation signal with no payload — codex's watcher saying\n // \"re-run skills/list\", which is exactly what this does. Not gated on\n // `active`: the operator can edit a skill between turns, and that is\n // in fact when they usually do.\n const connection = this.#connection\n if (connection) void this.#refreshSkills(connection)\n },\n 'account/rateLimits/updated': (params) => {\n // Pushed during a turn, so — unlike the Claude engine, whose CLI only\n // pushes on change and therefore needs an explicit poll — listening is\n // enough. Not gated on `active`: a window update is about the account,\n // not the turn.\n this.#emitRateLimits((params as { rateLimits?: AppServerRateLimits })?.rateLimits)\n },\n 'turn/plan/updated': (params) => {\n // v2's todo list, published as the codex.todo_list sdk_event payload\n // both clients already render.\n const active = this.#activeTurn\n if (!active) return\n const plan = (params as AppServerPlanUpdate)?.plan\n if (!Array.isArray(plan)) return\n this.#emit({\n type: 'sdk_event',\n payload: {\n type: 'codex.todo_list',\n id: `${active.nonce}:plan`,\n items: plan.map((step) => ({ text: step.step, completed: step.status === 'completed' })),\n },\n })\n },\n 'serverRequest/resolved': (params) => {\n // Codex settled one of its own asks (auto-resolution, e.g.\n // requestUserInput's autoResolutionMs) — retire the matching card. The\n // late JSON-RPC response we still send is ignored by the peer. The\n // resolved event reports 'deny' because we cannot know what codex\n // chose; the message says who really decided.\n const requestId = (params as { requestId?: string | number })?.requestId\n if (requestId === undefined) return\n for (const [id, pending] of this.#approvals) {\n if (pending.wireId === requestId) {\n this.#settleApproval(id, pending, { behavior: 'deny', message: 'resolved by codex' }, 'policy')\n return\n }\n }\n },\n 'error': (params) => {\n // Mostly retry noise (`willRetry: true`); keep the last message so a\n // turn that fails without its own error still explains itself.\n const active = this.#activeTurn\n const error = (params as { error?: { message?: string } })?.error\n if (active && typeof error?.message === 'string') active.lastError = error.message\n },\n }\n\n /** Answer a server→client request: the ask channels become pending\n * permission requests; anything else gets a JSON-RPC -32601 rather than a\n * hang (an unanswered server request wedges the turn). */\n async #answerServerRequest(\n method: string,\n params: unknown,\n wireId?: string | number,\n ): Promise<unknown> {\n const channel = APPROVAL_CHANNELS[method]\n if (channel) return this.#requestApproval(channel, method, params, wireId)\n throw new JsonRpcError(-32601, `workerdeck does not handle server request '${method}'`)\n }\n\n /**\n * Surface one ask-channel request as a pending {@link PermissionRequest};\n * the returned promise is the JSON-RPC response, resolved when a\n * `permission_decision` lands — or by the timeout, an interrupt, turn end,\n * session close, or codex resolving it itself. Never left hanging.\n */\n #requestApproval(\n channel: ApprovalChannel,\n method: string,\n params: unknown,\n wireId: string | number | undefined,\n ): Promise<unknown> {\n // AskUserQuestion policy resolution, the SessionRunner convention: 'auto'\n // picks each question's first (recommended) option, 'deny' sends the model\n // back to decide for itself — both visibly, neither pending.\n if (method === 'item/tool/requestUserInput') {\n const behavior = this.#config.questionBehavior ?? 'ask'\n if (behavior !== 'ask') {\n return Promise.resolve(this.#resolveQuestionByPolicy(channel, params, behavior))\n }\n }\n const id = randomUUID()\n const timeoutMs =\n this.#config.approvalTimeoutMs ??\n this.#config.defaultApprovalTimeoutMs ??\n DEFAULT_APPROVAL_TIMEOUT_MS\n const itemId = channel.itemId(params)\n const request: PermissionRequest = {\n id,\n ...channel.describe(params),\n // Anchored to the tool card the turn already emitted for this item (the\n // command that ran and was refused, the file change in flight); channels\n // with no item anchor on the request itself.\n toolUseId: itemId ? `${this.#activeTurn?.nonce ?? 'codex'}:${itemId}` : id,\n expiresAt: Date.now() + timeoutMs,\n }\n return new Promise<unknown>((resolve) => {\n const timer = setTimeout(() => {\n const pending = this.#approvals.get(id)\n if (pending) {\n this.#settleApproval(id, pending, { behavior: 'deny', message: 'Approval timed out' }, 'timeout')\n }\n }, timeoutMs)\n this.#approvals.set(id, {\n request,\n channel,\n params,\n offered: offeredDecisions(params),\n wireId,\n timer,\n respond: resolve,\n })\n this.#emit({ type: 'permission_requested', request })\n if (this.#activeTurn) this.#setStatus('awaiting_approval')\n })\n }\n\n /** 'auto'/'deny' sessions settle codex questions synchronously instead of\n * pending. Request/resolved events still fire so transcripts and job\n * webhooks show what was chosen. */\n #resolveQuestionByPolicy(\n channel: ApprovalChannel,\n params: unknown,\n mode: 'auto' | 'deny',\n ): unknown {\n const itemId = channel.itemId(params)\n const request: PermissionRequest = {\n id: randomUUID(),\n ...channel.describe(params),\n toolUseId: itemId ? `${this.#activeTurn?.nonce ?? 'codex'}:${itemId}` : randomUUID(),\n }\n this.#emit({ type: 'permission_requested', request })\n if (mode === 'deny') {\n this.#emit({\n type: 'permission_resolved',\n requestId: request.id,\n behavior: 'deny',\n resolvedBy: 'policy',\n message:\n 'Interactive questions are disabled for this session — choose the most reasonable option yourself and continue.',\n })\n return { answers: {} }\n }\n const answers: Record<string, { answers: string[] }> = {}\n for (const question of (params as AppServerUserInputParams).questions ?? []) {\n const first = question.options?.[0]?.label\n if (first) answers[question.id] = { answers: [first] }\n }\n this.#emit({\n type: 'permission_resolved',\n requestId: request.id,\n behavior: 'allow',\n resolvedBy: 'policy',\n })\n return { answers }\n }\n\n /**\n * Settle one pending approval: pick the channel's wire response for the\n * decision, answer the JSON-RPC request, and emit `permission_resolved`.\n * An allow the request offered no plain accept for becomes the channel's\n * denial, said out loud — never a silently widened grant, and never a\n * decision the request didn't offer.\n */\n #settleApproval(\n id: string,\n pending: PendingCodexApproval,\n decision: PermissionDecision,\n resolvedBy: PermissionDecisionSource,\n ): void {\n clearTimeout(pending.timer)\n this.#approvals.delete(id)\n let behavior = decision.behavior\n let message = decision.behavior === 'deny' ? (decision.message ?? 'Denied') : undefined\n let sent: { response: unknown; decision?: string }\n if (decision.behavior === 'allow') {\n const allowed = pending.channel.allow(pending.params, decision.updatedInput, pending.offered)\n if (allowed) {\n sent = allowed\n } else {\n behavior = 'deny'\n resolvedBy = 'policy'\n message =\n 'codex offered no plain accept for this request (only broader session/policy grants) — denied instead'\n sent = pending.channel.deny(pending.params, false, pending.offered)\n }\n } else {\n sent = pending.channel.deny(pending.params, decision.interrupt === true, pending.offered)\n }\n pending.respond(sent.response)\n this.#emit({ type: 'permission_resolved', requestId: id, behavior, resolvedBy, message })\n if (behavior === 'deny' && decision.behavior === 'deny' && decision.interrupt && sent.decision !== 'cancel') {\n // The wire decision couldn't carry the interrupt itself.\n void this.#interruptTurn()\n }\n if (!this.#closed && this.#approvals.size === 0 && this.#status === 'awaiting_approval') {\n this.#setStatus('running')\n }\n }\n\n // -------------------------------------------------------------------------\n // Item mapping (the v2 camelCase vocabulary → protocol events)\n // -------------------------------------------------------------------------\n\n /** Tool calls surface as tool_use when they start; text and reasoning stream\n * natively via the delta notifications. */\n #handleItemProgress(item: AppServerItem, active: ActiveTurn): void {\n const id = `${active.nonce}:${item.id}`\n if (item.type === 'commandExecution' && !active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, 'CodexCommand', { command: item.command })\n return\n }\n if (item.type === 'mcpToolCall' && !active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, `mcp__${item.server}__${item.tool}`, item.arguments)\n return\n }\n // Generating a picture takes seconds — the card exists while it runs, like\n // a command's does, rather than appearing only once it is finished.\n if (item.type === 'imageGeneration' && !active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, CODEX_IMAGE_TOOL, imageGenerationInput(item))\n // Rare but real: a progress item can already carry `savedPath`. Announce\n // it here too — `#emitFileProduced` dedupes by path, so the completed\n // item's second report costs nothing.\n if (item.savedPath) this.#emitFileProduced(item.savedPath, id)\n }\n }\n\n #handleItemCompleted(item: AppServerItem, active: ActiveTurn): void {\n const id = `${active.nonce}:${item.id}`\n const handler = this.#itemCompleted[item.type] as\n | ((item: AppServerItem, active: ActiveTurn, id: string) => void)\n | undefined\n if (handler) {\n handler(item, active, id)\n return\n }\n // An item type the union does not model yet: passed through as an sdk_event\n // rather than dropped — an unmapped item must not be invisible.\n const unknown = item as AppServerUnknownItem\n this.#emit({ type: 'sdk_event', payload: { type: `codex.${unknown.type}`, item: unknown } })\n }\n\n /**\n * The completed-item mapping, one handler per member of the {@link AppServerItem}\n * union. The mapped type is the invariant made checkable: model a new item\n * type in `types.ts` and this table fails to compile until it says what the\n * item becomes on the wire — the old switch silently fell through to the\n * unknown-item passthrough instead. (The runtime still receives types the\n * union has never heard of; those take the passthrough above.)\n */\n readonly #itemCompleted: {\n [K in AppServerItem['type']]: (\n item: Extract<AppServerItem, { type: K }>,\n active: ActiveTurn,\n id: string,\n ) => void\n } = {\n // The echo of our own turn/start input — already in the log.\n userMessage: () => {},\n agentMessage: (item, active, id) => {\n const text = typeof item.text === 'string' ? item.text : ''\n this.#emitAssistant(id, [{ type: 'text', text }])\n active.finalText = text\n },\n reasoning: (item, _active, id) => {\n // `summary` is what streamed (the default config); raw `content` only\n // exists when the operator's config enables it. Joined the way the\n // deltas rendered: sections as paragraphs.\n const summary = Array.isArray(item.summary) ? item.summary.filter(Boolean) : []\n const content = Array.isArray(item.content) ? item.content.filter(Boolean) : []\n const thinking = (summary.length > 0 ? summary : content).join('\\n\\n')\n if (thinking) this.#emitAssistant(id, [{ type: 'thinking', thinking }])\n },\n commandExecution: (item, active, id) => {\n if (!active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, 'CodexCommand', { command: item.command })\n }\n const exitCode = item.exitCode ?? undefined\n const failed =\n item.status === 'failed' ||\n item.status === 'declined' ||\n (exitCode !== undefined && exitCode !== 0)\n const output =\n (item.aggregatedOutput ?? '') +\n (exitCode !== undefined && exitCode !== 0 ? `\\n(exit code ${exitCode})` : '')\n this.#emitToolResult(id, output, failed)\n },\n fileChange: (item, _active, id) => {\n // The completed item: by the time it lands the patch applied, failed,\n // or was declined (a pending proposal rides the approval channel, not\n // this item). v2's `kind` is an object (`{type: 'update', …}`), mapped\n // defensively.\n this.#emitToolUse(id, 'CodexFileChange', { changes: item.changes })\n const lines = item.changes.map((change) => {\n const kind = typeof change.kind === 'string' ? change.kind : change.kind?.type\n return `${kind ?? 'change'}: ${change.path}`\n })\n // Codex reports a unified diff per change, so the wire can carry the\n // same `FilePatch` the Claude engine sends and every client renders one\n // shape. Only for a single-file change: the patch names one file, and a\n // multi-file edit has no honest way to say which.\n const only = item.changes.length === 1 ? item.changes[0] : undefined\n this.#emitToolResult(\n id,\n lines.join('\\n') || item.status,\n item.status === 'failed' || item.status === 'declined',\n only?.diff ? parseUnifiedDiff(only.diff, only.path) : undefined,\n )\n },\n mcpToolCall: (item, active, id) => {\n if (!active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, `mcp__${item.server}__${item.tool}`, item.arguments)\n }\n const isError = (item.error !== undefined && item.error !== null) || item.status === 'failed'\n this.#emitToolResult(\n id,\n item.error?.message ??\n (item.result === undefined || item.result === null ? '' : JSON.stringify(item.result)),\n isError,\n )\n },\n webSearch: (item, _active, id) => {\n this.#emitToolUse(id, 'CodexWebSearch', { query: item.query })\n this.#emitToolResult(id, '', false)\n },\n imageGeneration: (item, active, id) => {\n // Re-emitted, not guarded by `toolUseEmitted`: `savedPath` only exists\n // now, and the reducer upserts a tool_use by id — so this replaces the\n // in-progress card's input with the finished one. The result event\n // follows immediately, which is what settles the status again.\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, CODEX_IMAGE_TOOL, imageGenerationInput(item))\n // The path IS the deliverable — the bytes live on the host and no event\n // may carry them. `file_produced` is what makes those bytes reachable\n // anyway: the gateway serves a path its own runner reported, with no\n // host-file root to declare first.\n if (item.savedPath) this.#emitFileProduced(item.savedPath, id)\n const lines = [\n item.savedPath ? `Saved to ${item.savedPath}` : 'No saved path reported',\n ...(shortResult(item.result) ? [item.result] : []),\n ]\n this.#emitToolResult(id, lines.join('\\n'), item.status === 'failed')\n },\n imageView: (item, _active, id) => {\n this.#emitToolUse(id, 'CodexImageView', { path: item.path })\n this.#emitToolResult(id, item.path, false)\n },\n }\n\n // -------------------------------------------------------------------------\n // Emission (the AiSdkRunner tool_result shape, so the reducer and both UIs\n // render their existing cards unchanged)\n // -------------------------------------------------------------------------\n\n #emitDelta(delta: { type: 'text_delta'; text: string } | { type: 'thinking_delta'; thinking: string }): void {\n if (this.#config.includePartialMessages === false) return\n this.#emit({\n type: 'stream_delta',\n event: { type: 'content_block_delta', delta },\n parentToolUseId: null,\n uuid: randomUUID(),\n })\n }\n\n #emitAssistant(uuid: string, content: ContentBlock[]): void {\n this.#emit({\n type: 'assistant_message',\n message: { role: 'assistant', content, model: this.#model ?? this.#resolvedModel },\n parentToolUseId: null,\n uuid,\n })\n }\n\n #emitToolUse(id: string, name: string, input: unknown): void {\n this.#emit({\n type: 'assistant_message',\n message: {\n role: 'assistant',\n content: [{ type: 'tool_use', id, name, input }],\n model: this.#model ?? this.#resolvedModel,\n },\n parentToolUseId: null,\n uuid: `${id}-use`,\n })\n }\n\n #emitToolResult(\n toolUseId: string,\n content: string,\n isError: boolean,\n patch?: FilePatch,\n ): void {\n this.#emit({\n type: 'user_message',\n message: {\n role: 'user',\n content: [\n { type: 'tool_result', tool_use_id: toolUseId, content, is_error: isError || undefined },\n ],\n },\n parentToolUseId: null,\n synthetic: true,\n patch,\n uuid: `${toolUseId}-result`,\n })\n }\n\n /**\n * Per-turn usage re-mapped to the Anthropic accounting convention the whole\n * stack assumes (GOTCHAS §Codex engine): OpenAI's `inputTokens` includes the\n * cached share, so input excludes it (else queue token budgets double-count\n * cache-heavy runs); reasoning tokens are billed output; `totalCostUsd: 0` =\n * unknown, the AiSdkRunner precedent. Usage is summed from the turn's\n * `thread/tokenUsage/updated` notifications — `turn/completed` carries none.\n */\n #finishTurn(\n kind: 'success' | 'failure',\n startedAt: number,\n active: ActiveTurn,\n errors?: string[],\n ): void {\n // Approvals that outlived the turn (codex moved on, or the turn failed\n // around them) are settled now — a card must never outlive what it gates,\n // and an unanswered timer must never fire into a finished turn.\n for (const [id, pending] of this.#approvals) {\n this.#settleApproval(id, pending, { behavior: 'deny', message: 'Turn ended' }, 'policy')\n }\n this.#numTurns += 1\n this.#totalCostUsd = 0\n const usage = active.sawUsage ? active.usage : undefined\n this.#emit({\n type: 'turn_result',\n subtype: kind === 'success' ? 'success' : 'error_during_execution',\n isError: kind !== 'success',\n durationMs: Date.now() - startedAt,\n numTurns: this.#numTurns,\n totalCostUsd: 0,\n result: kind === 'success' ? (active.finalText ?? '') : undefined,\n errors,\n usage: usage\n ? {\n input_tokens: Math.max(0, usage.inputTokens - usage.cachedInputTokens),\n output_tokens: usage.outputTokens + usage.reasoningOutputTokens,\n cache_creation_input_tokens: usage.cacheWriteInputTokens ?? 0,\n cache_read_input_tokens: usage.cachedInputTokens,\n }\n : undefined,\n })\n this.#emitContextUsage(active)\n this.#setStatus('idle')\n }\n\n /**\n * Subscription windows, mapped onto the protocol's named vocabulary.\n *\n * The shapes disagree: codex reports windows *positionally* (`primary` /\n * `secondary`) with a length in minutes, while `RateLimitInfo.rateLimitType`\n * is a name whose meaning clients already know — iOS labels `seven_day` as\n * \"Weekly\" and derives the pace marker's denominator from it. Naming the\n * window by its measured duration is therefore the honest mapping rather\n * than a borrowed one: codex's primary window is 10080 minutes, which *is*\n * seven days. A duration we have no name for keeps an explicit\n * `window_<n>m` key — clients render it verbatim and simply draw no pace\n * marker, which beats mislabeling it as a week.\n *\n * `status` is 'allowed' by construction (the session is running), matching\n * `rateLimitEventsFromUsage`; codex's `rateLimitReachedType` is the one\n * signal that a limit is actually biting, so it becomes 'rejected'.\n */\n #emitRateLimits(limits: AppServerRateLimits | undefined | null): void {\n if (!limits) return\n const status = limits.rateLimitReachedType ? 'rejected' : 'allowed'\n for (const window of [limits.primary, limits.secondary]) {\n // A window with no percentage is unknown, not zero — dropped rather than\n // reported at 0%, the same rule the Claude mapping follows.\n if (!window || window.usedPercent === null || window.usedPercent === undefined) continue\n this.#emit({\n type: 'rate_limit',\n info: {\n status,\n rateLimitType: rateLimitWindowName(window.windowDurationMins),\n utilization: window.usedPercent,\n ...(typeof window.resetsAt === 'number' ? { resetsAt: window.resetsAt } : {}),\n },\n })\n }\n // Emitted once per change, like the Claude engine's — it names the windows\n // rather than sizing them.\n if (limits.planType && limits.planType !== this.#planType) {\n this.#planType = limits.planType\n this.#emit({ type: 'plan_info', subscriptionType: limits.planType })\n }\n }\n\n /**\n * Context occupancy, after the turn — the same cadence the Claude runner\n * polls `getContextUsage()` on, so clients need nothing new.\n *\n * Emitted only when the binary gave BOTH numbers: the protocol is explicit\n * that a client renders nothing rather than a 0% ring, and a window of\n * `null` (which app-server does send) would otherwise divide into a\n * meaningless percentage. `categories` is empty because codex publishes no\n * breakdown — clients must not render an empty \"Breakdown\" section for it.\n */\n #emitContextUsage(active: ActiveTurn): void {\n const totalTokens = active.contextTokens\n const maxTokens = active.contextWindow\n if (totalTokens === undefined || !maxTokens || maxTokens <= 0) return\n this.#emit({\n type: 'context_usage',\n usage: {\n categories: [],\n totalTokens,\n maxTokens,\n percentage: Math.min(100, (totalTokens / maxTokens) * 100),\n model: this.#model ?? this.#resolvedModel,\n },\n })\n }\n\n #setStatus(status: SessionStatus, detail?: string): void {\n if (this.#status === status) return\n if (this.#status === 'closed' || this.#status === 'failed') return\n this.#status = status\n this.#emit({ type: 'status_changed', status, detail })\n }\n\n #emit(body: SessionEventBody): void {\n // History replay reuses the live item mapping wholesale; the replay flag\n // is stamped here so the mapping itself stays one code path.\n if (this.#replayingHistory && (body.type === 'assistant_message' || body.type === 'user_message')) {\n body = { ...body, replay: true }\n }\n const event: SessionEvent = { ...body, seq: ++this.#seq, ts: Date.now() }\n this.#lastActivityAt = event.ts\n // Rows, not events: what a client diffs to know how much it missed.\n this.#activityCount += transcriptActivity(body)\n this.#events.push(event)\n this.#subscribers.emit(event)\n }\n}\n","import type { ModelCatalog } from '../adapter.ts'\n\n/**\n * The Codex engine's model catalog, seeded from the binary's own embedded\n * presets — `@openai/codex@0.146.0` ships its model table inside the\n * executable, and that table (not the SDK's stale `ModelReasoningEffort`\n * union) is the truth about which reasoning efforts each model takes.\n *\n * **Refresh procedure** (release checklist): extract the embedded JSON from\n * the platform binary and diff —\n *\n * node -e 'const d=require(\"fs\").readFileSync(process.argv[1]);\n * const s=d.indexOf(`{\\n \"models\": [`);\n * let i=s,n=0; do{n+=(d[i]===123)-(d[i]===125);i++}while(n);\n * const c=JSON.parse(d.slice(s,i));\n * for(const m of c.models) console.log(m.slug, m.display_name,\n * m.visibility, m.supported_reasoning_levels.map(l=>l.effort).join(\",\"))'\\\n * \"$(node -p 'require.resolve(\"@openai/codex-darwin-arm64/package.json\").replace(\"package.json\",\"vendor/aarch64-apple-darwin/bin/codex\")')\"\n *\n * Mapping decisions:\n * - the internal `codex-auto-review` row is dropped (the codex analogue of\n * dropping the CLI's `default` sentinel);\n * - `primary` mirrors the binary's own `visibility` field ('list' = shown in\n * its picker, 'hide' = its \"older models\"), so both UIs group the way\n * codex's own picker does;\n * - `reasoningEfforts` carries `supported_reasoning_levels` verbatim — note\n * `max`/`ultra` beyond the SDK union; trust the binary, keep strings open.\n */\nexport const CODEX_CATALOG: ModelCatalog = {\n provenance:\n 'embedded model presets of @openai/codex@0.146.0 (darwin-arm64 binary), extracted 2026-08-05',\n models: [\n {\n value: 'gpt-5.6-sol',\n resolvedModel: 'gpt-5.6-sol',\n displayName: 'GPT-5.6 Sol',\n description: 'Latest frontier agentic coding model.',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max', 'ultra'],\n },\n {\n value: 'gpt-5.6-terra',\n resolvedModel: 'gpt-5.6-terra',\n displayName: 'GPT-5.6 Terra',\n description: 'Balanced agentic coding model for everyday work.',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max', 'ultra'],\n },\n {\n value: 'gpt-5.6-luna',\n resolvedModel: 'gpt-5.6-luna',\n displayName: 'GPT-5.6 Luna',\n description: 'Fast and affordable agentic coding model.',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],\n },\n {\n value: 'gpt-5.5',\n resolvedModel: 'gpt-5.5',\n displayName: 'GPT-5.5',\n description: 'Frontier model for complex coding, research, and real-world work.',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],\n },\n {\n value: 'gpt-5.4',\n resolvedModel: 'gpt-5.4',\n displayName: 'GPT-5.4',\n description: 'Strong model for everyday coding.',\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],\n },\n {\n value: 'gpt-5.4-mini',\n resolvedModel: 'gpt-5.4-mini',\n displayName: 'GPT-5.4 Mini',\n description: 'Small, fast, and cost-efficient model for simpler coding tasks.',\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],\n },\n {\n value: 'gpt-5.2',\n resolvedModel: 'gpt-5.2',\n displayName: 'GPT-5.2',\n description: 'Optimized for professional work and long-running agents.',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],\n },\n ],\n}\n","import { spawn } from 'node:child_process'\nimport { JsonRpcStdioConnection } from './jsonrpc.ts'\nimport type { AppServerConnection } from './types.ts'\n\n/** How much stderr to keep for the exit diagnostic. The binary logs startup\n * noise there; only the tail explains a death. */\nconst STDERR_TAIL_BYTES = 4096\n\n/**\n * Spawn one `codex app-server` child and frame JSON-RPC over its stdio — the\n * real {@link AppServerConnectFn}. The child's env is passed **complete**\n * (a provided spawn env replaces process.env, never merges with it), with the\n * profile's CODEX_HOME pin already applied by the runner.\n *\n * No spawn cwd: the working directory is a thread/turn parameter, and a cwd\n * that doesn't exist should fail the *turn* with codex's own error, not the\n * spawn.\n */\nexport function connectAppServer(options: {\n executable: string\n env: Record<string, string>\n}): AppServerConnection {\n const child = spawn(options.executable, ['app-server'], {\n env: options.env,\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n const rpc = new JsonRpcStdioConnection({ input: child.stdout, output: child.stdin })\n\n let stderrTail = ''\n child.stderr.on('data', (chunk: Buffer) => {\n stderrTail = (stderrTail + String(chunk)).slice(-STDERR_TAIL_BYTES)\n })\n\n let closeHandler: ((message: string) => void) | undefined\n let done = false\n const settle = (message: string) => {\n if (done) return\n done = true\n rpc.fail(message)\n closeHandler?.(message)\n }\n child.on('error', (error) => settle(`codex app-server failed to start: ${error.message}`))\n child.on('exit', (code, signal) => {\n const tail = stderrTail.trim()\n settle(\n `codex app-server exited (${signal ?? `code ${code}`})` +\n (tail ? `: ${tail.slice(-500)}` : ''),\n )\n })\n\n return {\n request: (method, params) => rpc.request(method, params),\n notify: (method, params) => rpc.notify(method, params),\n onNotification: (handler) => rpc.onNotification(handler),\n onRequest: (handler) => rpc.onRequest(handler),\n onClose: (handler) => {\n closeHandler = handler\n },\n close: () => {\n // Deliberate teardown: suppress the exit callback so a session close\n // doesn't read as a crash, then let SIGTERM end the child.\n done = true\n rpc.fail('codex app-server connection closed')\n child.kill()\n },\n }\n}\n","import { execFile } from 'node:child_process'\nimport { existsSync, realpathSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport {\n ENGINE_CAPABILITIES,\n PROTOCOL_VERSION,\n type ProfileInfo,\n type SdkSessionSummary,\n} from '@workerdeck/protocol'\nimport type { EngineAdapter, EngineAvailability } from '../adapter.ts'\nimport { CodexRunner } from './runner.ts'\nimport { CODEX_CATALOG } from './catalog.ts'\nimport { connectAppServer } from './process.ts'\nimport type { AppServerConnectFn, AppServerThreadListResponse, AppServerThreadSummary } from './types.ts'\n\nconst NOT_INSTALLED =\n '@openai/codex is not installed — add it (an optional peer of @workerdeck/core) to run codex profiles'\n\n/**\n * The codex binary sessions will run: the per-platform package installed next\n * to `@openai/codex`, `vendor/<target-triple>/bin/codex` — the same file the\n * npm wrapper's own `bin/codex.js` launcher execs. Probing this binary rather\n * than whatever `codex` is on PATH means the availability answer is about the\n * executable sessions will actually run. Undefined when it can't be found;\n * callers degrade to 'unknown'.\n */\nexport function resolveBundledCodexExecutable(): string | undefined {\n const triple = targetTriple()\n if (!triple) return undefined\n try {\n // Two hops on purpose (the claude-auth pattern): the platform package is a\n // dependency of @openai/codex, so under pnpm's strict layout it only\n // resolves from @openai/codex's own location, never from ours. Plain\n // createRequire throughout — neither package has an exports map.\n const fromHere = createRequire(import.meta.url)\n const wrapper = fromHere.resolve('@openai/codex/package.json')\n const fromWrapper = createRequire(wrapper)\n const platformPackage = fromWrapper.resolve(`@openai/codex-${platformPackageSuffix()}/package.json`)\n const path = platformPackage.replace(/package\\.json$/, `vendor/${triple}/bin/codex`)\n if (existsSync(path)) return path\n } catch {\n // not installed — nothing to probe\n }\n return undefined\n}\n\nfunction targetTriple(): string | undefined {\n const { platform, arch } = process\n if (platform === 'darwin') return arch === 'arm64' ? 'aarch64-apple-darwin' : 'x86_64-apple-darwin'\n if (platform === 'linux') {\n return arch === 'arm64' ? 'aarch64-unknown-linux-musl' : 'x86_64-unknown-linux-musl'\n }\n if (platform === 'win32') return 'x86_64-pc-windows-msvc'\n return undefined\n}\n\nfunction platformPackageSuffix(): string {\n return `${process.platform}-${process.arch}`\n}\n\n/**\n * Availability, mirroring **the app-server surface's actual credential chain**\n * (verified 2026-08-05 against 0.146.0 by driving the raw binary): auth comes\n * solely from the CODEX_HOME auth store (`codex login`, file or keyring). The\n * env-key routes are dead ends here — `CODEX_API_KEY` is read only by\n * `codex exec` (a turn goes out with no credential at all: \"Missing bearer\"),\n * and `OPENAI_API_KEY` was never read by either surface. So, in order:\n *\n * 1. Binary resolvable, else unavailable with the install reason;\n * 2. `codex login status` under the profile's complete session env:\n * exit 0 → available; the \"Not logged in\" verdict → unavailable, with an\n * exact remedy when a stranded env key explains the misconfiguration;\n * anything else (a stray CODEX_ACCESS_TOKEN JWT error, a crashed spawn) →\n * 'unknown' — the checkClaudeAuth never-overclaim discipline.\n *\n * Only the exit code and the fixed verdict line are consulted — never\n * surfaced: `login status` output includes a masked key fragment. The\n * `smoke:codex --canary` run is the drift alarm for all of this.\n */\nasync function checkCodexAvailability(\n profile: ProfileInfo,\n env: Record<string, string | undefined>,\n options: { timeoutMs?: number } = {},\n): Promise<EngineAvailability> {\n const executable = resolveBundledCodexExecutable()\n if (!executable) return { available: false, reason: NOT_INSTALLED }\n const childEnv: Record<string, string> = {}\n for (const [key, value] of Object.entries(env)) {\n if (value !== undefined) childEnv[key] = value\n }\n if (profile.codexHome) childEnv.CODEX_HOME = profile.codexHome\n return new Promise((resolve) => {\n execFile(\n executable,\n ['login', 'status'],\n { env: childEnv, timeout: options.timeoutMs ?? 10_000 },\n (error, stdout, stderr) => {\n if (!error) {\n resolve({ available: true })\n return\n }\n // The verdict line lands on stderr (0.146.0); check both streams so a\n // future move doesn't silently degrade every verdict to 'unknown'.\n if (`${stdout}\\n${stderr}`.includes('Not logged in')) {\n // Presence checks on the NAMES only; values are never read.\n const hint = childEnv.CODEX_API_KEY\n ? ' CODEX_API_KEY is read only by `codex exec`, never by the app-server — run ' +\n '`codex login --with-api-key` under this profile’s CODEX_HOME to persist it.'\n : childEnv.OPENAI_API_KEY\n ? ' OPENAI_API_KEY is not used by codex — run `codex login --with-api-key` ' +\n 'under this profile’s CODEX_HOME.'\n : ''\n resolve({\n available: false,\n reason:\n `codex is not logged in for this profile's environment — run \\`codex login\\`` +\n (profile.codexHome ? ` with CODEX_HOME=${profile.codexHome}` : '') +\n `.${hint}`,\n })\n return\n }\n // An errored probe (not a verdict) is not evidence of a missing login.\n resolve({ available: 'unknown' })\n },\n )\n })\n}\n\n/** `thread/list` page size (its own default is 25) and a hard page bound so a\n * misbehaving cursor can never spin the listing forever. */\nconst LIST_PAGE_SIZE = 100\nconst MAX_LIST_PAGES = 40\n\n/** thread/list's `cwd` filter is an EXACT path match (measured, 0.146.0), so\n * offer both the spelled and canonical forms — macOS listings would otherwise\n * miss `/tmp/...` threads recorded under `/private/tmp/...`. */\nfunction cwdFilter(dir: string): string[] {\n const forms = new Set([dir])\n try {\n forms.add(realpathSync(dir))\n } catch {\n // A directory that no longer exists still names its recorded threads.\n }\n return [...forms]\n}\n\nconst secondsToMs = (value: number | null | undefined): number | undefined =>\n typeof value === 'number' && Number.isFinite(value) ? value * 1000 : undefined\n\n/** One thread row in the protocol's browser-safe summary shape. `id` is what\n * `CreateSessionRequest.resume` feeds `thread/resume` — the row's separate\n * `sessionId` field is not it. */\nfunction summarizeThread(row: AppServerThreadSummary): SdkSessionSummary {\n const name = typeof row.name === 'string' && row.name.length > 0 ? row.name : undefined\n const preview = typeof row.preview === 'string' && row.preview.length > 0 ? row.preview : undefined\n return {\n sessionId: row.id,\n summary: name ?? preview ?? row.id,\n lastModified: secondsToMs(row.updatedAt) ?? secondsToMs(row.createdAt) ?? 0,\n createdAt: secondsToMs(row.createdAt),\n customTitle: name,\n firstPrompt: preview,\n gitBranch:\n typeof row.gitInfo?.branch === 'string' && row.gitInfo.branch.length > 0\n ? row.gitInfo.branch\n : undefined,\n cwd: typeof row.cwd === 'string' ? row.cwd : undefined,\n }\n}\n\n/**\n * CODEX_HOME's threads over ONE short-lived `codex app-server` child: the\n * runner's own handshake (`experimentalApi` and all — one code path, no\n * second vocabulary to drift), `thread/list` pages walked by cursor, child\n * closed before returning. Requires no live session and costs no tokens —\n * it is how \"resume\" is offered before anything is running. The `connectFn`\n * seam exists for the scripted-peer tests; the adapter passes the real\n * spawn.\n */\nexport async function listCodexSessions(options: {\n connectFn: AppServerConnectFn\n profile?: ProfileInfo\n env: Record<string, string | undefined>\n dir?: string\n limit?: number\n offset?: number\n}): Promise<SdkSessionSummary[]> {\n const childEnv: Record<string, string> = {}\n for (const [key, value] of Object.entries(options.env)) {\n if (value !== undefined) childEnv[key] = value\n }\n if (options.profile?.codexHome) childEnv.CODEX_HOME = options.profile.codexHome\n const connection = options.connectFn({ env: childEnv })\n const rows: AppServerThreadSummary[] = []\n try {\n await connection.request('initialize', {\n clientInfo: {\n name: 'workerdeck',\n title: 'WorkerDeck',\n version: `protocol-${PROTOCOL_VERSION}`,\n },\n capabilities: { experimentalApi: true },\n })\n connection.notify('initialized')\n // Newest-first by *update* time — `lastModified` is the field the pickers\n // sort and render, and codex's own default sort is by creation.\n const base: Record<string, unknown> = {\n limit: LIST_PAGE_SIZE,\n sortKey: 'updated_at',\n ...(options.dir ? { cwd: cwdFilter(options.dir) } : {}),\n }\n const want = options.limit === undefined ? undefined : (options.offset ?? 0) + options.limit\n let cursor: string | undefined\n for (let page = 0; page < MAX_LIST_PAGES; page++) {\n const result = (await connection.request('thread/list', {\n ...base,\n ...(cursor ? { cursor } : {}),\n })) as AppServerThreadListResponse\n const data = Array.isArray(result?.data) ? result.data : []\n rows.push(...data)\n if (want !== undefined && rows.length >= want) break\n if (data.length === 0 || typeof result?.nextCursor !== 'string') break\n cursor = result.nextCursor\n }\n } finally {\n connection.close()\n }\n const summaries = rows\n // An ephemeral thread was never materialized on disk — nothing to resume.\n .filter((row) => typeof row.id === 'string' && row.id.length > 0 && !row.ephemeral)\n .map(summarizeThread)\n const start = options.offset ?? 0\n return options.limit === undefined ? summaries.slice(start) : summaries.slice(start, start + options.limit)\n}\n\n/**\n * OpenAI Codex as an engine: the codex CLI binary driven over its `app-server`\n * JSON-RPC surface — structurally the Claude engine's sibling (a local agent\n * binary with sessions, sandboxing and resume, resolving its own credentials\n * from the operator's environment). `@openai/codex` — the npm package that\n * carries the binary — is an **optional peer**: absent, every codex profile\n * reports unavailable and createRunner throws the same message, and no\n * consumer downloads a ~40 MB per-platform binary it never uses.\n */\nexport const codexAdapter: EngineAdapter = {\n engine: 'codex',\n capabilities: ENGINE_CAPABILITIES.codex,\n catalog: CODEX_CATALOG,\n checkAvailability: (profile, env) => checkCodexAvailability(profile, env),\n createRunner({ config, profile, restore, id }) {\n if (restore) throw new Error('the codex engine cannot rebuild a parked session')\n const executable =\n (config as { codexPathOverride?: string }).codexPathOverride ??\n resolveBundledCodexExecutable()\n if (!executable) throw new Error(NOT_INSTALLED)\n return new CodexRunner(\n {\n ...config,\n codexHome: profile?.codexHome,\n connectFn: (options) => connectAppServer({ executable, ...options }),\n },\n id,\n )\n },\n async listSessions(options) {\n const executable = resolveBundledCodexExecutable()\n if (!executable) throw new Error(NOT_INSTALLED)\n return listCodexSessions({\n ...options,\n connectFn: (connect) => connectAppServer({ executable, ...connect }),\n })\n },\n}\n","import { ENGINE_CAPABILITIES } from '@workerdeck/protocol'\nimport type { EngineAdapter } from '../adapter.ts'\n\n/**\n * The model-agnostic provider engine as a pseudo-adapter: capabilities and an\n * env-var probe live here, but its runners are assembled by the host's\n * `createEngineRunner` hook (which is where provider credentials are resolved\n * and model SDKs are imported — neither belongs in this repo's import graph).\n * The server routes provider creates to the hook; `createRunner` here throws\n * so a mis-routed call fails loudly instead of quietly building nothing.\n *\n * The catalog is empty by the same token: provider model ids are operator-\n * declared per profile (`provider.models`), not shipped with releases.\n */\nexport const providerAdapter: EngineAdapter = {\n engine: 'provider',\n capabilities: ENGINE_CAPABILITIES.provider,\n catalog: { models: [], provenance: 'provider model ids are operator-declared (provider.models)' },\n async checkAvailability(profile, env) {\n const keyEnv = profile.provider?.apiKeyEnv\n // No declared key variable = nothing this probe can check (the host hook\n // may resolve credentials some other way) — unknown, not unavailable.\n if (!keyEnv) return { available: 'unknown' }\n const value = env[keyEnv]\n if (value !== undefined && value !== '') return { available: true }\n return {\n available: false,\n reason: `${keyEnv} is not set in the server environment (profile '${profile.name}' names it as apiKeyEnv)`,\n }\n },\n createRunner() {\n throw new Error(\n \"provider-engine runners are built by the host's createEngineRunner hook, not the adapter\",\n )\n },\n}\n","import type {\n EngineCapabilities,\n ModelOption,\n ProfileEngine,\n ProfileInfo,\n SdkSessionSummary,\n} from '@workerdeck/protocol'\nimport type { Runner, RunnerSnapshot } from '../runner-interface.ts'\nimport type { SessionRunnerConfig } from './claude/runner.ts'\n\n/**\n * A probe's verdict on one profile's credentials. 'unknown' means the probe\n * could not run at all — which is NOT evidence of a missing login and must\n * never be surfaced as one (the `checkClaudeAuth` discipline, generalized).\n */\nexport type EngineAvailability =\n | { available: true }\n | { available: false; reason: string }\n | { available: 'unknown' }\n\n/**\n * A model catalog shipped with the release — the answer to \"what can a create\n * form offer\" with no process spawned, correct from a gateway's first request.\n *\n * Never contains a 'default' sentinel row (a choice, not a model — forms add\n * their own \"Profile default\" row mapping to an unset model). Staleness is\n * bounded by the release cadence: the release checklist re-runs each catalog's\n * extraction procedure (documented in its file header) and diffs.\n */\nexport type ModelCatalog = {\n models: ModelOption[]\n /** Source + date, for the release-checklist refresh. Not served. */\n provenance: string\n}\n\nexport type EngineRunnerRequest = {\n config: SessionRunnerConfig\n profile?: ProfileInfo\n /** Rebuild a parked session instead of starting fresh. Engines that cannot\n * rehydrate throw. */\n restore?: RunnerSnapshot\n /**\n * Adopt this session id instead of minting one. For rehydrating a session\n * across a gateway restart: the transcript comes back from the *engine's* own\n * store via `config.resume`, but every client keys its watermarks, unread\n * counts and routes on the WorkerDeck id, so that id has to survive too\n * (`SessionInfo.id` is documented as stable across resumes). A `restore`\n * carries its own id in the snapshot and does not need this.\n */\n id?: string\n}\n\n/**\n * One engine, as the server consumes it: its capability record, its shipped\n * model catalog, a credential probe, and a runner factory. The claude adapter\n * wraps `SessionRunner` without behaviour change; the codex adapter owns the\n * `codex app-server` integration; the provider adapter is a pseudo-adapter —\n * its runners are built by the host's `createEngineRunner` hook, so its\n * `createRunner` throws and the server routes around it.\n */\nexport interface EngineAdapter {\n readonly engine: ProfileEngine\n /** Must deep-equal ENGINE_CAPABILITIES[engine] — asserted by a core test, so\n * the protocol's browser-safe defaults can never drift from the adapter. */\n readonly capabilities: EngineCapabilities\n readonly catalog: ModelCatalog\n /**\n * Probe whether `profile`'s credentials are usable under `env` — the full\n * session environment the real assembly path produces, never a delta (codex\n * replaces the child env wholesale, and a delta would strand HOME/PATH and\n * the auth chain with it). Never rejects.\n */\n checkAvailability(\n profile: ProfileInfo,\n env: Record<string, string | undefined>,\n ): Promise<EngineAvailability>\n /** Build a Runner. Throwing fails the create (session POST 500s, job fails). */\n createRunner(request: EngineRunnerRequest): Runner | Promise<Runner>\n /**\n * List the engine's on-disk resumable sessions (`GET /sdk-sessions`) —\n * present exactly when the capability record's `listSessions` is true. Must\n * not require a live session: the codex adapter answers over a short-lived\n * `thread/list` app-server child it closes before returning; the claude\n * adapter reads the Agent SDK's store directly. `env` follows the\n * checkAvailability contract (the profile's complete session environment,\n * never a delta). `dir` narrows to one project directory; `limit`/`offset`\n * page the newest-first result.\n */\n listSessions?(options: {\n profile?: ProfileInfo\n env: Record<string, string | undefined>\n dir?: string\n limit?: number\n offset?: number\n }): Promise<SdkSessionSummary[]>\n}\n\nimport { claudeAdapter } from './claude/adapter.ts'\nimport { codexAdapter } from './codex/adapter.ts'\nimport { providerAdapter } from './provider/adapter.ts'\n\nconst ADAPTERS: Record<ProfileEngine, EngineAdapter> = {\n claude: claudeAdapter,\n codex: codexAdapter,\n provider: providerAdapter,\n}\n\n/** The in-repo adapter for an engine. An absent `engine` means 'claude'. */\nexport function getEngineAdapter(engine: ProfileEngine | undefined): EngineAdapter {\n return ADAPTERS[engine ?? 'claude']\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["#done","#waiter","#buffer","#listeners","#recordFor","#open","#settle","#records","#sweep","#settleCounter","DEFAULT_APPROVAL_TIMEOUT_MS","#cwd","#config","#permissionMode","#status","#sdkSessionId","#seq","#apiKeySource","#pending","#model","#activityCount","#contextUsage","#subagents","#title","#totalCostUsd","#numTurns","#lastActivityAt","#engineTitle","#started","#runPromise","#run","#closed","#input","#emit","#query","#settleApproval","#setStatus","#events","#subscribers","#resetSeq","sdkQuery","#backfillHistory","#buildOptions","#fetchCapabilities","#fetchContextUsage","#fetchRateLimits","#handleMessage","#canUseTool","#turnOverWhileBlocked","#fetchEngineTitle","#capabilitiesEmitted","#subscriptionType","#resolveQuestionByPolicy","#statusDetail","#config","#model","#permissionMode","#modelAlias","#restore","#seq","#events","#activityCount","#contextUsage","#messages","#pendingToolCalls","#dispatched","#numTurns","#totalUsage","#turnAccum","#lastActivityAt","#status","#modelId","#title","#started","#turnChain","#setStatus","#closed","#parked","#abort","#restingOnDeferred","#buildSnapshot","#subscribers","#emit","#scheduleTurn","#settlePendingCall","#runTurn","#applyExecutionResult","#announceParked","#dispatchPending","#finishTurn","#options","#execute","#allowsNetwork","#fetchText","#slots","#settle","#options","#early","#applyAnswer","#options","sdkListSessions","#output","#trace","#feed","#closed","#nextId","#pending","#write","#notificationHandler","#requestHandler","#buffer","#traceLine","#dispatch","#byThread","#settleCounter","#settle","#cwd","#config","#permissionMode","#model","#reasoningEffort","#sdkSessionId","#status","#seq","#approvals","#resolvedModel","#activityCount","#contextUsage","#title","#totalCostUsd","#numTurns","#lastActivityAt","#agents","#started","#turnChain","#warnUntrustedProject","#backfillPending","#backfillHistory","#setStatus","#probeSkills","#childEnv","#emit","#openScratchConnection","#closed","#refreshSkills","#buildInput","#queue","#scheduleTurn","#imageDir","#settleApproval","#interruptTurn","#activeTurn","#connection","#events","#subscribers","#runTurn","#readWorkspaceWrite","#workspaceWrite","#ensureThread","#threadLoaded","#handleNotification","#answerServerRequest","#resolvedEffort","#resumedHistory","#skillsRefresh","#skillsFingerprint","#mcpStatus","#producedPaths","#replayTurns","#newTurnState","#replayingHistory","#handleItemCompleted","#turnSandboxPolicy","#finishTurn","#isRootThread","#settleAgentTurn","#threadIdOf","#notifications","#emitToolUse","#emitToolResult","#emitDelta","#agentFor","#handleItemProgress","#itemProgress","#reasoningDelta","#emitRateLimits","#requestApproval","#resolveQuestionByPolicy","#itemCompleted","#emitFileProduced","#emitAssistant","#emitContextUsage","#planType"],"sources":["../src/lib/attachments.ts","../src/lib/input-queue.ts","../src/lib/patch.ts","../src/lib/normalize.ts","../src/lib/replay.ts","../src/lib/subscribers.ts","../src/engines/claude/subagents.ts","../src/engines/claude/runner.ts","../src/engines/provider/runner.ts","../src/engines/claude/auth.ts","../src/executors/quickjs-executor.ts","../src/lib/pending-registry.ts","../src/executors/browser-bridge-executor.ts","../src/executors/deferred-executor.ts","../src/engines/provider/tools.ts","../src/engines/provider/web-fetch.ts","../src/engines/provider/session.ts","../src/engines/claude/catalog.ts","../src/engines/claude/adapter.ts","../src/engines/codex/jsonrpc.ts","../src/engines/codex/subagents.ts","../src/engines/codex/trust.ts","../src/engines/codex/runner.ts","../src/engines/codex/catalog.ts","../src/engines/codex/process.ts","../src/engines/codex/adapter.ts","../src/engines/provider/adapter.ts","../src/engines/adapter.ts"],"sourcesContent":["import type { MessageAttachment } from '@workerdeck/protocol'\n\n/**\n * An attachment plus its bytes — what the host hands a runner at send time.\n *\n * The split matters: `data` goes into the message the engine sends and nowhere\n * else. What the runner emits into the seq-numbered event log is the\n * {@link MessageAttachment} half, so replay and parking stay cheap (see the\n * protocol's note on why the bytes are not on the wire).\n */\nexport type AttachmentInput = MessageAttachment & {\n /** Base64, no data-URL prefix. */\n data: string\n}\n\n/**\n * How an attachment reaches the model. Not every file can be handed to a model\n * as itself: images and PDFs have native block types, anything textual can be\n * inlined, and the rest has no representation at all — so uploads of it are\n * refused at the door rather than silently dropped from the message.\n */\nexport type AttachmentKind = 'image' | 'document' | 'text'\n\n/** The four the Anthropic API accepts. Notably absent: image/heic — an iPhone's\n * native photo format, which clients must transcode before upload. */\nconst IMAGE_TYPES = new Set(['image/jpeg', 'image/png', 'image/gif', 'image/webp'])\n\n/** Textual types whose media type doesn't start with `text/`. */\nconst TEXT_TYPES = new Set([\n 'application/json',\n 'application/xml',\n 'application/yaml',\n 'application/x-yaml',\n 'application/toml',\n 'application/javascript',\n 'application/typescript',\n 'application/x-sh',\n 'application/x-httpd-php',\n 'application/sql',\n])\n\n/** Strips any `; charset=…` parameter and lowercases. */\nexport function normalizeMediaType(mediaType: string): string {\n return mediaType.split(';')[0]!.trim().toLowerCase()\n}\n\n/** How this media type can be sent, or null if it can't be. */\nexport function attachmentKind(mediaType: string): AttachmentKind | null {\n const type = normalizeMediaType(mediaType)\n if (IMAGE_TYPES.has(type)) return 'image'\n if (type === 'application/pdf') return 'document'\n if (type.startsWith('text/') || TEXT_TYPES.has(type)) return 'text'\n return null\n}\n\n/** Human-readable list for the 415 an unsupported upload gets. */\nexport const SUPPORTED_ATTACHMENT_TYPES = [...IMAGE_TYPES, 'application/pdf', 'text/*'].join(', ')\n\n/**\n * Anthropic content blocks for a set of attachments, in the given order.\n *\n * Blocks lead the message and the user's text follows: the model reads the\n * picture, then the instruction about it. Text files are inlined in a named\n * envelope rather than as a bare block, so \"here is my config\" doesn't read as\n * something the user typed.\n *\n * Structurally typed — `packages/core` models Anthropic content the way\n * `packages/protocol` does, and the caller casts into the SDK's own param type.\n */\nexport function attachmentContentBlocks(\n attachments: readonly AttachmentInput[],\n): Array<Record<string, unknown>> {\n return attachments.map((attachment) => {\n const mediaType = normalizeMediaType(attachment.mediaType)\n switch (attachmentKind(mediaType)) {\n case 'image':\n return {\n type: 'image',\n source: { type: 'base64', media_type: mediaType, data: attachment.data },\n }\n case 'document':\n return {\n type: 'document',\n source: { type: 'base64', media_type: mediaType, data: attachment.data },\n title: attachment.name,\n }\n case 'text':\n return {\n type: 'text',\n text: `<attachment name=\"${attachment.name}\" type=\"${mediaType}\">\\n${decodeText(attachment.data)}\\n</attachment>`,\n }\n default:\n throw new Error(`unsupported attachment media type: ${attachment.mediaType}`)\n }\n })\n}\n\n/** Strip the bytes: the log-safe half of an attachment. */\nexport function attachmentRef(attachment: AttachmentInput): MessageAttachment {\n return {\n id: attachment.id,\n name: attachment.name,\n mediaType: attachment.mediaType,\n bytes: attachment.bytes,\n }\n}\n\nfunction decodeText(base64: string): string {\n return Buffer.from(base64, 'base64').toString('utf8')\n}\n","import type { SDKUserMessage } from '@anthropic-ai/claude-agent-sdk'\n\n/**\n * Push-based single-consumer AsyncIterable bridging imperative sendMessage() calls\n * into the streaming `prompt` the Agent SDK consumes.\n */\nexport class InputQueue implements AsyncIterable<SDKUserMessage> {\n #buffer: SDKUserMessage[] = []\n #waiter: ((result: IteratorResult<SDKUserMessage>) => void) | null = null\n #done = false\n\n push(message: SDKUserMessage): void {\n if (this.#done) return\n if (this.#waiter) {\n const resolve = this.#waiter\n this.#waiter = null\n resolve({ value: message, done: false })\n } else {\n this.#buffer.push(message)\n }\n }\n\n end(): void {\n if (this.#done) return\n this.#done = true\n if (this.#waiter) {\n const resolve = this.#waiter\n this.#waiter = null\n resolve({ value: undefined, done: true })\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<SDKUserMessage> {\n return {\n next: (): Promise<IteratorResult<SDKUserMessage>> => {\n const buffered = this.#buffer.shift()\n if (buffered !== undefined) return Promise.resolve({ value: buffered, done: false })\n if (this.#done) return Promise.resolve({ value: undefined, done: true })\n return new Promise((resolve) => {\n this.#waiter = resolve\n })\n },\n return: (): Promise<IteratorResult<SDKUserMessage>> => {\n this.end()\n return Promise.resolve({ value: undefined, done: true })\n },\n }\n }\n}\n","import type { FilePatch, PatchHunk } from '@workerdeck/protocol'\n\n/**\n * Turning an engine's edit output into the wire's {@link FilePatch}.\n *\n * Both engines know exactly which lines of which file changed, and both say so\n * in their own vocabulary: the Claude SDK hands over a `structuredPatch` array\n * on `SDKUserMessage.tool_use_result`, codex puts a unified diff string on each\n * `fileChange` item. A client can reconstruct neither — it has never seen the\n * file — so anything not normalized here is a diff that renders without line\n * numbers.\n *\n * Normalizing in the runner rather than in each client is the point: one shape\n * reaches the wire, and the dashboard, the extension and the phone all render\n * from it without a per-engine branch or a diff parser of their own.\n */\n\n/**\n * The most lines a patch may put on the wire.\n *\n * A patch is replayed on every attach and captured into parking snapshots, so\n * \"the diff is big\" must not become \"this session is expensive to open forever\".\n * Whole hunks are kept or dropped — half a hunk has misleading line numbers —\n * and the drop is flagged so a renderer can say the diff is partial instead of\n * presenting it as the whole change.\n */\nconst MAX_PATCH_LINES = 400\n\nfunction capHunks(hunks: PatchHunk[]): { hunks: PatchHunk[]; truncated?: boolean } {\n const kept: PatchHunk[] = []\n let lines = 0\n for (const hunk of hunks) {\n if (lines + hunk.lines.length > MAX_PATCH_LINES && kept.length > 0) {\n return { hunks: kept, truncated: true }\n }\n kept.push(hunk)\n lines += hunk.lines.length\n }\n return { hunks: kept }\n}\n\n/** Structural, not `instanceof`: this reads a field the SDK types as `unknown`,\n * and a shape check is the only honest way to know what arrived. */\nfunction isHunk(value: unknown): value is PatchHunk {\n const hunk = value as Partial<PatchHunk> | null\n return (\n !!hunk &&\n typeof hunk.oldStart === 'number' &&\n typeof hunk.oldLines === 'number' &&\n typeof hunk.newStart === 'number' &&\n typeof hunk.newLines === 'number' &&\n Array.isArray(hunk.lines) &&\n hunk.lines.every((line) => typeof line === 'string')\n )\n}\n\n/**\n * A {@link FilePatch} from the Claude SDK's structured tool output\n * (`SDKUserMessage.tool_use_result` for Edit/Write/NotebookEdit).\n *\n * Everything else on that object is deliberately left behind — `originalFile`\n * alone is the entire pre-edit file, which is precisely what must not be logged\n * (see `FilePatch`'s own note).\n */\nexport function filePatchFromToolResult(result: unknown): FilePatch | undefined {\n const output = result as\n | { filePath?: unknown; structuredPatch?: unknown; originalFile?: unknown; type?: unknown }\n | null\n | undefined\n if (!output || !Array.isArray(output.structuredPatch)) return undefined\n const hunks = output.structuredPatch.filter(isHunk)\n if (hunks.length === 0) return undefined\n const { hunks: kept, truncated } = capHunks(hunks)\n return {\n ...(typeof output.filePath === 'string' && { path: output.filePath }),\n // Write reports `type: 'create' | 'update'` directly. Edit has no such\n // field, but `originalFile` answers the same question: null means there was\n // no file to edit. Absent entirely (neither field) leaves `kind` unset\n // rather than assuming an update.\n ...(output.type === 'create' || output.originalFile === null\n ? ({ kind: 'create' } as const)\n : output.type === 'update' || typeof output.originalFile === 'string'\n ? ({ kind: 'update' } as const)\n : {}),\n hunks: kept,\n ...(truncated && { truncated }),\n }\n}\n\n/** `@@ -oldStart,oldLines +newStart,newLines @@` — the counts are optional and\n * mean 1 when absent, which is what a single-line hunk looks like. */\nconst HUNK_HEADER = /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@/\n\n/**\n * A {@link FilePatch} from a unified diff — codex's `fileChange.diff`.\n *\n * Only the hunks are read. A diff's `---`/`+++` header names the file, but codex\n * already reports the path on the change itself, and a header path is often\n * relative or `/dev/null`, so the caller's path is the one worth trusting.\n *\n * Returns undefined when there is no hunk header at all: that is not a unified\n * diff, and inventing hunk numbers for it would put wrong line numbers on screen\n * — worse than none.\n */\nexport function parseUnifiedDiff(diff: string, path?: string): FilePatch | undefined {\n const hunks: PatchHunk[] = []\n let current: PatchHunk | undefined\n for (const line of diff.split('\\n')) {\n const header = HUNK_HEADER.exec(line)\n if (header) {\n current = {\n oldStart: Number(header[1]),\n oldLines: header[2] === undefined ? 1 : Number(header[2]),\n newStart: Number(header[3]),\n newLines: header[4] === undefined ? 1 : Number(header[4]),\n lines: [],\n }\n hunks.push(current)\n continue\n }\n if (!current) continue\n // Inside a hunk, a line belongs to it when it carries a diff prefix. A\n // '\\' line (\"\\") is a note about the previous\n // line, not a line of the file, and is dropped.\n if (line.startsWith(' ') || line.startsWith('-') || line.startsWith('+')) {\n current.lines.push(line)\n } else if (line === '') {\n // An empty line in a diff body is a context line whose trailing space was\n // stripped somewhere between the engine and here — common enough that\n // dropping it would silently shift every line number after it.\n current.lines.push(' ')\n } else {\n current = undefined\n }\n }\n if (hunks.length === 0) return undefined\n const { hunks: kept, truncated } = capHunks(hunks)\n return { ...(path && { path }), hunks: kept, ...(truncated && { truncated }) }\n}\n","import type { McpServerStatus, SDKMessage } from '@anthropic-ai/claude-agent-sdk'\nimport type {\n ApiMessage,\n ContentBlock,\n McpServerStatusInfo,\n ModelOption,\n SessionEventBody,\n TextBlock,\n} from '@workerdeck/protocol'\nimport { filePatchFromToolResult } from './patch.ts'\n\n/** Does this message answer exactly one tool call? A patch is per-file-edit and\n * the message says nothing about which of two results it describes, so anything\n * else gets no patch rather than a diff pinned to the wrong call. */\nfunction singleToolResult(message: ApiMessage): boolean {\n const content = message.content\n if (!Array.isArray(content)) return false\n return content.filter((block) => block.type === 'tool_result').length === 1\n}\n\n/**\n * The wrappers the CLI writes into the transcript when the *harness* is talking\n * to the model rather than a person talking to the session.\n *\n * Deliberately a text test, and only these two. The live path has structure to\n * go on (`isSynthetic`, `origin.kind`), but **the resumed path has none**: the\n * SDK's `SessionMessage` carries exactly `message`, `uuid`, `session_id`,\n * `parent_tool_use_id`, `parent_agent_id` and `timestamp` — every one of\n * `isMeta`, `isSidechain`, `promptSource` and `origin` is dropped between the\n * stored JSONL and what `getSessionMessages` hands back (verified against real\n * transcripts). So on resume this is the only signal there is, and without it a\n * `<task-notification>` blob comes back as a blue user row and a scrubber mark,\n * as if someone had typed it.\n *\n * `<local-command-caveat>` is here for symmetry and cheap insurance: the SDK\n * filters `isMeta` entries out of a resumed transcript itself today, which is\n * not a contract anyone wrote down.\n *\n * What is *not* here matters as much:\n * - `<local-command-stdout>` — the reducer turns it into a notice row on\n * purpose; marking it synthetic would delete a row both paths show.\n * - `<command-name>` — that is a person running a slash command. The reducer\n * renders it as the command line they typed; hiding it would erase the turn's\n * cause.\n */\nconst SYNTHETIC_USER_PREFIXES = ['<task-notification>', '<local-command-caveat>']\n\n/** First text block's leading tag, for the test above. Tool results and images\n * carry no text and are never synthetic by this rule (a tool result is already\n * a tool result to every renderer). */\nexport function isSyntheticUserText(message: ApiMessage): boolean {\n const content = message.content\n const text =\n typeof content === 'string'\n ? content\n : Array.isArray(content)\n ? content.find((block): block is TextBlock => block.type === 'text')?.text\n : undefined\n if (typeof text !== 'string') return false\n const head = text.trimStart()\n return SYNTHETIC_USER_PREFIXES.some((prefix) => head.startsWith(prefix))\n}\n\nexport function toApiMessage(message: unknown): ApiMessage {\n const m = message as {\n role?: 'user' | 'assistant'\n content: string | ContentBlock[]\n model?: string\n stop_reason?: string | null\n usage?: ApiMessage['usage']\n }\n return {\n role: m.role ?? 'assistant',\n content: m.content,\n model: m.model,\n stop_reason: m.stop_reason,\n usage: m.usage,\n }\n}\n\n/** The half of the CLI's `/usage` response this package reads. Structurally typed\n * rather than imported: the SDK marks the control request experimental and its\n * method name says so out loud, so the runner probes for it at runtime and this\n * describes only the fields it needs. */\nexport type UsageRateLimits = {\n /** 'pro' | 'max' | 'team' | 'enterprise', or null for API-key / 3P sessions. */\n subscription_type?: string | null\n rate_limits_available?: boolean\n rate_limits?: {\n five_hour?: UsageWindow\n seven_day?: UsageWindow\n seven_day_opus?: UsageWindow\n seven_day_sonnet?: UsageWindow\n seven_day_oauth_apps?: UsageWindow\n model_scoped?: Array<{ display_name: string; utilization: number | null }>\n } | null\n}\n\ntype UsageWindow = { utilization: number | null; resets_at?: string | null } | null | undefined\n\n/**\n * Plan rate-limit windows from the CLI's structured `/usage` data, as `rate_limit`\n * events — the same shape a live `rate_limit_event` produces.\n *\n * Without this a client shows no usage at all until a window *changes*, which the\n * CLI only reports after a turn moves the needle, and never for a session that is\n * only being watched. Polling the snapshot and forwarding it through the existing\n * event means replay, the dashboard and the iOS app all get it for free, with no\n * new protocol surface.\n *\n * `status` is not per-window in the usage payload — 'allowed' is what a session\n * the CLI is running for us is, by construction. A window with no utilization is\n * unknown, not zero, and is dropped rather than reported at 0%.\n */\nexport function rateLimitEventsFromUsage(usage: UsageRateLimits): SessionEventBody[] {\n if (!usage.rate_limits_available || !usage.rate_limits) return []\n const limits = usage.rate_limits\n const events: SessionEventBody[] = []\n const seen = new Set<string>()\n const push = (rateLimitType: string, window: UsageWindow): void => {\n if (!window || window.utilization === null || seen.has(rateLimitType)) return\n seen.add(rateLimitType)\n const resetsAt = window.resets_at ? Date.parse(window.resets_at) : NaN\n events.push({\n type: 'rate_limit',\n info: {\n status: 'allowed',\n rateLimitType,\n utilization: window.utilization,\n ...(Number.isFinite(resetsAt) ? { resetsAt: resetsAt / 1000 } : {}),\n },\n })\n }\n push('five_hour', limits.five_hour)\n push('seven_day', limits.seven_day)\n push('seven_day_opus', limits.seven_day_opus)\n push('seven_day_sonnet', limits.seven_day_sonnet)\n push('seven_day_oauth_apps', limits.seven_day_oauth_apps)\n // Server-driven per-model buckets, keyed off their display name so a client that\n // groups on the `seven_day_` prefix keeps them with the other weekly windows.\n for (const bucket of limits.model_scoped ?? []) {\n const slug = bucket.display_name.trim().toLowerCase().replace(/[^a-z0-9]+/g, '_')\n if (slug) push(`seven_day_${slug}`, bucket)\n }\n return events\n}\n\n/**\n * The CLI's MCP status, as `McpServerStatusInfo`.\n *\n * The narrowing is the point: the SDK's config object carries `env` for stdio\n * servers and `headers` for HTTP ones, and both routinely hold API tokens. This\n * is the one place they are dropped, so no client — dashboard, phone, or a host\n * app reading the REST route — can turn \"show me my MCP servers\" into a\n * credential dump. Only the connection's identity survives.\n */\nexport function mcpStatusInfo(status: McpServerStatus): McpServerStatusInfo {\n const config = status.config as\n | { type?: string; command?: string; args?: string[]; url?: string }\n | undefined\n // stdio is the CLI's implicit default: a config with a command and no type.\n const transport = config?.type ?? (config?.command ? 'stdio' : undefined)\n return {\n name: status.name,\n status: status.status,\n scope: status.scope,\n error: status.error,\n serverInfo: status.serverInfo,\n transport:\n transport === 'stdio' || transport === 'http' || transport === 'sse' || transport === 'sdk'\n ? transport\n : undefined,\n command: config?.command,\n args: config?.args,\n url: config?.url,\n tools: status.tools?.map((tool) => ({\n name: tool.name,\n description: tool.description,\n annotations: tool.annotations,\n })),\n }\n}\n\n/** The half of the SDK's `ModelInfo` this package forwards. Structurally typed so\n * the mapping can be unit-tested without a live query. */\nexport type SdkModelInfo = {\n value: string\n resolvedModel?: string\n displayName: string\n description?: string\n /** Per-model reasoning efforts, when the SDK reports them (0.3.221+). */\n supportedEffortLevels?: string[]\n supportsEffort?: boolean\n}\n\n/**\n * The CLI's model list, as `ModelOption[]`.\n *\n * Two decisions live here rather than in each client:\n *\n * - **`default` is dropped.** The CLI offers a row whose id is literally\n * `default` (\"Default (recommended)\"), meaning \"whatever I would have picked\".\n * It is a legal id to send, but it is not a model: a session running on it\n * reports a real model, so a picker showing it has a row that can never be\n * checked, and a status bar naming it would say \"Default\" for a session\n * answering as Opus. Which model the default resolved to is a different\n * question, and `system_init` answers it.\n * - **`primary` is derived.** The CLI reports one flat list; Claude Code's own\n * picker shows the newest of each family and files the rest under \"more\n * models\". The list arrives newest-first, so the first row of each family is\n * the primary one. A heuristic, but a stable one — and doing it once here\n * means the dashboard and the phone group identically.\n */\n/** What the CLI's `default` row resolves to — the model a session will answer as\n * before it has answered anything. Dropped from the list, kept as this. */\nexport function defaultModelFromSdk(models: readonly SdkModelInfo[]): string | undefined {\n return models.find((model) => model.value === 'default')?.resolvedModel\n}\n\nexport function modelOptionsFromSdk(models: readonly SdkModelInfo[]): ModelOption[] {\n const rows = models.filter((model) => model.value !== 'default')\n // A derived name is only used when it is unambiguous. Two rows of one model\n // (a 1M-context variant beside a plain one) would derive the same string, and\n // there the CLI's own names are the ones that tell them apart.\n const derivedCounts = new Map<string, number>()\n for (const model of rows) {\n const derived = friendlyModelName(model.resolvedModel ?? model.value)\n if (derived) derivedCounts.set(derived, (derivedCounts.get(derived) ?? 0) + 1)\n }\n\n const seenFamilies = new Set<string>()\n const options: ModelOption[] = rows.map((model) => {\n const family = modelFamily(model.resolvedModel ?? model.value)\n const primary = !seenFamilies.has(family)\n seenFamilies.add(family)\n const derived = friendlyModelName(model.resolvedModel ?? model.value)\n return {\n value: model.value,\n // Carried through so a client can match the model a session *reports*\n // ('claude-opus-5[1m]') against the row that names it ('opus[1m]').\n resolvedModel: model.resolvedModel,\n displayName: derived && derivedCounts.get(derived) === 1 ? derived : model.displayName,\n description: model.description,\n primary,\n // Explicit [] when the CLI reports no effort support, so clients don't\n // fall back to the engine-wide default set for an effortless model.\n reasoningEfforts: model.supportedEffortLevels ?? (model.supportsEffort === false ? [] : undefined),\n }\n })\n\n // Capability order, which is what a person picking a model is choosing along\n // and what the CLI's own selector shows. The CLI reports its list in a\n // different order and gives no ranking field, so it is declared here — a\n // family this list has never heard of sorts after the known ones rather than\n // to the top, and ties keep the CLI's order.\n return options\n .map((option, index) => ({ option, index }))\n .sort((a, b) => {\n const rankA = familyRank(a.option)\n const rankB = familyRank(b.option)\n return rankA === rankB ? a.index - b.index : rankA - rankB\n })\n .map(({ option }) => option)\n}\n\nconst FAMILY_ORDER = ['fable', 'opus', 'sonnet', 'haiku']\n\nfunction familyRank(option: ModelOption): number {\n const rank = FAMILY_ORDER.indexOf(modelFamily(option.resolvedModel ?? option.value))\n return rank === -1 ? FAMILY_ORDER.length : rank\n}\n\n/**\n * The name a person says, from a wire model id: 'claude-opus-5[1m]' → \"Opus 5\",\n * 'claude-haiku-4-5-20251001' → \"Haiku 4.5\".\n *\n * The CLI's own `displayName` is the family alone (\"Opus\", \"Haiku\") or carries a\n * variant instead of a version (\"Opus (1M context)\"), and the version is the part\n * that answers \"is this the current one\". It is only ever in the id, so it is\n * read from there. Returns null when the id has no version to read — a bare\n * alias like 'sonnet' — and the CLI's name stands.\n */\nexport function friendlyModelName(id: string): string | null {\n const withoutVariant = id.split('[')[0] ?? id\n const parts = withoutVariant.toLowerCase().split('-').filter(Boolean)\n if (parts[0] === 'claude') parts.shift()\n const family = parts.shift()\n if (!family) return null\n // Trailing snapshot date ('20251001') is a build, not a version.\n const version = parts.filter((part) => !/^\\d{8}$/.test(part))\n if (version.length === 0 || version.some((part) => !/^\\d+$/.test(part))) return null\n return `${family.charAt(0).toUpperCase()}${family.slice(1)} ${version.join('.')}`\n}\n\n/** 'claude-opus-4-8[1m]' → \"opus\". The vendor prefix, the context-window suffix\n * and the version tail are all dropped; what is left is the family a person\n * names. Unrecognisable ids become their own family, so a model this rule has\n * never seen lands in the main list rather than being hidden. */\nfunction modelFamily(id: string): string {\n const withoutVariant = id.split('[')[0] ?? id\n const parts = withoutVariant.toLowerCase().split('-')\n if (parts[0] === 'claude') parts.shift()\n return parts[0] ?? withoutVariant\n}\n\n/**\n * Map one SDKMessage to a wire-protocol event body, or null for messages the runner\n * consumes itself (system_init and session-state changes carry runner state and are\n * emitted by the runner with extra context).\n */\nexport function normalizeSdkMessage(msg: SDKMessage): SessionEventBody | null {\n switch (msg.type) {\n case 'assistant':\n return {\n type: 'assistant_message',\n message: toApiMessage(msg.message),\n parentToolUseId: msg.parent_tool_use_id,\n uuid: msg.uuid,\n }\n case 'user': {\n const message = toApiMessage(msg.message)\n return {\n type: 'user_message',\n message,\n parentToolUseId: msg.parent_tool_use_id,\n replay: 'isReplay' in msg && msg.isReplay === true ? true : undefined,\n // Three ways to be the harness rather than a person: the SDK says so,\n // the message's origin says so (a background task reporting in is not\n // someone typing), or the text is one of the CLI's own wrappers — which\n // is the only one of the three a *resumed* transcript still carries.\n synthetic:\n msg.isSynthetic === true ||\n msg.origin?.kind === 'task-notification' ||\n isSyntheticUserText(message)\n ? true\n : undefined,\n // The engine's own line numbers, projected down to the hunks — see\n // `filePatchFromToolResult` for why the rest of `tool_use_result` stays\n // off the wire. Only with a single tool_result block, because nothing\n // in the message says which call a patch belongs to.\n patch: singleToolResult(message) ? filePatchFromToolResult(msg.tool_use_result) : undefined,\n uuid: msg.uuid,\n }\n }\n case 'stream_event':\n return {\n type: 'stream_delta',\n event: msg.event as { type: string; [key: string]: unknown },\n parentToolUseId: msg.parent_tool_use_id,\n uuid: msg.uuid,\n }\n case 'result':\n return {\n type: 'turn_result',\n subtype: msg.subtype,\n isError: msg.is_error,\n durationMs: msg.duration_ms,\n numTurns: msg.num_turns,\n totalCostUsd: msg.total_cost_usd,\n result: msg.subtype === 'success' ? msg.result : undefined,\n errors: msg.subtype === 'success' ? undefined : msg.errors,\n usage: msg.usage,\n }\n case 'conversation_reset':\n // /clear, plan-mode exit, fresh-conversation flows: same session, fresh\n // conversation. The runner reacts to this body too (reset watermark,\n // sdkSessionId adoption) — see SessionRunner.#handleMessage.\n return { type: 'conversation_reset', sdkSessionId: msg.new_conversation_id }\n case 'rate_limit_event':\n return {\n type: 'rate_limit',\n info: {\n status: msg.rate_limit_info.status,\n rateLimitType: msg.rate_limit_info.rateLimitType,\n utilization: msg.rate_limit_info.utilization,\n resetsAt: msg.rate_limit_info.resetsAt,\n isUsingOverage: msg.rate_limit_info.isUsingOverage,\n },\n }\n case 'system':\n // init and session_state_changed are handled by the runner directly.\n if (msg.subtype === 'init' || msg.subtype === 'session_state_changed') return null\n return { type: 'sdk_event', payload: msg as unknown as { type: string } }\n default:\n return { type: 'sdk_event', payload: msg as unknown as { type: string } }\n }\n}\n","import {\n imagePartRef,\n replayCoalesceKey,\n replayRetains,\n TOOL_RESULT_HEAD_CHARS,\n transcriptContent,\n type SessionEvent,\n type ToolResultBlock,\n} from '@workerdeck/protocol'\n\n/**\n * Which buffered events a coalesced replay should skip: everything superseded\n * by a later event with the same {@link replayCoalesceKey}.\n *\n * A **backwards** scan, keeping the first occurrence of each key — which is the\n * whole trick. Walking forwards would need a second pass to know which of the\n * fifty context readings was the last one; walking backwards, the first one you\n * meet *is* the last one, and everything after it (in scan order) is history.\n *\n * Note what this does **not** do: it never reorders and never touches an event\n * with no key. Transcript content is an ordered fold — a stream delta\n * accumulates onto a message, a tool result attaches to a call that came\n * earlier, a turn result finalizes — so it must arrive exactly as it was\n * emitted. Only last-write-wins *state* is eligible, and `replayCoalesceKey`\n * is where that judgement lives.\n *\n * `afterSeq` is honoured so the scan agrees with the caller's replay window: an\n * event the caller was never going to send must not suppress one it was.\n */\nexport function staleReplaySeqs(events: readonly SessionEvent[], afterSeq: number): Set<number> {\n const stale = new Set<number>()\n const seen = new Set<string>()\n for (let index = events.length - 1; index >= 0; index--) {\n const event = events[index]!\n if (event.seq <= afterSeq) break\n const key = replayCoalesceKey(event)\n if (key === undefined) continue\n if (seen.has(key)) stale.add(event.seq)\n else seen.add(key)\n }\n return stale\n}\n\n/**\n * The one replay body, and what a socket receives from it.\n *\n * Every runner had a byte-identical copy of this loop — three spellings of four\n * rules, one of which (\"never drop the highest-seq event, whatever the rule\n * says\") is load-bearing and was three copies of a comment. Not a base class:\n * the runners share nothing else, and a base class would have to own `#emit`,\n * the most engine-specific method each of them has.\n *\n * The rules, in the order they are applied:\n *\n * 1. `afterSeq` — the caller already holds everything at or below it.\n * 2. `resetSeq` — transcript *content* strictly below the latest\n * `conversation_reset` is skipped, so a re-attach cannot resurrect a cleared\n * conversation while state events still replay. Claude's alone; the other\n * engines pass 0.\n * 3. `coalesceReplay` — last-write-wins state readings superseded later in the\n * same replay (`staleReplaySeqs`), plus everything `replayRetains` says the\n * reducer reads and discards. Opt-in, and only sound for a consumer whose\n * handling of those events is last-write-wins.\n * 4. `imageRefs` — a base64 image part is delivered as an `image_ref` address\n * (protocol's {@link imagePartRef}), its bytes one REST fetch away. Applied\n * **before** rule 5, because it stamps indices from the stored part array\n * which rule 5 then reshapes. Unlike rule 5 this also applies to the live\n * path (see `SubscriberSet`), which is the one place these two rules differ.\n * 5. `truncateResults` — a huge `tool_result` block is delivered as its head\n * plus the markers that say so. **Never mutates the stored event**: the live\n * path, the parking snapshot and the fetch route all need the whole thing,\n * so this builds a copy and the log stays the log.\n *\n * The highest-seq event is delivered whatever rules 2 and 3 say — a client's\n * replay hold waits for `state.lastSeq` to reach the attach's and would\n * otherwise hang forever — but it is still *truncated* when rule 4 applies. A\n * session that ends on a `find /` puts its 641 KB frame exactly there.\n */\nexport function replaySlice(\n events: readonly SessionEvent[],\n options: {\n afterSeq: number\n resetSeq?: number\n coalesceReplay?: boolean\n truncateResults?: boolean\n imageRefs?: boolean\n },\n): SessionEvent[] {\n const { afterSeq, resetSeq = 0, coalesceReplay, truncateResults, imageRefs } = options\n const stale = coalesceReplay ? staleReplaySeqs(events, afterSeq) : undefined\n const lastSeq = events[events.length - 1]?.seq ?? 0\n const out: SessionEvent[] = []\n for (const event of events) {\n if (event.seq <= afterSeq) continue\n if (event.seq < resetSeq && transcriptContent(event)) continue\n if (stale?.has(event.seq)) continue\n if (coalesceReplay && event.seq !== lastSeq && !replayRetains(event)) continue\n // Refs before heads, always: `refImageParts` stamps addresses from the\n // stored part array and `truncateResultBlocks` reshapes it.\n let delivered = event\n if (imageRefs) delivered = refImageParts(delivered)\n if (truncateResults) delivered = truncateResultBlocks(delivered)\n out.push(delivered)\n }\n return out\n}\n\n/**\n * A copy of `event` whose oversized `tool_result` blocks carry their head and\n * say so — or `event` itself, unchanged and un-copied, when nothing is over the\n * budget. That identity matters: an attach is mostly small events, and a fresh\n * object for every one of them would cost more than the feature saves.\n *\n * Blocks are measured and cut **individually**. A message answering three calls\n * where one is a `find /` keeps the two small results whole, which is what makes\n * the per-block marker (rather than a per-event one) honest.\n */\nexport function truncateResultBlocks(event: SessionEvent): SessionEvent {\n if (event.type !== 'user_message') return event\n const content = event.message.content\n if (!Array.isArray(content)) return event\n let cut = false\n const blocks = content.map((block) => {\n if (block.type !== 'tool_result') return block\n const result = block as ToolResultBlock\n if (result.truncated) return block\n const total = resultChars(result.content)\n if (total <= TOOL_RESULT_HEAD_CHARS) return block\n cut = true\n return {\n ...result,\n content: headOf(result.content, TOOL_RESULT_HEAD_CHARS),\n truncated: true,\n total_chars: total,\n } satisfies ToolResultBlock\n })\n if (!cut) return event\n return { ...event, message: { ...event.message, content: blocks } }\n}\n\n/** Characters in a result's content, in the same terms a reader sees it: the\n * string itself, or every text part of a block list joined by newlines — which\n * is exactly what `blockText` in the reducer builds. Non-text parts (an image\n * block) contribute nothing, because they are not what is large here and\n * slicing them would corrupt them. */\nfunction resultChars(content: ToolResultBlock['content']): number {\n if (typeof content === 'string') return content.length\n if (!Array.isArray(content)) return 0\n return content.reduce(\n (total, part, index) =>\n total + (typeof part.text === 'string' ? part.text.length + (index > 0 ? 1 : 0) : 0),\n 0,\n )\n}\n\n/** The first `chars` characters, in the content's own shape — a string stays a\n * string, a block list stays a block list (cut at the part that crosses the\n * budget, with the remaining parts dropped). Shape-preserving on purpose: the\n * reducer, both renderers and the copy button all read this the same way they\n * read a whole one, so truncation is a shorter result and never a different\n * kind of one. */\nfunction headOf(content: ToolResultBlock['content'], chars: number): ToolResultBlock['content'] {\n if (typeof content === 'string') return content.slice(0, chars)\n if (!Array.isArray(content)) return content\n const parts: Array<{ type: string; text?: string; [key: string]: unknown }> = []\n let used = 0\n for (const part of content) {\n // An address this gateway itself just minted (`refImageParts` runs first).\n // Keeping it is what lets the two rules compose: dropped here, a socket\n // asking for both heads and refs would lose every picture with no marker.\n // Raw `image` parts keep being dropped exactly as Part 4 shipped them,\n // which is what keeps a truncate-only socket byte-identical.\n if (part.type === 'image_ref') {\n parts.push(part)\n continue\n }\n if (typeof part.text !== 'string') continue\n // `continue`, not `break`: an exhausted text budget must not strand the\n // refs that come after it. Identical output for text either way.\n if (used >= chars) continue\n const text = part.text.slice(0, chars - used)\n parts.push({ ...part, text })\n used += text.length + 1\n }\n return parts\n}\n\n/**\n * A copy of `event` whose `tool_result` blocks carry `image_ref` addresses in\n * place of their base64 image parts — or `event` itself, unchanged and\n * un-copied, when it holds none. Same identity rule as\n * {@link truncateResultBlocks}, and it matters more here: an event carrying an\n * image at all is the exception, so the common path must not allocate.\n *\n * **Never mutates the stored event.** The log is what the parking snapshot\n * embeds, what `Runner.eventAt` reads, and therefore what the fetch route\n * serves the bytes back from — a drop that reached the log would 404 the very\n * lazy-load this rule promises.\n *\n * Indices are stamped from the **stored** array, which is why this runs *before*\n * truncation rather than after: `headOf` reshapes a block's parts, so an address\n * computed on its output would name the wrong part of the stored block. That\n * ordering is asserted in `replay-image-ref.test.ts`, not merely intended.\n */\nexport function refImageParts(event: SessionEvent): SessionEvent {\n if (event.type !== 'user_message') return event\n const content = event.message.content\n if (!Array.isArray(content)) return event\n let changed = false\n const blocks = content.map((block) => {\n if (block.type !== 'tool_result') return block\n const result = block as ToolResultBlock\n const parts = result.content\n if (!Array.isArray(parts)) return block\n let blockChanged = false\n const mapped = parts.map((part, index) => {\n const ref = imagePartRef(part, index)\n if (!ref) return part\n blockChanged = true\n return ref\n })\n if (!blockChanged) return block\n changed = true\n return { ...result, content: mapped } satisfies ToolResultBlock\n })\n if (!changed) return event\n return { ...event, message: { ...event.message, content: blocks } }\n}\n","/**\n * The other half of `replaySlice`, and the same argument.\n *\n * Three runners had a byte-identical `subscribe` body — replay the buffer, add\n * to a `Set`, return a deleter — and a byte-identical fan-out loop beside it,\n * try/catch comment included. `replaySlice` retired the first half of that\n * duplication when the replay grew rules worth stating once. This retires the\n * second, and it is not merely tidiness: the moment a rule applies to **live**\n * events as well as replayed ones, a bare `Set<listener>` has nowhere to keep\n * the options that rule is conditioned on, and each runner would grow its own\n * copy of the answer.\n *\n * So a subscriber is a listener *plus what it asked for*, and delivery is one\n * method. Not a base class, for `replaySlice`'s reason: the runners share\n * nothing else, and a base class would have to own `#emit`, the most\n * engine-specific method each of them has.\n *\n * **Which rules reach the live path is the whole judgement here**, and there is\n * exactly one:\n *\n * - `coalesceReplay` — replay-only by construction. It drops readings superseded\n * *later in the same replay*; live, there is no later.\n * - `truncateResults` — replay-only by decision. `TOOL_RESULT_HEAD_CHARS` is set\n * above both clients' own display budgets, so a result arriving while you\n * watch is already fully on screen and truncating it would buy a fetch for\n * nothing.\n * - `imageRefs` — **both**. The client's one render path is ref-then-fetch, so\n * bytes on a live event would either be discarded (335 KB median, once per\n * attached watcher) or need a second decode-from-event path — which pins\n * megabytes of base64 inside `TranscriptState`, which the transcript LRU then\n * retains across session switches. That is the disease relocated, not cured.\n *\n * Consumers that subscribe with no options — parking, notifications, the queue —\n * see every byte, as they do for every other rule in this family.\n */\nimport type { SessionEvent } from '@workerdeck/protocol'\nimport type { SessionEventListener } from '../runner-interface.ts'\nimport { refImageParts, replaySlice } from './replay.ts'\n\n/** What a subscriber asked for. Absent fields mean the untransformed stream. */\nexport type SubscribeOptions = {\n coalesceReplay?: boolean\n truncateResults?: boolean\n imageRefs?: boolean\n}\n\nexport class SubscriberSet {\n readonly #listeners = new Map<SessionEventListener, SubscribeOptions>()\n\n /**\n * Replay `events` to `listener` under `options`, then hold it for live\n * delivery. Returns the unsubscribe.\n *\n * The replay runs *before* the listener joins the set, which is the ordering\n * every runner already had and is load-bearing: joining first would deliver a\n * live event emitted mid-replay ahead of the buffered events preceding it.\n */\n subscribe(\n events: readonly SessionEvent[],\n listener: SessionEventListener,\n afterSeq = 0,\n options?: SubscribeOptions,\n resetSeq = 0,\n ): () => void {\n const asked = options ?? {}\n for (const event of replaySlice(events, { ...asked, afterSeq, resetSeq })) listener(event)\n this.#listeners.set(listener, asked)\n return () => {\n this.#listeners.delete(listener)\n }\n }\n\n /** Drop every subscriber — a park, which ends the session's live stream. */\n clear(): void {\n this.#listeners.clear()\n }\n\n /** Fan one event out, transformed per subscriber. */\n emit(event: SessionEvent): void {\n for (const [listener, asked] of this.#listeners) {\n try {\n listener(asked.imageRefs ? refImageParts(event) : event)\n } catch {\n // Listener errors must not break the runner loop.\n }\n }\n }\n}\n","import {\n SUBAGENT_HISTORY,\n type ContentBlock,\n type SessionEventBody,\n type SubagentInfo,\n} from '@workerdeck/protocol'\n\n/**\n * The rollup behind `SessionInfo.subagents` — what a sessions list (which never\n * attaches) can know about the sub-agents running inside a session. Fed from\n * `SessionRunner.#emit`, the one chokepoint every event passes through, so the\n * resume backfill — which replays history through the same path — reconstructs\n * it with no persistence of its own. Grouping is by Task id throughout, never\n * adjacency: parallel sub-agents interleave in the stream, the same fact that\n * broke the terminal theme's positional row model.\n *\n * Three decisions live here rather than in the protocol doc:\n *\n * **What counts as a spawn.** A record opens when a *top-level* assistant\n * message carries a `tool_use` named `Task` or `Agent` — the moment the\n * sub-agent exists, so a just-spawned agent is visible before its first nested\n * event, with the block's input in hand for its labels. Both names are observed\n * SDK spellings (`Task` synchronous, `Agent` async), and the name is a\n * convention, not a law — a session that spawned three `Agent`s under a tracker\n * that only knew `Task` reported all three as label-less failures. So three\n * more openers back the allowlist up: the CLI's own `task_started` system event\n * (which positively names the `tool_use_id` an agent runs under, with the brief\n * as labels), the launch acknowledgement (below), and — as before — any nested\n * event whose `parentToolUseId` has no record: an id that events demonstrably\n * nest under *is* a sub-agent, whatever the spawning call was named. A fallback\n * record never saw an input, so it stays label-less until a named signal fills\n * it in rather than resetting an accumulated count.\n *\n * **A background agent's `tool_result` is a launch receipt, not a verdict.**\n * An async agent's spawn call resolves seconds after the spawn with \"Async\n * agent launched successfully. (This tool result is internal metadata …)\" —\n * long before the agent has done anything — and its actual outcome travels on\n * a `task_notification` system event instead (`status: 'completed'` is `done`,\n * any other way of stopping is `failed`: the report the notification exists to\n * deliver never came). Settling on the receipt would read \"0 of 3 agents\n * running\" while three agents burn tokens, so a non-error result on a record\n * known to be background never settles it. Known how: the `task_started` event\n * live, or the receipt's own wrapper text on a resume — the stored transcript\n * carries none of the CLI's system events, so, exactly as\n * `isSyntheticUserText` documents for the `<task-notification>` blob, the text\n * is the only signal the replayed path has.\n *\n * **What an interrupted turn leaves behind.** A Task whose `tool_result` never\n * arrives — interrupt, session error, a turn or budget cap — would otherwise\n * read `running` on an idle session forever, a lie a list re-renders at every\n * poll. So the end of a turn settles every still-running record as `failed`:\n * the report never came, which is the one thing `done` could have claimed. The\n * sweep keys on `turn_result`, on the status coming to rest (`idle` — which is\n * how a resumed history that ends mid-Task settles, since the backfill replays\n * no `turn_result` — or a terminal state), and on the session closing. A real\n * verdict arriving anyway outranks the sweep's inference. The sweep's premise\n * — \"the turn ended, so anything still running was cut off\" — is false for a\n * background agent, which is *designed* to outlive its turn: the real session\n * behind this file ended three turns while its agents ran, and every\n * `turn_result` re-branded live, working agents as failures. So the turn and\n * idle sweeps spare a record marked background by a **live** signal. They do\n * not spare one whose only evidence is replayed: the backfill describes a\n * process that is gone, and a background agent the old process died inside can\n * never notify — `running` would be the forever-lie again. `session_closed`\n * and the terminal statuses settle everything, background included, for the\n * same reason: the process hosting those agents is gone.\n */\nexport class SubagentTracker {\n #records = new Map<string, TrackedSubagent>()\n #settleCounter = 0\n\n /** Fold one emitted event body into the rollup, in log order. */\n observe(body: SessionEventBody, ts: number): void {\n switch (body.type) {\n case 'assistant_message': {\n if (body.parentToolUseId != null) {\n const record = this.#recordFor(body.parentToolUseId, ts)\n // Progress is tool calls, not prose: a sub-agent's text and thinking\n // are its working, and counting them would make two agents' readings\n // incomparable. A nested `Task` block (a grandchild spawn, should an\n // engine ever nest) is still one tool call of *this* sub-agent.\n record.toolCount += toolUseBlocks(body.message.content).length\n return\n }\n for (const block of toolUseBlocks(body.message.content)) {\n if (!SPAWNER_NAMES.has(block.name)) continue\n this.#open(block, ts)\n }\n return\n }\n case 'user_message': {\n if (body.parentToolUseId != null) {\n // A sidechain's first event is usually its brief; touching the record\n // here is what makes the fallback catch a renamed spawner promptly.\n this.#recordFor(body.parentToolUseId, ts)\n return\n }\n // A background agent stopping, as the resume backfill spells it: the\n // `<task-notification>` wrapper the CLI writes into the transcript\n // (live, the same fact arrives as a `task_notification` system event\n // and no user message at all). Parsed before the plain-string return\n // below — the stored form is a bare string.\n const note = parseTaskNotification(firstText(body.message.content))\n if (note) {\n const record = this.#recordFor(note.toolUseId, ts)\n const status = note.status === 'completed' ? 'done' : 'failed'\n if (record.status !== status) this.#settle(record, status)\n return\n }\n const content = body.message.content\n if (typeof content === 'string') return\n for (const block of content) {\n if (block.type !== 'tool_result') continue\n const result = block as {\n tool_use_id?: unknown\n is_error?: unknown\n content?: unknown\n }\n if (typeof result.tool_use_id !== 'string') continue\n if (result.is_error !== true && isLaunchAck(result.content)) {\n // The receipt marks the record background rather than settling it —\n // and *how* it was marked matters to the sweep: `live` outlives the\n // turn, `replay` describes a process that is gone. Live evidence is\n // never downgraded by a re-streamed duplicate on resume.\n const record = this.#recordFor(result.tool_use_id, ts)\n if (record.background !== 'live') {\n record.background = body.replay === true ? 'replay' : 'live'\n }\n continue\n }\n const record = this.#records.get(result.tool_use_id)\n if (!record) continue\n // Belt beside the wording sniff above: whatever the receipt says, a\n // non-error result on a background record carries no verdict — the\n // verdict travels on the notification.\n if (result.is_error !== true && record.background !== undefined) continue\n const status = result.is_error === true ? 'failed' : 'done'\n // Equal-verdict results are skipped rather than re-stamped: the SDK\n // re-streams user messages on resume, and a duplicate that re-stamped\n // settle order would shuffle the retention bound. An *unequal* one\n // re-settles — the engine's own verdict outranks the sweep's.\n if (record.status === status) continue\n this.#settle(record, status)\n }\n return\n }\n case 'sdk_event': {\n // The CLI's background-task lifecycle, live only — none of it lands in\n // the stored transcript a resume replays. `task_updated` is skipped on\n // purpose: it is keyed by task id alone, and the `task_notification`\n // that follows it carries the `tool_use_id` this rollup is keyed by.\n // `task_progress`'s `description` is skipped too — it is the agent's\n // *current activity* (\"Running grep …\"), not its brief, and a label\n // slot that changed per poll would be a status field wearing a label's\n // name.\n const p = body.payload as {\n type?: unknown\n subtype?: unknown\n tool_use_id?: unknown\n status?: unknown\n subagent_type?: unknown\n description?: unknown\n }\n if (p.type !== 'system' || typeof p.tool_use_id !== 'string') return\n if (p.subtype === 'task_started') {\n const record = this.#recordFor(p.tool_use_id, ts)\n record.background = 'live'\n record.agentType ??= cleaned(p.subagent_type)\n record.description ??= cleaned(p.description)\n return\n }\n if (p.subtype === 'task_notification') {\n const record = this.#recordFor(p.tool_use_id, ts)\n const status = p.status === 'completed' ? 'done' : 'failed'\n if (record.status !== status) this.#settle(record, status)\n return\n }\n return\n }\n case 'turn_result':\n this.#sweep(false)\n return\n case 'session_closed':\n this.#sweep(true)\n return\n case 'status_changed':\n if (body.status === 'idle') this.#sweep(false)\n else if (body.status === 'failed' || body.status === 'closed') this.#sweep(true)\n return\n case 'conversation_reset':\n // The conversation is gone; so are the Tasks it ran. The same claim the\n // reset watermark makes for replay: a fresh attacher never sees those\n // rows, so a rollup pointing into them would dangle.\n this.#records.clear()\n return\n default:\n // stream_delta lands here on purpose: deltas count zero (superseded by\n // construction), and they must not open the fallback either — the\n // resume backfill replays no deltas, so a record only a delta opened\n // would not survive a rebuild.\n return\n }\n }\n\n /**\n * The rollup as `SessionInfo.subagents` serves it: spawn order (the\n * transcript's own), fresh objects, and `undefined` when there is nothing to\n * say — absent and empty mean the same thing to a client, and an empty array\n * on every row of a 1.2s-polled list is bytes spent saying nothing.\n */\n list(): SubagentInfo[] | undefined {\n if (this.#records.size === 0) return undefined\n const out: SubagentInfo[] = []\n for (const r of this.#records.values()) {\n out.push({\n toolUseId: r.toolUseId,\n agentType: r.agentType,\n description: r.description,\n status: r.status,\n startedAt: r.startedAt,\n toolCount: r.toolCount,\n })\n }\n return out\n }\n\n #recordFor(toolUseId: string, ts: number): TrackedSubagent {\n let record = this.#records.get(toolUseId)\n if (!record) {\n record = { toolUseId, status: 'running', startedAt: ts, toolCount: 0 }\n this.#records.set(toolUseId, record)\n }\n return record\n }\n\n #open(block: { id: string; input: unknown }, ts: number): void {\n const record = this.#recordFor(block.id, ts)\n const input = block.input as\n | { subagent_type?: unknown; description?: unknown }\n | null\n | undefined\n // Fill-in, never overwrite: this may be upgrading a label-less fallback\n // record that already accumulated a count.\n record.agentType ??= cleaned(input?.subagent_type)\n record.description ??= cleaned(input?.description)\n }\n\n /**\n * End of turn (`final: false`): anything still running was cut off before\n * its report — except a background agent the live process still hosts, which\n * is designed to outlive the turn and settles by notification instead. End\n * of session (`final: true`): everything, background included, because the\n * process those agents lived in is gone.\n */\n #sweep(final: boolean): void {\n for (const record of this.#records.values()) {\n if (record.status !== 'running') continue\n if (!final && record.background === 'live') continue\n this.#settle(record, 'failed')\n }\n }\n\n #settle(record: TrackedSubagent, status: 'done' | 'failed'): void {\n record.status = status\n record.settledOrder = ++this.#settleCounter\n // The bound is enforced here rather than in list(): a settle happens once\n // per sub-agent, list() once per row of a polled sessions list. Running\n // records are never evicted — they are the live reading and the reason the\n // field exists.\n let settled = 0\n for (const r of this.#records.values()) {\n if (r.settledOrder !== undefined) settled++\n }\n while (settled > SUBAGENT_HISTORY) {\n let oldestId: string | undefined\n let oldestOrder = Infinity\n for (const r of this.#records.values()) {\n if (r.settledOrder === undefined || r.settledOrder >= oldestOrder) continue\n oldestId = r.toolUseId\n oldestOrder = r.settledOrder\n }\n if (oldestId === undefined) break\n this.#records.delete(oldestId)\n settled--\n }\n }\n}\n\ntype TrackedSubagent = SubagentInfo & {\n /** Monotonic settle stamp; the retention bound evicts the smallest. Insertion\n * order cannot stand in for it — records open in spawn order, and a slow\n * early Task settles after a fast late one. */\n settledOrder?: number\n /** Set when this is a *background* agent — one designed to outlive the turn\n * that spawned it — and by what kind of evidence: `live` (the `task_started`\n * event, or the launch receipt arriving on the live stream) spares it from\n * the turn/idle sweep; `replay` (the receipt replayed from a resumed\n * transcript) does not, because the process that ran it is gone and it can\n * never notify. Never downgraded from `live`. Internal — protocol's\n * `SubagentInfo` deliberately says nothing about it. */\n background?: 'live' | 'replay'\n}\n\n/** The spawner names observed in the wild: `Task` runs the agent inside the\n * turn, `Agent` launches it in the background. Deliberately just these two —\n * a third spelling is caught by `task_started`, the launch receipt, or the\n * nested-event fallback, so widening this to every tool would only turn\n * ordinary calls into phantom agents. */\nconst SPAWNER_NAMES = new Set(['Task', 'Agent'])\n\n/** The async spawn's immediate `tool_result` — \"Async agent launched\n * successfully. (This tool result is internal metadata …)\" — recognized by its\n * wrapper text because on a resume that text is the only signal there is (the\n * `SYNTHETIC_USER_PREFIXES` argument; the CLI's system events are not stored).\n * Live, `task_started` marks the record first and this is redundant armor. */\nconst isLaunchAck = (content: unknown): boolean => {\n const text =\n typeof content === 'string' ? content : firstText(Array.isArray(content) ? content : [])\n return typeof text === 'string' && text.trimStart().startsWith('Async agent launched')\n}\n\n/** A background agent stopping, parsed from the `<task-notification>` wrapper\n * the CLI writes into the transcript. Field-tolerant on purpose: only the\n * `tool-use-id` (this rollup's key) and the `status` verdict are read. */\nconst parseTaskNotification = (\n text: string | undefined,\n): { toolUseId: string; status: string } | undefined => {\n if (text === undefined || !text.trimStart().startsWith('<task-notification>')) return undefined\n const toolUseId = /<tool-use-id>\\s*([^<\\s]+)\\s*<\\/tool-use-id>/.exec(text)?.[1]\n if (toolUseId === undefined) return undefined\n const status = /<status>\\s*([^<]*?)\\s*<\\/status>/.exec(text)?.[1] ?? ''\n return { toolUseId, status }\n}\n\n/** The first text of a message body, however the content is spelled — the\n * stored transcript uses bare strings, the live stream uses blocks. */\nconst firstText = (content: string | ContentBlock[] | unknown[]): string | undefined => {\n if (typeof content === 'string') return content\n for (const block of content) {\n const b = block as { type?: unknown; text?: unknown } | null | undefined\n if (b?.type === 'text' && typeof b.text === 'string') return b.text\n }\n return undefined\n}\n\n/** Trim, drop blank, clip at the same 80 the terminal theme's `taskLabel` uses.\n * Model-authored input rides every row of a polled sessions list, so it is\n * bounded here rather than trusted — a 10KB `description` would be paid for at\n * every poll. */\nconst cleaned = (value: unknown): string | undefined => {\n if (typeof value !== 'string') return undefined\n const text = value.trim()\n if (text === '') return undefined\n return text.length > 80 ? text.slice(0, 79) + '…' : text\n}\n\n/** The `tool_use` blocks of a message body, however the content is spelled. */\nfunction toolUseBlocks(\n content: string | ContentBlock[],\n): Array<{ id: string; name: string; input: unknown }> {\n if (typeof content === 'string') return []\n const blocks: Array<{ id: string; name: string; input: unknown }> = []\n for (const block of content) {\n if (block.type !== 'tool_use') continue\n const b = block as { id?: unknown; name?: unknown; input?: unknown }\n if (typeof b.id !== 'string' || typeof b.name !== 'string') continue\n blocks.push({ id: b.id, name: b.name, input: b.input })\n }\n return blocks\n}\n","import { randomUUID } from 'node:crypto'\nimport {\n getSessionInfo,\n getSessionMessages,\n query as sdkQuery,\n type CanUseTool,\n type Options,\n type PermissionResult,\n type Query,\n type SDKMessage,\n type SDKSessionInfo,\n type SDKUserMessage,\n type SessionMessage,\n} from '@anthropic-ai/claude-agent-sdk'\nimport {\n ENGINE_CAPABILITIES,\n contextReading,\n type ContextReading,\n transcriptActivity,\n type CreateSessionRequest,\n type McpServerStatusInfo,\n type PermissionMode,\n type PermissionRequest,\n type SessionEvent,\n type SessionEventBody,\n type SessionInfo,\n type SessionStatus,\n} from '@workerdeck/protocol'\nimport {\n type AttachmentInput,\n attachmentContentBlocks,\n attachmentRef,\n} from '../../lib/attachments.ts'\nimport { InputQueue } from '../../lib/input-queue.ts'\nimport {\n type UsageRateLimits,\n defaultModelFromSdk,\n isSyntheticUserText,\n mcpStatusInfo,\n modelOptionsFromSdk,\n normalizeSdkMessage,\n rateLimitEventsFromUsage,\n toApiMessage,\n} from '../../lib/normalize.ts'\nimport type { PermissionDecision, Runner, SessionEventListener } from '../../runner-interface.ts'\nimport { SubscriberSet, type SubscribeOptions } from '../../lib/subscribers.ts'\nimport { SubagentTracker } from './subagents.ts'\n\nexport type QueryFn = (params: {\n prompt: AsyncIterable<SDKUserMessage>\n options?: Options\n}) => Query\n\nexport type HistoryFn = (\n sdkSessionId: string,\n options: { dir?: string },\n) => Promise<SessionMessage[]>\n\nexport type SessionInfoFn = (\n sdkSessionId: string,\n options: { dir?: string },\n) => Promise<SDKSessionInfo | undefined>\n\nexport type SessionRunnerConfig = CreateSessionRequest & {\n /** Injectable query implementation (tests, instrumentation). Defaults to the SDK's query(). */\n queryFn?: QueryFn\n /** Environment for the spawned Claude Code process. Defaults to process.env. */\n env?: Record<string, string | undefined>\n pathToClaudeCodeExecutable?: string\n /** Escape hatch merged last into the SDK Options. */\n extraOptions?: Partial<Options>\n /** Timeout for pending approvals when the request itself doesn't set one. Default 300000. */\n defaultApprovalTimeoutMs?: number\n /** With `resume`: emit the resumed session's history as replay events before the query\n * starts, so late-attaching clients get a full transcript. Default true. */\n backfillHistory?: boolean\n /** Injectable history reader (tests). Defaults to the SDK's getSessionMessages. */\n historyFn?: HistoryFn\n /** Injectable session-metadata reader (tests). Defaults to the SDK's\n * getSessionInfo — the only place the CLI's own session title is readable\n * from, since no message on the stream carries it. */\n sessionInfoFn?: SessionInfoFn\n}\n\nconst DEFAULT_APPROVAL_TIMEOUT_MS = 300_000\n\ntype PendingApproval = {\n request: PermissionRequest\n resolve: (result: PermissionResult) => void\n timer: ReturnType<typeof setTimeout>\n}\n\n/**\n * One live Agent SDK session: owns the query() call, the streaming input queue, the\n * pending-approval table, and a seq-numbered event log that subscribers can replay.\n * No transport — the server (or any host) subscribes and bridges to the wire.\n */\nexport class SessionRunner implements Runner {\n readonly id: string\n readonly createdAt: number\n\n #config: SessionRunnerConfig\n /** {@link SessionRunnerConfig.cwd}, checked once in the constructor. */\n readonly #cwd: string\n #events: SessionEvent[] = []\n #subscribers = new SubscriberSet()\n #seq = 0\n /**\n * Latest context-window reading, retained from the last `context_usage` this\n * runner emitted so `GET /sessions` can answer it without an attach — see\n * `SessionInfo.contextUsage`. Folded in the emit path, so it is by\n * construction the same number the transcript last drew.\n */\n #contextUsage: ContextReading | undefined\n #activityCount = 0\n /**\n * Seq of the latest `conversation_reset` event, 0 when none. The log itself is\n * never truncated — it still carries the state-bearing events (`capabilities`,\n * `system_init`, …) a fresh attacher depends on and which are not re-emitted —\n * but `subscribe()` skips transcript *content* strictly below this mark, so a\n * replay does not resurrect a cleared conversation. A later reset supersedes\n * an earlier one by overwriting it.\n */\n #resetSeq = 0\n #status: SessionStatus = 'starting'\n #statusDetail: string | undefined\n #sdkSessionId: string | undefined\n #model: string | undefined\n #apiKeySource: string | undefined\n #permissionMode: PermissionMode | undefined\n #pending = new Map<string, PendingApproval>()\n /**\n * The turn ended while an approval was standing, and nothing has started a\n * new one since.\n *\n * `awaiting_approval` rightly outranks `idle` for display, so a turn-over\n * signal arriving under a standing approval cannot be applied when it lands.\n * It used to be **discarded** for that reason, which is a different thing\n * from outranked: the settle path then asserted `running` on the assumption\n * that an answered approval means work resumes, and when the turn was already\n * over — an interrupt, a timeout — the session claimed to be running one that\n * had produced its result. Status is purely edge-driven here, with no poll and\n * no reconciliation anywhere, so that single dropped edge never came back and\n * every client rendered it faithfully for the life of the session.\n *\n * So the fact is *deferred* rather than dropped, and it is deliberately\n * cleared the moment work genuinely resumes — a turn-over belongs to the turn\n * that produced it and must not settle the next one.\n */\n #turnOverWhileBlocked = false\n /** The read-time sub-agent rollup (`SessionInfo.subagents`), fed from #emit —\n * the one chokepoint — so the resume backfill reconstructs it for free. */\n #subagents = new SubagentTracker()\n #totalCostUsd: number | undefined\n #numTurns: number | undefined\n #lastActivityAt: number | undefined\n #input = new InputQueue()\n #query: Query | undefined\n #capabilitiesEmitted = false\n /** Last plan reported by the usage poll, so `plan_info` is emitted on change\n * rather than once per turn. */\n #subscriptionType: string | undefined\n /** The title the CLI gave this thread (see `#fetchEngineTitle`). Undefined\n * until it has one — a session gets its summary a turn or two in. */\n #engineTitle: string | undefined\n #started = false\n #closed = false\n #runPromise: Promise<void> | undefined\n\n constructor(config: SessionRunnerConfig, id: string = randomUUID()) {\n // Optional on the wire (an engine with no host filesystem takes none) but\n // required here: this one spawns the CLI in a real directory. The gateway\n // enforces it off `EngineCapabilities.hostCwd`, so reaching this throw means\n // a host built a runner around that check.\n if (!config.cwd) throw new Error('the claude engine requires a cwd')\n this.#cwd = config.cwd\n this.#config = config\n this.#permissionMode = config.permissionMode\n this.id = id\n this.createdAt = Date.now()\n }\n\n get status(): SessionStatus {\n return this.#status\n }\n\n get sdkSessionId(): string | undefined {\n return this.#sdkSessionId\n }\n\n get lastSeq(): number {\n return this.#seq\n }\n\n /** 'oauth' = claude.ai subscription credentials; other values are API-key provenance. */\n get apiKeySource(): string | undefined {\n return this.#apiKeySource\n }\n\n get pendingApprovals(): PermissionRequest[] {\n return [...this.#pending.values()].map((p) => p.request)\n }\n\n info(): SessionInfo {\n return {\n id: this.id,\n sdkSessionId: this.#sdkSessionId,\n status: this.#status,\n cwd: this.#cwd,\n profile: this.#config.profile,\n engine: 'claude',\n capabilities: ENGINE_CAPABILITIES.claude,\n model: this.#model ?? this.#config.model,\n permissionMode: this.#permissionMode,\n // Fixed at spawn: the CLI refuses to switch into bypass unless it was\n // launched for it (see #buildOptions). Reported so a client can disable\n // the mode rather than offer a switch that will be refused.\n canBypassPermissions:\n this.#config.permissionMode === 'bypassPermissions' ||\n this.#config.allowDangerouslySkipPermissions === true,\n apiKeySource: this.#apiKeySource,\n createdAt: this.createdAt,\n lastSeq: this.#seq,\n activityCount: this.#activityCount,\n contextUsage: this.#contextUsage,\n pendingPermissionCount: this.#pending.size,\n subagents: this.#subagents.list(),\n meta: this.#config.meta,\n scope: this.#config.scope,\n title: this.#title(),\n totalCostUsd: this.#totalCostUsd,\n numTurns: this.#numTurns,\n lastActivityAt: this.#lastActivityAt,\n }\n }\n\n /**\n * Three sources, most-deliberate first: the host's own rename (`meta.title`),\n * the title the CLI gave this thread (`#engineTitle`), then the first prompt\n * truncated.\n *\n * The rename outranks everything by design — a person naming a session must\n * not have it renamed under them by a model — which is also why the engine\n * title is *only ever read* while `meta.title` is unset (see\n * `#fetchEngineTitle`), rather than read and then discarded here.\n */\n #title(): string | undefined {\n const metaTitle = this.#config.meta?.title\n if (typeof metaTitle === 'string' && metaTitle.length > 0) return metaTitle\n if (this.#engineTitle) return this.#engineTitle\n const prompt = this.#config.prompt\n if (!prompt) return undefined\n return prompt.length > 80 ? prompt.slice(0, 77) + '…' : prompt\n }\n\n /** Host-facing rename: writes `meta.title`, which `#title()` prefers. Clearing\n * it (undefined) restores the derived title. The engine is never told. */\n setTitle(title: string | undefined): void {\n const meta = { ...this.#config.meta }\n if (title) meta.title = title\n else delete meta.title\n this.#config = { ...this.#config, meta }\n }\n\n /** Begin the session. Idempotent; returns the run promise (resolves when the query ends). */\n start(): Promise<void> {\n if (this.#started) return this.#runPromise!\n this.#started = true\n if (this.#config.prompt) this.sendMessage(this.#config.prompt)\n this.#runPromise = this.#run()\n return this.#runPromise\n }\n\n /** Queue a user message for the session (starts the next turn when idle).\n *\n * `attachments` carry their own bytes; they reach the CLI as content blocks and\n * are logged as references. A message may be attachments alone — an empty text\n * block is not valid API input, so the text is only added when there is some. */\n sendMessage(text: string, attachments?: readonly AttachmentInput[]): void {\n if (this.#closed) throw new Error('session is closed')\n const blocks = attachments?.length ? attachmentContentBlocks(attachments) : []\n const content = blocks.length\n ? ([...blocks, ...(text ? [{ type: 'text', text }] : [])] as unknown as SDKUserMessage['message']['content'])\n : text\n this.#input.push({\n type: 'user',\n message: { role: 'user', content },\n parent_tool_use_id: null,\n session_id: this.#sdkSessionId,\n })\n // The SDK does not echo streamed-input user messages back, so the transcript\n // would never show them — emit the event here (the one place input enters).\n this.#emit({\n type: 'user_message',\n message: { role: 'user', content: text },\n parentToolUseId: null,\n attachments: attachments?.length ? attachments.map(attachmentRef) : undefined,\n uuid: randomUUID(),\n })\n }\n\n /** Live MCP server status, straight from the CLI. Undefined when the engine\n * can't answer (an injected fake query in tests) — the caller 501s rather than\n * pretending the session has no servers. */\n async mcpServers(): Promise<McpServerStatusInfo[] | undefined> {\n const query = this.#query\n if (typeof query?.mcpServerStatus !== 'function') return undefined\n return (await query.mcpServerStatus()).map(mcpStatusInfo)\n }\n\n async reconnectMcpServer(name: string): Promise<void> {\n const query = this.#query\n if (typeof query?.reconnectMcpServer !== 'function') {\n throw new Error('this session cannot reconnect MCP servers')\n }\n await query.reconnectMcpServer(name)\n }\n\n async setMcpServerEnabled(name: string, enabled: boolean): Promise<void> {\n const query = this.#query\n if (typeof query?.toggleMcpServer !== 'function') {\n throw new Error('this session cannot enable or disable MCP servers')\n }\n await query.toggleMcpServer(name, enabled)\n }\n\n /** Resolve a pending permission request. Returns false if the id is unknown (e.g. timed out). */\n resolvePermission(requestId: string, decision: PermissionDecision): boolean {\n const pending = this.#pending.get(requestId)\n if (!pending) return false\n this.#settleApproval(requestId, pending, decision, 'client')\n return true\n }\n\n async interrupt(): Promise<void> {\n await this.#query?.interrupt()\n }\n\n async setPermissionMode(mode: PermissionMode): Promise<void> {\n await this.#query?.setPermissionMode(mode)\n this.#permissionMode = mode\n this.#emit({ type: 'permission_mode_changed', mode })\n }\n\n /** Switch the model for subsequent responses; undefined = back to the default. */\n async setModel(model?: string): Promise<void> {\n await this.#query?.setModel(model)\n this.#model = model\n this.#emit({ type: 'model_changed', model })\n }\n\n /** Emit a session_error and terminate. For host-enforced policy (e.g. requireApiKey). */\n fail(message: string): void {\n if (this.#closed) return\n this.#emit({ type: 'session_error', message })\n this.#setStatus('failed')\n this.close('error')\n }\n\n /** Terminate the session and the underlying CLI subprocess. */\n close(reason: 'client' | 'server' | 'error' = 'client'): void {\n if (this.#closed) return\n this.#closed = true\n for (const [id, pending] of this.#pending) {\n this.#settleApproval(id, pending, { behavior: 'deny', message: 'Session closed' }, 'policy')\n }\n this.#input.end()\n this.#query?.close()\n this.#emit({ type: 'session_closed', reason })\n this.#setStatus('closed')\n }\n\n /** See `Runner.eventAt`. A linear scan: the one caller is a reader pressing\n * \"show everything\" on one row, so a per-runner seq index would be a map\n * maintained on every emit to save a walk nobody makes twice a minute. */\n eventAt(seq: number): SessionEvent | undefined {\n return this.#events.find((event) => event.seq === seq)\n }\n\n /**\n * Replay buffered events with seq > afterSeq, then deliver live events.\n * Returns an unsubscribe function.\n *\n * Replay honours the reset watermark: transcript content below the latest\n * `conversation_reset` is skipped (the reducer would clear it again anyway,\n * and a pre-reset client that never learned the reducer's case would render\n * a conversation the engine has discarded), while state-bearing events —\n * which are emitted once and never again — always replay. The reset event\n * itself replays (the skip is strictly-below), which is what clears a\n * reconnecting client still holding pre-reset rows; superseded resets are\n * content below the newer one and are skipped with what they cleared.\n */\n subscribe(\n listener: SessionEventListener,\n afterSeq = 0,\n options?: SubscribeOptions,\n ): () => void {\n return this.#subscribers.subscribe(this.#events, listener, afterSeq, options, this.#resetSeq)\n }\n\n async #run(): Promise<void> {\n const queryFn = this.#config.queryFn ?? (sdkQuery as QueryFn)\n try {\n await this.#backfillHistory()\n if (this.#closed) return\n this.#query = queryFn({ prompt: this.#input, options: this.#buildOptions() })\n // Without an initial prompt the CLI stays silent (no init handshake) until the\n // first message arrives, so 'starting' would never resolve — the session is\n // already accepting input, which is what 'idle' means. The control channel\n // does answer before init, though — fetch capabilities, a context baseline and\n // the plan's usage now so promptless sessions aren't blank until their first\n // turn. A session opened only to be watched may never have one.\n if (!this.#config.prompt) {\n this.#setStatus('idle')\n void this.#fetchCapabilities()\n void this.#fetchContextUsage()\n void this.#fetchRateLimits()\n }\n for await (const message of this.#query) {\n this.#handleMessage(message)\n }\n if (!this.#closed) {\n this.#closed = true\n this.#input.end()\n this.#emit({ type: 'session_closed', reason: 'server' })\n this.#setStatus('closed')\n }\n } catch (error) {\n if (!this.#closed) {\n this.#emit({\n type: 'session_error',\n message: error instanceof Error ? error.message : String(error),\n })\n this.#setStatus('failed')\n this.close('error')\n }\n }\n }\n\n /**\n * On resume, emit the prior session's transcript as replay events (seq'd before any\n * live event). The SDK only re-streams *user* messages on resume; assistant history\n * would otherwise be lost to clients attaching after a server restart. Duplicated\n * user messages are deduped client-side by uuid.\n */\n async #backfillHistory(): Promise<void> {\n const c = this.#config\n if (!c.resume || c.backfillHistory === false) return\n const historyFn = c.historyFn\n ?? ((sessionId: string, options: { dir?: string }) => getSessionMessages(sessionId, options))\n let messages: SessionMessage[]\n try {\n messages = await historyFn(c.resume, { dir: this.#cwd })\n } catch {\n // Best-effort: a missing/unreadable transcript must not block the resume itself.\n return\n }\n for (const m of messages) {\n if (this.#closed) return\n if (m.type === 'user') {\n const message = toApiMessage(m.message)\n this.#emit({\n type: 'user_message',\n message,\n parentToolUseId: m.parent_tool_use_id,\n replay: true,\n // The live path reads this off `isSynthetic` / `origin.kind`; a stored\n // message carries neither (see `isSyntheticUserText`), so the wrapper\n // text is the only thing left to read it from. Without it a resumed\n // session's `<task-notification>` blobs come back as blue user rows —\n // and, because `transcriptActivity` counts a non-synthetic user\n // message as a row, as unread badges for work nobody typed.\n synthetic: isSyntheticUserText(message) ? true : undefined,\n uuid: m.uuid,\n })\n } else if (m.type === 'assistant') {\n this.#emit({\n type: 'assistant_message',\n message: toApiMessage(m.message),\n parentToolUseId: m.parent_tool_use_id,\n replay: true,\n uuid: m.uuid,\n })\n }\n }\n }\n\n #buildOptions(): Options {\n const c = this.#config\n const options: Options = {\n cwd: this.#cwd,\n permissionMode: c.permissionMode,\n allowedTools: c.allowedTools,\n disallowedTools: c.disallowedTools,\n mcpServers: c.mcpServers as Options['mcpServers'],\n settingSources: c.settingSources,\n model: c.model,\n maxTurns: c.maxTurns,\n maxBudgetUsd: c.maxBudgetUsd,\n resume: c.resume,\n forkSession: c.forkSession,\n // Open string on the wire; the SDK's union lags the CLI's vocabulary and\n // the CLI silently downgrades an effort the model doesn't support.\n effort: c.reasoningEffort as Options['effort'],\n includePartialMessages: c.includePartialMessages ?? true,\n // Without this the SDK forwards only a subagent's tool_use/tool_result\n // blocks — \"enough for a heartbeat counter\", in its own words — and its\n // prompt, thinking and final report never reach the stream at all. That\n // is not a rendering gap a client can close: a nested transcript with no\n // text in it is a list of tool names. On, therefore, because this surface\n // claims to be the session rather than a summary of it; a host that wants\n // the quieter stream sets it back through `extraOptions`, which is spread\n // last precisely so it can.\n forwardSubagentText: true,\n canUseTool: this.#canUseTool,\n env: c.env,\n pathToClaudeCodeExecutable: c.pathToClaudeCodeExecutable,\n // The CLI refuses to *switch into* bypassPermissions unless it was spawned\n // with the capability — smoke-verified: \"Cannot set permission mode to\n // bypassPermissions because the session was not launched with\n // --dangerously-skip-permissions\".\n ...(c.permissionMode === 'bypassPermissions' || c.allowDangerouslySkipPermissions\n ? { allowDangerouslySkipPermissions: true }\n : {}),\n ...c.extraOptions,\n }\n return options\n }\n\n #handleMessage(msg: SDKMessage): void {\n if (msg.type === 'system' && msg.subtype === 'init') {\n this.#sdkSessionId = msg.session_id\n this.#model = msg.model\n this.#permissionMode = msg.permissionMode\n this.#apiKeySource = msg.apiKeySource\n this.#emit({\n type: 'system_init',\n sdkSessionId: msg.session_id,\n model: msg.model,\n cwd: msg.cwd,\n apiKeySource: msg.apiKeySource,\n tools: msg.tools,\n skills: msg.skills,\n slashCommands: msg.slash_commands,\n permissionMode: msg.permissionMode,\n claudeCodeVersion: msg.claude_code_version,\n mcpServers: msg.mcp_servers,\n })\n this.#turnOverWhileBlocked = false\n this.#setStatus('running')\n void this.#fetchCapabilities()\n void this.#fetchContextUsage()\n void this.#fetchRateLimits()\n // A resumed thread usually already has one; a fresh one will not for a\n // turn or two, which is what the turn-end poll is for.\n void this.#fetchEngineTitle()\n return\n }\n if (msg.type === 'system' && msg.subtype === 'session_state_changed') {\n // Authoritative turn-over signal — but a pending approval outranks it for\n // *display*, which is not a reason to forget what it said. Remember, and\n // apply it when the approval settles.\n if (this.#pending.size > 0) {\n if (msg.state === 'idle') this.#turnOverWhileBlocked = true\n else if (msg.state === 'running') this.#turnOverWhileBlocked = false\n return\n }\n if (msg.state === 'idle') this.#setStatus('idle')\n else if (msg.state === 'running') this.#setStatus('running')\n return\n }\n const body = normalizeSdkMessage(msg)\n if (body) {\n this.#emit(body)\n if (body.type === 'conversation_reset') {\n // Same session, fresh conversation: adopt the new conversation id now\n // rather than waiting for the follow-up system_init (which only arrives\n // with the next prompt) — a dormant record written in between must\n // resume the fresh conversation, not replay the cleared one. The next\n // system_init stays authoritative and overwrites it.\n if (body.sdkSessionId) this.#sdkSessionId = body.sdkSessionId\n // The window now holds an almost-empty conversation; re-poll so clients\n // aren't left staring at the cleared conversation's reading.\n void this.#fetchContextUsage()\n }\n if (body.type === 'turn_result') {\n // total_cost_usd / num_turns are session-cumulative on each result message.\n this.#totalCostUsd = body.totalCostUsd\n this.#numTurns = body.numTurns\n // Fallback for SDK versions without session_state_changed, deferred\n // under a standing approval for the reason above.\n if (this.#pending.size === 0) this.#setStatus('idle')\n else this.#turnOverWhileBlocked = true\n // Context usage moves every turn; the poll is a cheap control request.\n void this.#fetchContextUsage()\n void this.#fetchRateLimits()\n void this.#fetchEngineTitle()\n }\n }\n }\n\n /** Ask the CLI what models/commands it supports and surface them as an event\n * (replayed to late attachers). Called eagerly for promptless sessions and again\n * on init — the flag keeps it a single emit. Optional-chained: injected fake\n * queries in tests may not implement these, and a failure must not affect the\n * session. */\n async #fetchCapabilities(): Promise<void> {\n if (this.#capabilitiesEmitted) return\n const query = this.#query\n if (typeof query?.supportedModels !== 'function' || typeof query.supportedCommands !== 'function') {\n return\n }\n try {\n const [models, commands] = await Promise.all([\n query.supportedModels(),\n query.supportedCommands(),\n ])\n if (this.#closed || this.#capabilitiesEmitted) return\n this.#capabilitiesEmitted = true\n this.#emit({\n type: 'capabilities',\n models: modelOptionsFromSdk(models),\n defaultModel: defaultModelFromSdk(models),\n commands: commands.map((c) => ({\n name: c.name,\n description: c.description,\n argumentHint: c.argumentHint,\n aliases: c.aliases,\n })),\n })\n } catch {\n // Capabilities are best-effort decoration; the session works without them.\n }\n }\n\n /**\n * Adopt the title the CLI gave this thread — the \"friendly title\" it writes a\n * turn or two into a session, and the name a resumed thread already carries.\n *\n * A **poll, not an observation**, and unavoidably so: no member of the SDK's\n * `SDKMessage` union carries it (the whole union was checked). It lives on\n * `SDKSessionInfo`, which only `getSessionInfo` / `listSessions` return — the\n * same record `GET /sdk-sessions` already serves as `SdkSessionSummary`. So it\n * is read at init and after each turn, which is also roughly the rate at which\n * it changes.\n *\n * Two rules:\n * - **Never while `meta.title` is set.** A rename is a person's decision and a\n * generated summary must not overwrite it. Not read at all in that case, so\n * there is no stored value waiting to resurface if the rename is cleared —\n * the next turn simply fetches it again.\n * - `summary` falls back to the first prompt when the session has no real\n * title yet, so it is taken only when it *differs* from `firstPrompt`.\n * Otherwise `#title()`'s own prompt fallback covers it, and the two would\n * disagree only in how they truncate.\n *\n * Best-effort throughout: an unreadable transcript, a session file that is not\n * there yet, an SDK without the function — all leave the title as it was.\n */\n async #fetchEngineTitle(): Promise<void> {\n const metaTitle = this.#config.meta?.title\n if (typeof metaTitle === 'string' && metaTitle.length > 0) return\n const sdkSessionId = this.#sdkSessionId\n if (!sdkSessionId) return\n const read = this.#config.sessionInfoFn ?? getSessionInfo\n try {\n const info = await read(sdkSessionId, { dir: this.#cwd })\n if (this.#closed || !info) return\n const summary =\n info.summary && info.summary !== info.firstPrompt ? info.summary : undefined\n const title = info.customTitle || summary\n if (title) this.#engineTitle = title\n } catch {\n // The title is decoration; a session with none works exactly as well.\n }\n }\n\n /** Snapshot the context window after a turn and surface it as an event. Optional-chained\n * and best-effort for the same reasons as #fetchCapabilities. */\n async #fetchContextUsage(): Promise<void> {\n const query = this.#query\n if (typeof query?.getContextUsage !== 'function') return\n try {\n const usage = await query.getContextUsage()\n if (this.#closed) return\n this.#emit({\n type: 'context_usage',\n usage: {\n categories: usage.categories.map((c) => ({\n name: c.name,\n tokens: c.tokens,\n color: c.color,\n })),\n totalTokens: usage.totalTokens,\n maxTokens: usage.maxTokens,\n percentage: usage.percentage,\n model: usage.model,\n },\n })\n } catch {\n // Usage is best-effort decoration; the session works without it.\n }\n }\n\n /**\n * Snapshot the plan's rate-limit windows and surface them as `rate_limit`\n * events — the same event a live `rate_limit_event` produces, so clients need\n * nothing new to render it.\n *\n * The CLI only *pushes* a window when it changes, which for a session being\n * watched rather than driven can be never; polling is what makes usage show up\n * at all. The control request is marked experimental in the SDK, name included,\n * so it is probed for by name and every failure is silent — one more reason\n * this can only ever be decoration.\n */\n async #fetchRateLimits(): Promise<void> {\n const query = this.#query as\n | { usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET?: () => Promise<unknown> }\n | undefined\n const fetchUsage = query?.usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET\n if (typeof fetchUsage !== 'function') return\n try {\n const usage = (await fetchUsage.call(query)) as UsageRateLimits\n if (this.#closed) return\n // The plan names the windows, so it goes out ahead of them — and only when\n // it changes, since this is polled after every turn and the answer is the\n // same one all session long.\n const subscriptionType = usage.subscription_type\n if (subscriptionType && subscriptionType !== this.#subscriptionType) {\n this.#subscriptionType = subscriptionType\n this.#emit({ type: 'plan_info', subscriptionType })\n }\n for (const body of rateLimitEventsFromUsage(usage)) this.#emit(body)\n } catch {\n // Best-effort, and experimental on top of that.\n }\n }\n\n #canUseTool: CanUseTool = (toolName, input, options) => {\n const id = randomUUID()\n const timeoutMs = this.#config.approvalTimeoutMs ?? this.#config.defaultApprovalTimeoutMs\n ?? DEFAULT_APPROVAL_TIMEOUT_MS\n const request: PermissionRequest = {\n id,\n toolName,\n input,\n toolUseId: options.toolUseID,\n title: options.title,\n displayName: options.displayName,\n description: options.description,\n decisionReason: options.decisionReason,\n agentId: options.agentID,\n expiresAt: Date.now() + timeoutMs,\n }\n const questionBehavior = this.#config.questionBehavior ?? 'ask'\n if (toolName === 'AskUserQuestion' && questionBehavior !== 'ask') {\n delete request.expiresAt\n return Promise.resolve(this.#resolveQuestionByPolicy(request, questionBehavior))\n }\n return new Promise<PermissionResult>((resolve) => {\n const timer = setTimeout(() => {\n const pending = this.#pending.get(id)\n if (pending) {\n this.#settleApproval(\n id,\n pending,\n { behavior: 'deny', message: 'Approval timed out' },\n 'timeout',\n )\n }\n }, timeoutMs)\n this.#pending.set(id, { request, resolve, timer })\n options.signal.addEventListener('abort', () => {\n const pending = this.#pending.get(id)\n if (pending) {\n this.#settleApproval(\n id,\n pending,\n { behavior: 'deny', message: 'Turn aborted' },\n 'policy',\n )\n }\n })\n this.#emit({ type: 'permission_requested', request })\n this.#setStatus('awaiting_approval')\n })\n }\n\n /** 'auto'/'deny' sessions settle AskUserQuestion synchronously instead of pending:\n * 'auto' picks each question's first (recommended) option, 'deny' sends the model\n * back to decide for itself. Request/resolved events still fire so transcripts and\n * job webhooks show what was chosen. */\n #resolveQuestionByPolicy(request: PermissionRequest, mode: 'auto' | 'deny'): PermissionResult {\n this.#emit({ type: 'permission_requested', request })\n if (mode === 'deny') {\n const message =\n 'Interactive questions are disabled for this session — choose the most reasonable option yourself and continue.'\n this.#emit({\n type: 'permission_resolved',\n requestId: request.id,\n behavior: 'deny',\n resolvedBy: 'policy',\n message,\n })\n return { behavior: 'deny', message, toolUseID: request.toolUseId }\n }\n this.#emit({\n type: 'permission_resolved',\n requestId: request.id,\n behavior: 'allow',\n resolvedBy: 'policy',\n })\n return {\n behavior: 'allow',\n updatedInput: { ...request.input, answers: recommendedAnswers(request.input) },\n toolUseID: request.toolUseId,\n }\n }\n\n #settleApproval(\n id: string,\n pending: PendingApproval,\n decision: PermissionDecision,\n resolvedBy: 'client' | 'timeout' | 'policy',\n ): void {\n clearTimeout(pending.timer)\n this.#pending.delete(id)\n if (decision.behavior === 'allow') {\n pending.resolve({\n behavior: 'allow',\n // The SDK requires a record here even for an unmodified allow — echo the\n // original input back when the client didn't rewrite it.\n updatedInput: decision.updatedInput ?? pending.request.input,\n toolUseID: pending.request.toolUseId,\n })\n } else {\n pending.resolve({\n behavior: 'deny',\n message: decision.message ?? 'Denied',\n interrupt: decision.interrupt,\n toolUseID: pending.request.toolUseId,\n })\n }\n this.#emit({\n type: 'permission_resolved',\n requestId: id,\n behavior: decision.behavior,\n resolvedBy,\n message: decision.behavior === 'deny' ? (decision.message ?? 'Denied') : undefined,\n })\n if (this.#pending.size === 0) {\n // The deferred turn-over wins: this approval was the only thing standing\n // between the session and the truth. Consumed either way, so a later\n // approval in a live turn cannot inherit it.\n const endedWhileBlocked = this.#turnOverWhileBlocked\n this.#turnOverWhileBlocked = false\n if (endedWhileBlocked) this.#setStatus('idle')\n else if (this.#status === 'awaiting_approval') this.#setStatus('running')\n }\n }\n\n #setStatus(status: SessionStatus, detail?: string): void {\n if (this.#status === status && this.#statusDetail === detail) return\n // Terminal states win.\n if (this.#status === 'closed' || this.#status === 'failed') return\n this.#status = status\n this.#statusDetail = detail\n this.#emit({ type: 'status_changed', status, detail })\n }\n\n #emit(body: SessionEventBody): void {\n const event: SessionEvent = { ...body, seq: ++this.#seq, ts: Date.now() }\n this.#lastActivityAt = event.ts\n // Rows, not events: what a client diffs to know how much it missed. The\n // count is monotonic across a conversation_reset on purpose — it is an\n // unread cursor, not an item count (see SessionInfo.activityCount).\n this.#activityCount += transcriptActivity(body)\n // The list's copy of the reading the transcript already has. Folded here\n // rather than at the point it is fetched, so every producer — and any\n // future one — passes through the same rule.\n this.#contextUsage = contextReading(body) ?? this.#contextUsage\n if (body.type === 'conversation_reset') {\n this.#resetSeq = event.seq\n // A reset retires the conversation the window described; the old fill is\n // not this conversation's, exactly as the transcript state clears it.\n this.#contextUsage = undefined\n }\n // Before fan-out, like #pending: a listener that reads info() on this very\n // event must see it already folded in.\n this.#subagents.observe(body, event.ts)\n this.#events.push(event)\n this.#subscribers.emit(event)\n }\n}\n\n/** Answer each AskUserQuestion question with its first option's label — the tool's\n * convention puts the recommended choice first. Keyed by question text, the shape the\n * CLI expects back in `updatedInput.answers`. */\nfunction recommendedAnswers(input: Record<string, unknown>): Record<string, string> {\n const answers: Record<string, string> = {}\n const questions = Array.isArray(input.questions) ? input.questions : []\n for (const entry of questions) {\n const q = entry as { question?: unknown; options?: unknown }\n if (typeof q.question !== 'string' || !Array.isArray(q.options)) continue\n const first = q.options[0] as { label?: unknown } | undefined\n if (typeof first?.label === 'string') answers[q.question] = first.label\n }\n return answers\n}\n","import { randomUUID } from 'node:crypto'\nimport {\n ToolLoopAgent,\n generateText,\n isStepCount,\n type LanguageModel,\n type ModelMessage,\n type ToolSet,\n} from 'ai'\nimport {\n ENGINE_CAPABILITIES,\n snapshotRetains,\n contextReading,\n type ContextReading,\n transcriptActivity,\n type ContentBlock,\n type CreateSessionRequest,\n type McpServerStatusInfo,\n type PermissionMode,\n type PermissionRequest,\n type SessionEvent,\n type SessionEventBody,\n type SessionInfo,\n type SessionStatus,\n type ToolExecutionBackend,\n} from '@workerdeck/protocol'\nimport type { SandboxVfs } from '@workerdeck/sandbox'\nimport { type AttachmentInput, attachmentRef, normalizeMediaType } from '../../lib/attachments.ts'\nimport type {\n ParkedExecution,\n PermissionDecision,\n Runner,\n RunnerSnapshot,\n SessionEventListener,\n} from '../../runner-interface.ts'\nimport type { ToolExecutionCall, ToolExecutionResult, ToolExecutor } from '../../executors/tool-executor.ts'\nimport { SubscriberSet, type SubscribeOptions } from '../../lib/subscribers.ts'\n\n/** Permission modes this engine can honor. The rest of the protocol vocabulary\n * (acceptEdits/plan/auto) is Claude Code CLI semantics with no meaning here —\n * setPermissionMode rejects them, which the server surfaces as protocol_error. */\nconst SUPPORTED_PERMISSION_MODES: readonly PermissionMode[] = ['default', 'bypassPermissions', 'dontAsk']\n\n/** `cwd` is optional for this engine: the loop has no host-filesystem coupling\n * (tools get scoped VFS handles instead). Defaults to process.cwd() for display. */\nexport type AiSdkRunnerConfig = Omit<CreateSessionRequest, 'cwd'> & {\n cwd?: string\n /** AI SDK language model instance (or gateway model id string). Provider\n * resolution from profiles happens host-side; core takes the resolved model. */\n languageModel: LanguageModel\n /** Tools available to the loop. Tools WITHOUT `execute` halt the loop when\n * called; their calls surface via `pendingToolCalls` and are answered with\n * `resolveToolCall()`, which re-enters the loop by message-state replay. */\n tools?: ToolSet\n /** System prompt (AI SDK v7 `instructions`). */\n instructions?: string\n /** Max loop steps per turn. Default 20. */\n maxSteps?: number\n /**\n * Executes tool calls the loop cannot run inline (tools declared without\n * `execute`). With one set, the runner drives the whole cycle itself:\n * dispatch on park, apply the result, re-enter. Without one, parked calls\n * stay on {@link pendingToolCalls} for the host to answer via\n * {@link resolveToolCall}.\n */\n executor?: ToolExecutor\n /** Names the executor handles. Others stay pending for the host. */\n executableTools?: string[]\n /** Scratch filesystem handed to sandboxed executions. */\n vfs?: SandboxVfs\n /** Per-execution limits passed to the executor. */\n executionLimits?: { timeoutMs?: number; memoryLimitBytes?: number }\n /** Which backend the executor represents, for `execution_dispatched` events. */\n executionBackend?: ToolExecutionBackend\n /** Swap models mid-session (`set_model`). Unset = setModel() is rejected. */\n resolveModel?: (modelId: string | undefined) => LanguageModel\n /**\n * Live MCP status for this session, when the host wired MCP at all. Unlike\n * the CLI engines — which ask their binary — this engine's MCP is entirely\n * host-assembled, so the host is the only party that can answer. Unset means\n * \"no MCP here\", which reads as an empty list rather than an error: a session\n * with no servers is a fact, not a missing feature.\n *\n * Named apart from the inherited `mcpServers` request field on purpose —\n * that one is the *wire configuration* a client asked for, this one is what\n * the host actually connected.\n */\n reportMcpServers?: () => Promise<McpServerStatusInfo[] | undefined>\n /** Called once when the session closes — release per-session resources the\n * host attached (an MCP connection, a watcher). Errors are swallowed. Also\n * runs when the session parks: parking releases the same resources. */\n onClose?: () => void | Promise<void>\n /**\n * Rebuild a parked session from {@link AiSdkRunner.park}'s snapshot instead of\n * starting a fresh one: the id, event log, seq counter, message history, and\n * the executions it parked on are all adopted. The rest of the config is the\n * live wiring (model, tools, executor, VFS) and is taken as given — a\n * rehydrated session may legitimately come up against a re-created tool set.\n */\n restore?: RunnerSnapshot\n}\n\n/** An external (execute-less) tool call the loop is parked on. */\nexport type PendingToolCall = {\n toolCallId: string\n toolName: string\n input: unknown\n /** True when the executor declared the execution deferred — the session may\n * park on it, and only a host-delivered result can settle it. */\n deferred?: boolean\n /** Epoch ms the host's execution watchdog should fire at. */\n expiresAt?: number\n}\n\n/** The provider engine's half of a {@link RunnerSnapshot} — its continuation\n * state. Opaque to the host; only this class reads it. */\nexport type AiSdkSessionState = {\n messages: ModelMessage[]\n pendingToolCalls: PendingToolCall[]\n /** Calls already handed to an executor, so rehydration never re-dispatches them. */\n dispatched: string[]\n numTurns: number\n totalUsage: { input: number; output: number; cacheWrite: number; cacheRead: number }\n /** The in-progress turn's accumulator: a parked turn's earlier legs still owe\n * their tokens and elapsed time to the turn_result that eventually lands. */\n turnAccum?: { startedAt: number; input: number; output: number; cacheWrite: number; cacheRead: number }\n permissionMode: PermissionMode\n /** Model alias last requested (config.model or a set_model), NOT the resolved\n * provider model id — re-resolution goes back through `resolveModel`. */\n model?: string\n lastActivityAt?: number\n /** When the snapshot was taken, so a rehydrated turn can discount the time it\n * spent parked instead of billing it as elapsed turn duration. */\n parkedAt?: number\n}\n\nexport type ToolCallOutput =\n | { type: 'text'; value: string }\n | { type: 'json'; value: unknown }\n\n/**\n * Model-agnostic runner over the AI SDK v7 ToolLoopAgent. The session's durable\n * state is its ModelMessage history: every turn — including continuation after an\n * externally-executed tool call — is a fresh streamed call over that history\n * (message-state replay; the loop cannot be suspended). Output is emitted as it\n * happens: `stream_delta` per token (unless includePartialMessages is false) and\n * assistant/tool messages per step. Emits the same seq-numbered SessionEvent log\n * as SessionRunner; engine-specific CLI telemetry (system_init, capabilities,\n * rate_limit, ...) is simply never emitted.\n */\nexport class AiSdkRunner implements Runner {\n readonly id: string\n readonly createdAt: number\n\n #config: AiSdkRunnerConfig\n #model: LanguageModel\n #events: SessionEvent[] = []\n #subscribers = new SubscriberSet()\n #seq = 0\n /**\n * Latest context-window reading, retained from the last `context_usage` this\n * runner emitted so `GET /sessions` can answer it without an attach — see\n * `SessionInfo.contextUsage`. Folded in the emit path, so it is by\n * construction the same number the transcript last drew.\n */\n #contextUsage: ContextReading | undefined\n #activityCount = 0\n #status: SessionStatus = 'starting'\n #permissionMode: PermissionMode\n #messages: ModelMessage[] = []\n #pendingToolCalls = new Map<string, PendingToolCall>()\n /** Calls already handed to the executor, so a re-park never double-dispatches. */\n #dispatched = new Set<string>()\n #turnChain: Promise<void> = Promise.resolve()\n #abort: AbortController | undefined\n /** Accumulates across every leg of one turn. A turn that parks on external\n * tool calls spans several generate() calls; usage and elapsed time must\n * cover all of them, not just the leg that happens to finish. */\n #turnAccum: { startedAt: number; input: number; output: number; cacheWrite: number; cacheRead: number } | undefined\n #numTurns = 0\n #totalUsage = { input: 0, output: 0, cacheWrite: 0, cacheRead: 0 }\n #lastActivityAt: number | undefined\n #started = false\n #closed = false\n /** Parked: state has been snapshotted and this instance is inert. Not closed —\n * the session lives on in the snapshot and resumes as a new instance. */\n #parked = false\n /** Model alias as requested (not the resolved provider id) — what set_model was\n * given, so a rehydrated session can re-resolve the same choice. */\n #modelAlias: string | undefined\n\n constructor(config: AiSdkRunnerConfig, id: string = randomUUID()) {\n const mode = config.permissionMode ?? 'default'\n if (!SUPPORTED_PERMISSION_MODES.includes(mode)) {\n throw new Error(`permission mode '${mode}' is not supported by the AI SDK engine`)\n }\n this.#config = config\n this.#model = config.languageModel\n this.#permissionMode = mode\n this.#modelAlias = config.model\n // A rehydrated session keeps its identity: same id, same age, same event log.\n this.id = config.restore?.id ?? id\n this.createdAt = config.restore?.createdAt ?? Date.now()\n if (config.restore) this.#restore(config.restore)\n }\n\n /** Adopt a parked session's state. The event log and seq counter come back\n * verbatim: a client reattaching with `afterSeq` must see one unbroken stream\n * across the teardown, not a second session that restarts at 1. */\n #restore(snapshot: RunnerSnapshot): void {\n if (snapshot.engine !== 'provider') {\n throw new Error(`cannot restore a '${snapshot.engine}' snapshot into the AI SDK engine`)\n }\n const state = snapshot.state as AiSdkSessionState | undefined\n if (!state || !Array.isArray(state.messages)) {\n throw new Error('session snapshot is missing its provider-engine state')\n }\n this.#seq = snapshot.seq\n this.#events = [...snapshot.events]\n // Recomputed rather than carried in the snapshot: the log IS the count, and\n // deriving it here means a rehydrated session cannot disagree with itself.\n // The context reading is derived from the same walk, under the same rule the\n // emit path uses — a session that parked with a reading must come back with\n // it, or every parked row would show an empty ring until the next turn.\n this.#activityCount = 0\n for (const event of this.#events) {\n this.#activityCount += transcriptActivity(event)\n if (event.type === 'conversation_reset') this.#contextUsage = undefined\n else this.#contextUsage = contextReading(event) ?? this.#contextUsage\n }\n this.#messages = [...state.messages]\n for (const call of state.pendingToolCalls) this.#pendingToolCalls.set(call.toolCallId, call)\n // Already handed to a backend before the teardown: re-dispatching would run\n // the work twice (and a deferred backend can only ever answer once).\n this.#dispatched = new Set(state.dispatched)\n this.#numTurns = state.numTurns\n this.#totalUsage = { ...state.totalUsage }\n this.#turnAccum = state.turnAccum ? { ...state.turnAccum } : undefined\n if (this.#turnAccum && state.parkedAt !== undefined) {\n // The turn's clock stops while parked: a run that waited two days for a\n // remote result did not take two days of turn time.\n this.#turnAccum.startedAt += Date.now() - state.parkedAt\n }\n this.#permissionMode = state.permissionMode\n this.#lastActivityAt = state.lastActivityAt\n this.#status = this.#pendingToolCalls.size > 0 ? 'parked' : 'idle'\n if (state.model !== undefined && state.model !== this.#modelAlias && this.#config.resolveModel) {\n this.#modelAlias = state.model\n this.#model = this.#config.resolveModel(state.model)\n }\n }\n\n get status(): SessionStatus {\n return this.#status\n }\n\n get lastSeq(): number {\n return this.#seq\n }\n\n /** The session's durable state — persist to park, replay to rehydrate. */\n get messages(): ModelMessage[] {\n return [...this.#messages]\n }\n\n /** External tool calls the loop is currently parked on. */\n get pendingToolCalls(): PendingToolCall[] {\n return [...this.#pendingToolCalls.values()]\n }\n\n get pendingApprovals(): PermissionRequest[] {\n return []\n }\n\n /** The session's scratch filesystem (see Runner.vfs) — the server's file\n * routes serve deliverables straight from it. */\n get vfs(): SandboxVfs | undefined {\n return this.#config.vfs\n }\n\n info(): SessionInfo {\n return {\n id: this.id,\n status: this.#status,\n // Never process.cwd(): this engine opens no directory, and reporting the\n // gateway's own deploy path to every client would leak host layout into\n // a surface that has no business seeing it.\n cwd: this.#config.cwd ?? '',\n profile: this.#config.profile,\n engine: 'provider',\n capabilities: ENGINE_CAPABILITIES.provider,\n model: this.#modelId(),\n permissionMode: this.#permissionMode,\n createdAt: this.createdAt,\n lastSeq: this.#seq,\n activityCount: this.#activityCount,\n contextUsage: this.#contextUsage,\n pendingPermissionCount: 0,\n meta: this.#config.meta,\n scope: this.#config.scope,\n title: this.#title(),\n numTurns: this.#numTurns || undefined,\n lastActivityAt: this.#lastActivityAt,\n }\n }\n\n start(): Promise<void> {\n if (this.#started) return this.#turnChain\n this.#started = true\n if (this.#config.restore) {\n // Rehydrated: the prompt was consumed by the original run, and the history\n // is whatever the snapshot captured. **Schedule nothing.** Waiting is the\n // whole point — a parked session re-enters the loop when an execution is\n // settled, and an idle one when the user says something.\n //\n // There used to be a `if (#pendingToolCalls.size === 0) #scheduleTurn()`\n // here, and it was unreachable: `park()` only ever produced a snapshot\n // while resting on deferred calls, so the size was never 0. `snapshot()`\n // makes it reachable, and it would be a live bug — an *interrupted* turn\n // leaves the history ending on the user's message (the catch path flushes\n // a partial `assistant_message` for the transcript but never pushes the\n // model's response messages, since the throw skipped that), so\n // `#runTurn`'s \"already answered\" guard would pass and the restored\n // session would re-run the very turn the user killed, unprompted, on first\n // attach. Restoring behaves exactly as the live session did: the\n // interrupted turn stays interrupted, and the next message answers both.\n return this.#turnChain\n }\n this.#setStatus('idle')\n if (this.#config.prompt) this.sendMessage(this.#config.prompt)\n return this.#turnChain\n }\n\n /**\n * Snapshot durable state, release engine resources, and go inert — the session\n * continues in the snapshot, not in this object. Returns undefined when parking\n * would lose work or has nothing to wait for: a turn in flight, no parked call,\n * or an already-closed/parked runner.\n */\n park(): RunnerSnapshot | undefined {\n if (this.#closed || this.#parked) return undefined\n // A generate() in flight cannot be snapshotted — its messages are not in the\n // history yet. Parking is only ever correct once the loop has come to rest on\n // external calls, which is exactly when #abort has been cleared.\n if (this.#abort || !this.#restingOnDeferred()) return undefined\n // Emitted before the snapshot so the persisted log carries the transition and\n // still-attached listeners see it.\n this.#setStatus('parked')\n const snapshot = this.#buildSnapshot()\n this.#parked = true\n this.#subscribers.clear()\n try {\n void Promise.resolve(this.#config.onClose?.()).catch(() => {})\n } catch {\n // Disposer errors must not break the park — the snapshot is already taken.\n }\n return snapshot\n }\n\n /**\n * The same snapshot, taken without ending anything.\n *\n * `park()` and this are two operations that happen to produce the same value,\n * and the difference is the whole point: `park()` *ends* the live runner\n * (inert, listeners dropped, `onClose` called), which is right for deferred\n * execution — the session has nothing to do for possibly days — and wrong for\n * restart-survival, where the session is active and someone is mid-\n * conversation. This one changes nothing at all: no status emit, no listener\n * clear, no disposer. The host writes the value through to durable storage\n * after each turn and keeps the runner live and warm, so a restart rebuilds\n * from the last write through the existing `restore` path and the next message\n * costs no wake.\n *\n * The gate is `park()`'s minus the requirement that there be something parked:\n *\n * - `#abort` set is refused for the reason it always was — a `generate()` in\n * flight has produced messages that are not in the history yet, so the\n * snapshot would be of a turn that half-happened.\n * - Pending calls that are **not** all deferred are refused, which is\n * `park()`'s rule wearing a different hat. An in-process execution's result\n * is coming back to *this* runner and dies with the process; a restore would\n * wait on it forever, and `state.dispatched` is what would stop the rebuilt\n * runner from simply calling it again.\n * - Idle with nothing pending — the case `park()` exists to refuse — is\n * exactly the case this exists to allow.\n */\n snapshot(): RunnerSnapshot | undefined {\n if (this.#closed || this.#parked || this.#abort) return undefined\n if (this.#pendingToolCalls.size > 0 && !this.#restingOnDeferred()) return undefined\n return this.#buildSnapshot()\n }\n\n /**\n * The snapshot value itself, shared so a park and a write-through cannot\n * disagree about what a session *is*.\n *\n * The event log is filtered through {@link snapshotRetains} — the persisted\n * log drops stream deltas, which are superseded by the `assistant_message`\n * that flushes them and would otherwise be tens of times the size of the text\n * they spell. Parks get it too, and should: a park sits on disk for days.\n *\n * The `parked` list and `state.parkedAt` are honest under both callers. An\n * idle write-through has no pending calls, so `parked` is empty and the host\n * arms no watchdogs; `parkedAt` is \"when this was taken\", which is what\n * `#restore` needs to discount a turn's clock either way.\n */\n #buildSnapshot(): RunnerSnapshot {\n const parked: ParkedExecution[] = [...this.#pendingToolCalls.values()].map((call) => ({\n executionId: call.toolCallId,\n toolName: call.toolName,\n expiresAt: call.expiresAt,\n }))\n const state: AiSdkSessionState = {\n messages: this.#messages,\n pendingToolCalls: [...this.#pendingToolCalls.values()],\n dispatched: [...this.#dispatched],\n numTurns: this.#numTurns,\n totalUsage: { ...this.#totalUsage },\n turnAccum: this.#turnAccum ? { ...this.#turnAccum } : undefined,\n permissionMode: this.#permissionMode,\n model: this.#modelAlias,\n lastActivityAt: this.#lastActivityAt,\n parkedAt: Date.now(),\n }\n return {\n engine: 'provider',\n id: this.id,\n createdAt: this.createdAt,\n seq: this.#seq,\n events: this.#events.filter((event) => snapshotRetains(event)),\n vfs: this.#config.vfs?.snapshot(),\n parked,\n state,\n }\n }\n\n sendMessage(text: string, attachments?: readonly AttachmentInput[]): void {\n if (this.#parked) throw new Error('session is parked')\n if (this.#closed) throw new Error('session is closed')\n // AI SDK v7 has one part type for attached bytes: `file`, with the media type\n // telling the provider what it is. Parts lead, text follows — same order the\n // Claude engine uses, for the same reason.\n const content = attachments?.length\n ? [\n ...attachments.map((attachment) => ({\n type: 'file' as const,\n data: attachment.data,\n mediaType: normalizeMediaType(attachment.mediaType),\n filename: attachment.name,\n })),\n ...(text ? [{ type: 'text' as const, text }] : []),\n ]\n : text\n this.#messages.push({ role: 'user', content })\n this.#emit({\n type: 'user_message',\n message: { role: 'user', content: text },\n parentToolUseId: null,\n attachments: attachments?.length ? attachments.map(attachmentRef) : undefined,\n uuid: randomUUID(),\n })\n this.#scheduleTurn()\n }\n\n /**\n * Deliver the result of an external (execute-less) tool call. Appends the\n * tool-result message and, once no calls remain pending, re-enters the loop.\n * Idempotent per toolCallId: unknown/already-settled ids return false.\n */\n resolveToolCall(toolCallId: string, output: ToolCallOutput, options?: { isError?: boolean }): boolean {\n if (!this.#settlePendingCall(toolCallId, output, options?.isError === true)) return false\n if (this.#pendingToolCalls.size === 0) this.#scheduleTurn()\n return true\n }\n\n /** Record a parked call's outcome into the message history (so it stays\n * replayable — a dangling tool call without a result is invalid input for\n * providers) and the event log. Does NOT re-enter the loop. */\n #settlePendingCall(toolCallId: string, output: ToolCallOutput, isError: boolean): boolean {\n const pending = this.#pendingToolCalls.get(toolCallId)\n if (!pending || this.#closed || this.#parked) return false\n this.#pendingToolCalls.delete(toolCallId)\n // Keep the result adjacent to the assistant message that made the call:\n // user messages typed while the turn was parked must sort AFTER the tool\n // results, or providers reject the replayed history (a tool call whose\n // result is not in the directly following message).\n let insertAt = this.#messages.length\n while (insertAt > 0 && this.#messages[insertAt - 1]!.role === 'user') insertAt--\n this.#messages.splice(insertAt, 0, {\n role: 'tool',\n content: [\n {\n type: 'tool-result',\n toolCallId,\n toolName: pending.toolName,\n output: (isError ? { type: 'error-text', value: textValue(output) } : output) as never,\n },\n ],\n })\n this.#emit({\n type: 'user_message',\n message: {\n role: 'user',\n content: [\n {\n type: 'tool_result',\n tool_use_id: toolCallId,\n content: textValue(output),\n is_error: isError || undefined,\n },\n ],\n },\n parentToolUseId: null,\n synthetic: true,\n uuid: randomUUID(),\n })\n return true\n }\n\n resolvePermission(_requestId: string, _decision: PermissionDecision): boolean {\n return false\n }\n\n /** Emit `file_delivered` — the deliver_file tool's hand-over event (wired by\n * createEngineSession via ToolContextOptions.onFileDelivered). */\n emitFileDelivered(file: { path: string; bytes: number; description?: string }): void {\n if (this.#closed || this.#parked) return\n this.#emit({ type: 'file_delivered', ...file })\n }\n\n /**\n * One plain generateText over the session's current model, billed into the\n * running turn's usage accumulator — the web_fetch digest pass uses this so\n * its tokens are never lost from the turn's accounting.\n */\n async generateDigest(prompt: string): Promise<string> {\n const result = await generateText({\n model: this.#model,\n prompt,\n abortSignal: this.#abort?.signal,\n })\n const accum = this.#turnAccum\n if (accum) {\n accum.input += result.usage.inputTokens ?? 0\n accum.output += result.usage.outputTokens ?? 0\n accum.cacheWrite += result.usage.inputTokenDetails?.cacheWriteTokens ?? 0\n accum.cacheRead += result.usage.inputTokenDetails?.cacheReadTokens ?? 0\n }\n return result.text\n }\n\n async interrupt(): Promise<void> {\n if (this.#abort) {\n this.#abort.abort()\n } else if (this.#pendingToolCalls.size > 0) {\n // A parked turn has no generate() in flight to abort. Fail the parked\n // calls (recorded as error results so the history stays replayable) and\n // finish the turn — otherwise a park nobody answers is unrecoverable.\n const accum = this.#turnAccum ?? { startedAt: Date.now(), input: 0, output: 0, cacheWrite: 0, cacheRead: 0 }\n // Snapshot first: settling mutates the map we are iterating.\n for (const call of Array.from(this.#pendingToolCalls.values())) {\n this.#settlePendingCall(call.toolCallId, { type: 'text', value: 'interrupted' }, true)\n }\n this.#dispatched.clear()\n this.#numTurns += 1\n this.#emit({\n type: 'turn_result',\n subtype: 'error_during_execution',\n isError: true,\n durationMs: Date.now() - accum.startedAt,\n numTurns: this.#numTurns,\n totalCostUsd: 0,\n errors: ['interrupted'],\n usage: turnUsage(accum),\n })\n this.#turnAccum = undefined\n this.#setStatus('idle')\n }\n await this.#turnChain\n }\n\n async setPermissionMode(mode: PermissionMode): Promise<void> {\n if (!SUPPORTED_PERMISSION_MODES.includes(mode)) {\n throw new Error(`permission mode '${mode}' is not supported by the AI SDK engine`)\n }\n this.#permissionMode = mode\n this.#emit({ type: 'permission_mode_changed', mode })\n }\n\n async setModel(model?: string): Promise<void> {\n const resolve = this.#config.resolveModel\n if (!resolve) throw new Error('set_model is not supported by this session')\n this.#model = resolve(model)\n this.#modelAlias = model\n this.#emit({ type: 'model_changed', model })\n }\n\n fail(message: string): void {\n if (this.#closed) return\n this.#emit({ type: 'session_error', message })\n this.#setStatus('failed')\n this.close('error')\n }\n\n close(reason: 'client' | 'server' | 'error' = 'client'): void {\n // Parked instances are already handed off — the host drops them from its\n // registry, and that must not read as the session ending.\n if (this.#closed || this.#parked) return\n this.#closed = true\n this.#abort?.abort()\n this.#pendingToolCalls.clear()\n this.#dispatched.clear()\n this.#emit({ type: 'session_closed', reason })\n this.#setStatus('closed')\n try {\n void Promise.resolve(this.#config.onClose?.()).catch(() => {})\n } catch {\n // Disposer errors must not break teardown.\n }\n }\n\n /** See `Runner.eventAt`. A linear scan: the one caller is a reader pressing\n * \"show everything\" on one row, so a per-runner seq index would be a map\n * maintained on every emit to save a walk nobody makes twice a minute. */\n eventAt(seq: number): SessionEvent | undefined {\n return this.#events.find((event) => event.seq === seq)\n }\n\n subscribe(\n listener: SessionEventListener,\n afterSeq = 0,\n options?: SubscribeOptions,\n ): () => void {\n return this.#subscribers.subscribe(this.#events, listener, afterSeq, options)\n }\n\n #scheduleTurn(): void {\n this.#turnChain = this.#turnChain.then(() => this.#runTurn())\n }\n\n /**\n * Deliver the result of an execution this runner dispatched. Used by the host\n * when a backend settled out-of-band (a browser bridge answering later, a\n * deferred executor). Idempotent by executionId.\n */\n settleExecution(executionId: string, result: ToolExecutionResult): boolean {\n if (this.#closed || this.#parked) return false\n if (!this.#pendingToolCalls.has(executionId)) return false\n this.#applyExecutionResult(executionId, result)\n return true\n }\n\n /** Hand every parked call the executor owns to it. */\n #dispatchPending(): void {\n const executor = this.#config.executor\n if (!executor) return\n const executable = this.#config.executableTools\n const inFlight: Array<Promise<unknown>> = []\n let anyDeferred = false\n // Snapshot first: applying a result mutates the map we are iterating.\n for (const call of Array.from(this.#pendingToolCalls.values())) {\n if (executable && !executable.includes(call.toolName)) continue\n if (this.#dispatched.has(call.toolCallId)) continue\n this.#dispatched.add(call.toolCallId)\n const toolCall: ToolExecutionCall = {\n executionId: call.toolCallId,\n sessionId: this.id,\n tool: call.toolName,\n input: call.input,\n vfs: this.#config.vfs,\n limits: this.#config.executionLimits,\n signal: this.#abort?.signal,\n }\n // Per call, not per executor: a routing executor may keep one tool in\n // process and defer another, and only the deferred one may park us.\n const profile = executor.describe?.(toolCall) ?? {}\n call.deferred = profile.deferred === true ? true : undefined\n call.expiresAt = profile.timeoutMs === undefined ? undefined : Date.now() + profile.timeoutMs\n anyDeferred ||= call.deferred === true\n this.#emit({\n type: 'execution_dispatched',\n executionId: call.toolCallId,\n toolName: call.toolName,\n backend: profile.backend ?? this.#config.executionBackend ?? 'server',\n deferred: call.deferred,\n expiresAt: call.expiresAt,\n })\n inFlight.push(\n executor\n .dispatch(toolCall)\n .then((dispatch) => {\n // 'pending' means the result arrives later via settleExecution().\n if (dispatch.status === 'settled') {\n this.#applyExecutionResult(call.toolCallId, dispatch.result)\n }\n })\n .catch((error: unknown) => {\n this.#applyExecutionResult(call.toolCallId, {\n status: 'failed',\n reason: 'dispatch_error',\n error: error instanceof Error ? error.message : String(error),\n })\n }),\n )\n }\n // Announce the park only once every dispatch of this batch has been handed\n // over: a host that parks on the first announcement would snapshot a session\n // whose remaining calls are still being dispatched — and dispatch them into a\n // runner it had already discarded.\n if (anyDeferred) void Promise.allSettled(inFlight).then(() => this.#announceParked())\n }\n\n /**\n * The turn has come to rest on deferred executions: nothing is in flight, and\n * only a host-delivered result can move it. `status_changed: 'parked'` is the\n * host's cue to snapshot via {@link park} — a single, correctly-timed signal\n * rather than an inference from individual dispatch events.\n */\n #announceParked(): void {\n if (this.#closed || this.#parked || this.#abort) return\n if (this.#restingOnDeferred()) this.#setStatus('parked')\n }\n\n /** The loop is waiting, and everything it waits on can only be answered from\n * outside this process. One still-live in-process execution means a result is\n * coming back to THIS runner, and tearing it down would strand it. */\n #restingOnDeferred(): boolean {\n if (this.#pendingToolCalls.size === 0) return false\n for (const call of this.#pendingToolCalls.values()) {\n if (call.deferred !== true) return false\n }\n return true\n }\n\n /** Fold an execution's outcome back into the loop, whichever way it went. */\n #applyExecutionResult(executionId: string, result: ToolExecutionResult): void {\n // A parked instance is not the session any more: its rehydrated successor owns\n // the pending call, and applying here would write into a discarded history.\n if (this.#closed || this.#parked) return\n this.#dispatched.delete(executionId)\n if (result.status === 'ok') {\n this.#emit({\n type: 'execution_result',\n executionId,\n output: { type: 'json', value: result.output },\n logs: result.logs,\n })\n this.resolveToolCall(executionId, { type: 'json', value: result.output })\n return\n }\n this.#emit({\n type: 'execution_failed',\n executionId,\n reason: result.reason,\n error: result.error,\n logs: result.logs,\n })\n // A failed execution is ordinary tool output: the agent gets to adapt.\n this.resolveToolCall(\n executionId,\n { type: 'text', value: `${result.reason}: ${result.error}` },\n { isError: true },\n )\n }\n\n async #runTurn(): Promise<void> {\n if (this.#closed || this.#parked || this.#pendingToolCalls.size > 0) return\n // Nothing to respond to: the history already ends with the assistant.\n // Happens when several triggers queued turns for the same input (a message\n // typed mid-park + the park resolving) — one turn answers all of it, the\n // stragglers must not burn a generate() on an already-answered history.\n if (this.#messages.at(-1)?.role === 'assistant') return\n this.#setStatus('running')\n const agent = new ToolLoopAgent({\n model: this.#model,\n tools: this.#config.tools ?? {},\n instructions: this.#config.instructions,\n stopWhen: isStepCount(this.#config.maxSteps ?? 20),\n })\n const abort = new AbortController()\n this.#abort = abort\n const accum = (this.#turnAccum ??= {\n startedAt: Date.now(),\n input: 0,\n output: 0,\n cacheWrite: 0,\n cacheRead: 0,\n })\n // Completed blocks of the step in progress, flushed as an assistant\n // message at each tool call (its result may follow immediately and the\n // transcript needs the call first) and at every step boundary. Declared\n // outside the try: the catch flushes what an interrupted turn had produced.\n let blocks: ContentBlock[] = []\n const textBuf = new Map<string, string>()\n const reasoningBuf = new Map<string, string>()\n const flush = (): void => {\n if (blocks.length === 0) return\n this.#emit({\n type: 'assistant_message',\n message: { role: 'assistant', content: blocks, model: this.#modelId() },\n parentToolUseId: null,\n uuid: randomUUID(),\n })\n blocks = []\n }\n try {\n // Streamed, not generate(): a multi-step turn must reach the transcript\n // as it happens — token deltas while text is produced, each step's\n // messages the moment the step completes — not as one blob at the end.\n const result = await agent.stream({\n messages: [...this.#messages],\n abortSignal: abort.signal,\n })\n const partials = this.#config.includePartialMessages !== false\n const emitToolResult = (toolCallId: string, content: string, isError?: boolean): void => {\n flush()\n this.#emit({\n type: 'user_message',\n message: {\n role: 'user',\n content: [{ type: 'tool_result', tool_use_id: toolCallId, content, is_error: isError }],\n },\n parentToolUseId: null,\n synthetic: true,\n uuid: randomUUID(),\n })\n }\n let streamError: unknown\n for await (const part of result.fullStream) {\n if (this.#closed) break\n switch (part.type) {\n case 'text-delta':\n textBuf.set(part.id, (textBuf.get(part.id) ?? '') + part.text)\n if (partials) {\n this.#emit({\n type: 'stream_delta',\n event: { type: 'content_block_delta', delta: { type: 'text_delta', text: part.text } },\n parentToolUseId: null,\n uuid: randomUUID(),\n })\n }\n break\n case 'text-end': {\n const text = textBuf.get(part.id)\n textBuf.delete(part.id)\n if (text) blocks.push({ type: 'text', text })\n break\n }\n case 'reasoning-delta':\n reasoningBuf.set(part.id, (reasoningBuf.get(part.id) ?? '') + part.text)\n if (partials) {\n this.#emit({\n type: 'stream_delta',\n event: { type: 'content_block_delta', delta: { type: 'thinking_delta', thinking: part.text } },\n parentToolUseId: null,\n uuid: randomUUID(),\n })\n }\n break\n case 'reasoning-end': {\n const thinking = reasoningBuf.get(part.id)\n reasoningBuf.delete(part.id)\n if (thinking) blocks.push({ type: 'thinking', thinking })\n break\n }\n case 'tool-call':\n blocks.push({\n type: 'tool_use',\n id: part.toolCallId,\n name: part.toolName,\n input: part.input,\n })\n flush()\n break\n case 'tool-result':\n emitToolResult(\n part.toolCallId,\n typeof part.output === 'string' ? part.output : JSON.stringify(part.output),\n )\n break\n case 'tool-error':\n emitToolResult(part.toolCallId, errorText(part.error), true)\n break\n case 'finish-step':\n flush()\n break\n case 'error':\n streamError ??= part.error\n break\n default:\n break\n }\n }\n flush()\n if (streamError !== undefined) throw streamError\n if (abort.signal.aborted) throw new Error('interrupted')\n const [responseMessages, usage, toolCalls, text] = await Promise.all([\n result.responseMessages,\n result.totalUsage,\n result.toolCalls,\n result.text,\n ])\n if (this.#closed) return\n // v7's totalUsage is already cumulative across THIS call's steps — add it\n // once per leg, never per step.\n accum.input += usage.inputTokens ?? 0\n accum.output += usage.outputTokens ?? 0\n accum.cacheWrite += usage.inputTokenDetails?.cacheWriteTokens ?? 0\n accum.cacheRead += usage.inputTokenDetails?.cacheReadTokens ?? 0\n this.#messages.push(...(responseMessages as ModelMessage[]))\n // Tool calls the SDK did not execute locally (no `execute`) park the loop.\n // Settled = every call with a tool message in the response — NOT\n // `result.toolResults`, which omits errored executions (`tool-error`\n // parts). An errored call was already fed back to the model by the SDK;\n // parking on it would hang the session forever (nobody owns it).\n const settled = new Set<string>()\n for (const message of responseMessages as ModelMessage[]) {\n if (message.role !== 'tool' || !Array.isArray(message.content)) continue\n for (const part of message.content) {\n if (part.type === 'tool-result') settled.add(part.toolCallId)\n }\n }\n for (const call of toolCalls) {\n if (settled.has(call.toolCallId)) continue\n this.#pendingToolCalls.set(call.toolCallId, {\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n input: call.input,\n })\n }\n if (this.#pendingToolCalls.size > 0) {\n // Parked: no turn_result yet. With an executor wired in, drive the\n // executions ourselves; otherwise the host answers via resolveToolCall.\n this.#dispatchPending()\n return\n }\n this.#finishTurn(text)\n } catch (error) {\n if (this.#closed) return\n // What the turn produced before it died is part of the record: without a\n // durable assistant_message the partial text exists only as stream\n // deltas, which the client holds in a singleton streaming item — wiped\n // by the *next* turn's message and glued onto by its deltas. An\n // interrupted minute of output must not vanish on the next question or\n // the next attach. Buffers still holding text mean the abort cut a block\n // mid-stream (no `text-end` came); completed-but-unflushed blocks are in\n // `blocks` already.\n for (const [, thinking] of reasoningBuf) {\n if (thinking) blocks.push({ type: 'thinking', thinking })\n }\n for (const [, text] of textBuf) {\n if (text) blocks.push({ type: 'text', text })\n }\n flush()\n const message = error instanceof Error ? error.message : String(error)\n this.#numTurns += 1\n this.#emit({\n type: 'turn_result',\n subtype: 'error_during_execution',\n isError: true,\n durationMs: Date.now() - accum.startedAt,\n numTurns: this.#numTurns,\n totalCostUsd: 0,\n errors: [abort.signal.aborted ? 'interrupted' : message],\n usage: turnUsage(accum),\n })\n this.#turnAccum = undefined\n this.#setStatus('idle')\n } finally {\n if (this.#abort === abort) this.#abort = undefined\n }\n }\n\n /** Emit the turn's result from the whole-turn accumulator, so a turn that\n * parked on external tool calls reports every leg's tokens and the full\n * elapsed time (including the time spent executing those tools). */\n #finishTurn(text: string): void {\n const accum = this.#turnAccum ?? { startedAt: Date.now(), input: 0, output: 0, cacheWrite: 0, cacheRead: 0 }\n this.#numTurns += 1\n this.#totalUsage.input += accum.input\n this.#totalUsage.output += accum.output\n this.#totalUsage.cacheWrite += accum.cacheWrite\n this.#totalUsage.cacheRead += accum.cacheRead\n this.#emit({\n type: 'turn_result',\n subtype: 'success',\n isError: false,\n durationMs: Date.now() - accum.startedAt,\n numTurns: this.#numTurns,\n totalCostUsd: 0,\n result: text,\n usage: turnUsage(accum),\n })\n this.#turnAccum = undefined\n this.#setStatus('idle')\n }\n\n #modelId(): string | undefined {\n const model = this.#model\n if (typeof model === 'string') return model\n return (model as { modelId?: string }).modelId\n }\n\n #title(): string | undefined {\n const metaTitle = this.#config.meta?.title\n if (typeof metaTitle === 'string' && metaTitle.length > 0) return metaTitle\n const prompt = this.#config.prompt\n if (!prompt) return undefined\n return prompt.length > 80 ? prompt.slice(0, 77) + '…' : prompt\n }\n\n /**\n * This session's MCP servers, as the host assembled them.\n *\n * Always answers — an empty list when no MCP was wired — because the\n * alternative (undefined, which the server turns into a 501) says \"this\n * engine cannot tell you\", and this engine can: the host that built the\n * session is the only party who knows, and it has been asked.\n */\n async mcpServers(): Promise<McpServerStatusInfo[] | undefined> {\n return (await this.#config.reportMcpServers?.()) ?? []\n }\n\n /** Host-facing rename: writes `meta.title`, which `#title()` prefers. Clearing\n * it (undefined) restores the derived title. The engine is never told. */\n setTitle(title: string | undefined): void {\n const meta = { ...this.#config.meta }\n if (title) meta.title = title\n else delete meta.title\n this.#config = { ...this.#config, meta }\n }\n\n #setStatus(status: SessionStatus, detail?: string): void {\n if (this.#status === status) return\n if (this.#status === 'closed' || this.#status === 'failed') return\n this.#status = status\n this.#emit({ type: 'status_changed', status, detail })\n }\n\n #emit(body: SessionEventBody): void {\n const event: SessionEvent = { ...body, seq: ++this.#seq, ts: Date.now() }\n this.#lastActivityAt = event.ts\n // Rows, not events: what a client diffs to know how much it missed.\n this.#activityCount += transcriptActivity(body)\n // The list's copy of the reading the transcript already has. Folded here\n // rather than at the point it is fetched, so every producer — and any\n // future one — passes through the same rule.\n this.#contextUsage = contextReading(body) ?? this.#contextUsage\n // A reset retires the conversation the window described; the old fill is\n // not this conversation's, exactly as the transcript state clears it.\n if (body.type === 'conversation_reset') this.#contextUsage = undefined\n this.#events.push(event)\n this.#subscribers.emit(event)\n }\n}\n\nfunction turnUsage(accum: { input: number; output: number; cacheWrite: number; cacheRead: number }) {\n return {\n input_tokens: accum.input,\n output_tokens: accum.output,\n cache_creation_input_tokens: accum.cacheWrite,\n cache_read_input_tokens: accum.cacheRead,\n }\n}\n\nfunction textValue(output: ToolCallOutput): string {\n return output.type === 'text' ? output.value : JSON.stringify(output.value)\n}\n\nfunction errorText(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n","import { execFile } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\n\n/**\n * Credential presence for one Claude Code environment, as the CLI itself reports\n * it. 'unknown' means the check could not run at all (no binary, a CLI too old\n * for `auth status`, unparseable output) — which is NOT evidence of a missing\n * login and must never be surfaced as one.\n */\nexport type ClaudeAuthStatus = 'logged_in' | 'logged_out' | 'unknown'\n\n/** Injectable form of {@link checkClaudeAuth} (tests, custom probes). */\nexport type ClaudeAuthProbe = (\n env: Record<string, string | undefined>,\n) => Promise<ClaudeAuthStatus>\n\n/**\n * The native Claude Code binary the Agent SDK itself spawns, resolved the way\n * the SDK resolves it: the platform-specific optional dependency installed next\n * to the SDK package (`@anthropic-ai/claude-agent-sdk-<platform>-<arch>/claude`).\n * Probing this binary rather than whatever `claude` is on PATH means an auth\n * check answers for the executable sessions will actually run — the two can be\n * different versions logged into different places. Returns undefined when it\n * can't be found (optional dep skipped, unsupported platform); callers degrade\n * to 'unknown', and the SDK surfaces its own error if a session is created.\n */\nexport function resolveBundledClaudeExecutable(): string | undefined {\n try {\n // Two hops on purpose: the platform package is a dependency of the SDK, not\n // of this package, so under pnpm's strict layout it only resolves from the\n // SDK's own location.\n const fromHere = createRequire(import.meta.url)\n const fromSdk = createRequire(fromHere.resolve('@anthropic-ai/claude-agent-sdk'))\n const suffix = process.platform === 'win32' ? '.exe' : ''\n // On linux only the matching libc variant installs (os/cpu/libc on the\n // optional deps), so trying both flavours needs no musl detection.\n const platforms =\n process.platform === 'linux'\n ? [`linux-${process.arch}`, `linux-${process.arch}-musl`]\n : [`${process.platform}-${process.arch}`]\n for (const platform of platforms) {\n try {\n const path = fromSdk.resolve(`@anthropic-ai/claude-agent-sdk-${platform}/claude${suffix}`)\n if (existsSync(path)) return path\n } catch {\n // not installed — try the next candidate\n }\n }\n } catch {\n // the SDK itself doesn't resolve here; nothing to probe\n }\n return undefined\n}\n\n/**\n * Ask the CLI whether `env` holds usable credentials: `claude auth status`\n * prints a JSON verdict covering every source the CLI itself consults for that\n * environment — `<CLAUDE_CONFIG_DIR>/.credentials.json`, the macOS login\n * Keychain, ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, Bedrock/Vertex\n * (verified against 2.1.217). Only the `loggedIn` boolean is ever read; the\n * identity fields in the payload (email, org, subscription) never leave the\n * parse. The exit code is deliberately ignored — 2.1.217 exits 1 on a\n * logged-out verdict where other versions exit 0 — and anything that doesn't\n * parse to a `loggedIn` boolean is 'unknown', because `auth status` is not a\n * stable contract. Never rejects.\n */\nexport function checkClaudeAuth(\n env: Record<string, string | undefined>,\n options: { executable?: string; timeoutMs?: number } = {},\n): Promise<ClaudeAuthStatus> {\n const executable = options.executable ?? resolveBundledClaudeExecutable()\n if (!executable) return Promise.resolve('unknown')\n return new Promise((resolve) => {\n execFile(\n executable,\n ['auth', 'status'],\n // The timeout kills a hung CLI rather than leaking it; the killed child's\n // partial output then fails the parse below, which is the right verdict.\n { env: env as NodeJS.ProcessEnv, timeout: options.timeoutMs ?? 10_000 },\n (_error, stdout) => {\n try {\n const parsed = JSON.parse(stdout) as { loggedIn?: unknown }\n if (typeof parsed.loggedIn === 'boolean') {\n resolve(parsed.loggedIn ? 'logged_in' : 'logged_out')\n return\n }\n } catch {\n // not this CLI's JSON — fall through\n }\n resolve('unknown')\n },\n )\n })\n}\n","import { runScript, type SandboxEngine } from '@workerdeck/sandbox'\nimport type {\n ToolExecutionCall,\n ToolExecutionDispatch,\n ToolExecutionResult,\n ToolExecutor,\n} from './tool-executor.ts'\n\n/** Resolve a URL to text for the guest. Runs host-side with host authority —\n * this is where a credential may be attached, never inside the sandbox. */\nexport type HostFetch = (url: string, signal: AbortSignal) => Promise<string>\n\nexport type QuickJsExecutorOptions = {\n engine: SandboxEngine\n /**\n * Hostnames the guest may reach, exact or `*.example.com`. Empty/unset =\n * no network at all (the guest's fetchText throws). Matched host-side; the\n * guest is never told the allowlist and never holds a credential.\n */\n allowedHosts?: string[]\n /** Performs the actual request. Unset = global fetch, text body. */\n hostFetch?: HostFetch\n /** Per-fetch cap. The guest deadline does NOT cover host-function time, so\n * every capability needs its own bound. Default 10000. */\n fetchTimeoutMs?: number\n /** Default guest wall-clock limit when the call doesn't set one. Default 5000. */\n defaultTimeoutMs?: number\n /** Default guest allocator cap when the call doesn't set one. Default 64 MiB. */\n defaultMemoryLimitBytes?: number\n}\n\n/** Tool input for `eval_script`. */\ntype EvalScriptInput = { script?: unknown }\n\n/**\n * In-process execution backend: runs a tool's untrusted script in the QuickJS\n * WASM guest. Always settles inline — nothing downstream assumes that, which is\n * what lets a deferred backend replace it behind the same seam.\n */\nexport class QuickJsExecutor implements ToolExecutor {\n #options: QuickJsExecutorOptions\n\n constructor(options: QuickJsExecutorOptions) {\n this.#options = options\n }\n\n async dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch> {\n return {\n executionId: call.executionId,\n status: 'settled',\n result: await this.#execute(call),\n }\n }\n\n async #execute(call: ToolExecutionCall): Promise<ToolExecutionResult> {\n if (call.tool !== 'eval_script') {\n return {\n status: 'failed',\n reason: 'unsupported_tool',\n error: `tool '${call.tool}' is not executable by the QuickJS backend`,\n }\n }\n const script = (call.input as EvalScriptInput | undefined)?.script\n if (typeof script !== 'string') {\n return {\n status: 'failed',\n reason: 'invalid_input',\n error: 'eval_script requires a string `script` input',\n }\n }\n const result = await runScript(this.#options.engine, {\n script,\n vfs: call.vfs,\n signal: call.signal,\n timeoutMs: call.limits?.timeoutMs ?? this.#options.defaultTimeoutMs ?? 5000,\n memoryLimitBytes:\n call.limits?.memoryLimitBytes ?? this.#options.defaultMemoryLimitBytes ?? 64 * 1024 * 1024,\n fetchText: this.#allowsNetwork() ? (url) => this.#fetchText(url, call.signal) : undefined,\n })\n const logs = result.logs.map((l) => `[${l.level}] ${l.text}`)\n return result.ok\n ? { status: 'ok', output: result.value, logs }\n : { status: 'failed', reason: result.reason, error: result.error, logs }\n }\n\n #allowsNetwork(): boolean {\n return (this.#options.allowedHosts?.length ?? 0) > 0\n }\n\n async #fetchText(url: string, outer: AbortSignal | undefined): Promise<string> {\n if (!isHostAllowed(url, this.#options.allowedHosts ?? [])) {\n throw new Error(`host not allowed: ${safeHost(url) ?? url}`)\n }\n // The guest's interrupt deadline cannot preempt a host call — bound it here.\n const controller = new AbortController()\n const onOuterAbort = () => controller.abort()\n outer?.addEventListener('abort', onOuterAbort)\n const timer = setTimeout(() => controller.abort(), this.#options.fetchTimeoutMs ?? 10_000)\n try {\n const fetchImpl = this.#options.hostFetch ?? defaultHostFetch\n return await fetchImpl(url, controller.signal)\n } finally {\n clearTimeout(timer)\n outer?.removeEventListener('abort', onOuterAbort)\n }\n }\n}\n\nasync function defaultHostFetch(url: string, signal: AbortSignal): Promise<string> {\n const response = await fetch(url, { signal })\n if (!response.ok) throw new Error(`request failed: ${response.status}`)\n return await response.text()\n}\n\nfunction safeHost(url: string): string | undefined {\n try {\n return new URL(url).hostname\n } catch {\n return undefined\n }\n}\n\n/** Exact hostname match, or a single leading `*.` wildcard covering subdomains\n * (not the bare parent). Only http(s) — no file:, data:, or other schemes. */\nexport function isHostAllowed(url: string, allowedHosts: string[]): boolean {\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n return false\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') return false\n const host = parsed.hostname.toLowerCase()\n return allowedHosts.some((entry) => {\n const pattern = entry.trim().toLowerCase()\n if (!pattern) return false\n if (pattern.startsWith('*.')) return host.endsWith(pattern.slice(1))\n return host === pattern\n })\n}\n","/**\n * One registry for every request that leaves the runner and must come back:\n * permission approvals, browser-bridged tool calls, and deferred executions.\n * They differ only in who answers and how long that takes — the correlation,\n * timeout, idempotent settle, and provenance tagging are identical, so they\n * live here once.\n */\n\n/** What kind of async request this is. Purely descriptive — the mechanics are shared. */\nexport type PendingKind = 'approval' | 'tool_call' | 'execution'\n\n/** Who settled a request. Mirrors the existing approval vocabulary. */\nexport type SettledBy = 'client' | 'timeout' | 'policy' | 'server'\n\nexport type PendingOutcome<T> =\n | { ok: true; value: T; settledBy: SettledBy }\n | { ok: false; reason: string; error: string; settledBy: SettledBy }\n\nexport type PendingEntry = {\n id: string\n kind: PendingKind\n createdAt: number\n /** Epoch ms the timeout policy fires at, when one was set. */\n expiresAt?: number\n /** Caller-supplied descriptor for display/rehydration (tool name, request, ...). */\n meta?: Record<string, unknown>\n}\n\ntype Slot<T> = PendingEntry & {\n resolve: (outcome: PendingOutcome<T>) => void\n timer?: ReturnType<typeof setTimeout>\n}\n\nexport type RegisterOptions<T> = {\n id: string\n kind: PendingKind\n /** Fail the request automatically after this long. Omit for no deadline\n * (deferred executions whose watchdog lives elsewhere). */\n timeoutMs?: number\n meta?: Record<string, unknown>\n /** Called when the entry settles, however it settled. For emitting events. */\n onSettle?: (outcome: PendingOutcome<T>, entry: PendingEntry) => void\n}\n\nexport class PendingRequestRegistry {\n #slots = new Map<string, Slot<unknown>>()\n\n get size(): number {\n return this.#slots.size\n }\n\n /**\n * Register a request and get a promise for its outcome. The promise **never\n * rejects**: a timeout or cancellation resolves with `ok: false` so callers\n * feed the failure back into the agent loop instead of unwinding it.\n *\n * Re-registering a live id throws — silently replacing it would strand the\n * first waiter forever.\n */\n register<T>(options: RegisterOptions<T>): Promise<PendingOutcome<T>> {\n if (this.#slots.has(options.id)) {\n throw new Error(`pending request '${options.id}' is already registered`)\n }\n const entry: PendingEntry = {\n id: options.id,\n kind: options.kind,\n createdAt: Date.now(),\n expiresAt: options.timeoutMs === undefined ? undefined : Date.now() + options.timeoutMs,\n meta: options.meta,\n }\n return new Promise<PendingOutcome<T>>((resolve) => {\n const slot: Slot<T> = {\n ...entry,\n resolve: (outcome) => {\n options.onSettle?.(outcome, entry)\n resolve(outcome)\n },\n }\n if (options.timeoutMs !== undefined) {\n slot.timer = setTimeout(() => {\n this.#settle(options.id, {\n ok: false,\n reason: 'timeout',\n error: `request timed out after ${options.timeoutMs}ms`,\n settledBy: 'timeout',\n })\n }, options.timeoutMs)\n slot.timer.unref?.()\n }\n this.#slots.set(options.id, slot as Slot<unknown>)\n })\n }\n\n /** Deliver a result. Returns false for unknown or already-settled ids —\n * duplicate and late deliveries are no-ops, never a second application. */\n settle<T>(id: string, value: T, settledBy: SettledBy = 'client'): boolean {\n return this.#settle(id, { ok: true, value, settledBy })\n }\n\n /** Fail a request. Same idempotence guarantee as {@link settle}. */\n fail(id: string, reason: string, error: string, settledBy: SettledBy = 'server'): boolean {\n return this.#settle(id, { ok: false, reason, error, settledBy })\n }\n\n has(id: string): boolean {\n return this.#slots.has(id)\n }\n\n get(id: string): PendingEntry | undefined {\n const slot = this.#slots.get(id)\n return slot && toEntry(slot)\n }\n\n list(kind?: PendingKind): PendingEntry[] {\n const entries = [...this.#slots.values()].map(toEntry)\n return kind ? entries.filter((e) => e.kind === kind) : entries\n }\n\n /** Fail everything (optionally of one kind) — session close, turn interrupt. */\n cancelAll(reason: string, error: string, kind?: PendingKind): number {\n let canceled = 0\n // Snapshot ids first: settling mutates the map we would be iterating.\n for (const slot of Array.from(this.#slots.values())) {\n if (kind && slot.kind !== kind) continue\n if (this.#settle(slot.id, { ok: false, reason, error, settledBy: 'server' })) canceled += 1\n }\n return canceled\n }\n\n #settle(id: string, outcome: PendingOutcome<unknown>): boolean {\n const slot = this.#slots.get(id)\n if (!slot) return false\n clearTimeout(slot.timer)\n this.#slots.delete(id)\n slot.resolve(outcome)\n return true\n }\n}\n\nfunction toEntry(slot: Slot<unknown>): PendingEntry {\n return {\n id: slot.id,\n kind: slot.kind,\n createdAt: slot.createdAt,\n expiresAt: slot.expiresAt,\n meta: slot.meta,\n }\n}\n","import type { ToolCallRequestFrame, ToolExecutionOutput } from '@workerdeck/protocol'\nimport { PendingRequestRegistry, type PendingOutcome } from '../lib/pending-registry.ts'\nimport type {\n ToolExecutionCall,\n ToolExecutionDispatch,\n ToolExecutionResult,\n ToolExecutor,\n} from './tool-executor.ts'\n\n/** Answer a bridged call, as delivered by the client over the wire. */\nexport type BridgeAnswer =\n | { output: ToolExecutionOutput; logs?: string[] }\n | { reason: string; error: string; logs?: string[] }\n\nexport type BrowserBridgeExecutorOptions = {\n /**\n * Put a `tool_call_request` on the wire to the attached client. Returning\n * false means nobody is attached — the execution fails immediately rather\n * than hanging until its deadline.\n */\n send: (frame: ToolCallRequestFrame) => boolean\n /** Tell the client to abandon a call the server gave up on. */\n cancel?: (executionId: string, reason: string) => void\n /** How long to wait for the client before failing the execution. Default 60000. */\n timeoutMs?: number\n /**\n * Called once per dispatched execution when it reaches a terminal result,\n * however it got there (client answer, timeout, abort, no client). This is\n * the wire back into the agent loop — the host feeds it to the runner's\n * `resolveToolCall`. A timeout arrives here as a failed result, not silence.\n */\n onResult?: (executionId: string, result: ToolExecutionResult) => void\n /** Share the session's registry so approvals, bridged calls, and deferred\n * executions live in one table. Omit to get a private one. */\n registry?: PendingRequestRegistry\n}\n\n/**\n * Executes tool calls in the attached client's own sandbox. The first backend\n * that genuinely returns `pending`: dispatch puts a request on the wire and\n * returns, and the result arrives later through {@link resolve}.\n *\n * Data locality is the point — documents can stay in the browser and never\n * reach the server. The tradeoff is trust: whatever comes back is untrusted\n * input, fine for the user's own data but never a source for authoritative\n * server state (that is why MCP and secret-bearing tools are never bridged).\n */\nexport class BrowserBridgeExecutor implements ToolExecutor {\n readonly registry: PendingRequestRegistry\n #options: BrowserBridgeExecutorOptions\n /** Results that arrive before dispatch registers them (fast client, slow\n * bookkeeping) would otherwise be dropped — hold them briefly. */\n #early = new Map<string, BridgeAnswer>()\n\n constructor(options: BrowserBridgeExecutorOptions) {\n this.#options = options\n this.registry = options.registry ?? new PendingRequestRegistry()\n }\n\n async dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch> {\n const timeoutMs = call.limits?.timeoutMs ?? this.#options.timeoutMs ?? 60_000\n const expiresAt = Date.now() + timeoutMs\n const frame: ToolCallRequestFrame = {\n type: 'tool_call_request',\n executionId: call.executionId,\n toolName: call.tool,\n input: call.input,\n vfsSeed: call.vfs?.snapshot(),\n limits: call.limits,\n expiresAt,\n }\n\n const settled = this.registry.register<BridgeAnswer>({\n id: call.executionId,\n kind: 'tool_call',\n timeoutMs,\n meta: { toolName: call.tool, sessionId: call.sessionId },\n })\n\n if (!this.#options.send(frame)) {\n this.registry.fail(call.executionId, 'no_client', 'no client is attached to execute this call')\n // Nobody can ever answer this one — settle it inline rather than making\n // the caller wait out a deadline for a result that cannot come.\n return {\n executionId: call.executionId,\n status: 'settled',\n result: toExecutionResult(await settled),\n }\n }\n\n // Drain an answer that beat the registration.\n const early = this.#early.get(call.executionId)\n if (early) {\n this.#early.delete(call.executionId)\n this.#applyAnswer(call.executionId, early)\n }\n\n // Only fail it here — the settle handler below owns sending the cancel, so\n // every non-client failure notifies the client exactly once.\n const onAbort = () => {\n this.registry.fail(call.executionId, 'aborted', 'the turn was interrupted')\n }\n call.signal?.addEventListener('abort', onAbort, { once: true })\n void settled.then((outcome) => {\n call.signal?.removeEventListener('abort', onAbort)\n // Let the client stop working on anything it can no longer answer.\n if (!outcome.ok && outcome.settledBy !== 'client') {\n this.#options.cancel?.(call.executionId, outcome.reason)\n }\n this.#options.onResult?.(call.executionId, toExecutionResult(outcome))\n })\n\n return { executionId: call.executionId, status: 'pending' }\n }\n\n /**\n * Apply a client's answer. Returns false when the id is unknown or already\n * settled — a late result after a timeout must not re-open a settled call.\n */\n resolve(executionId: string, answer: BridgeAnswer): boolean {\n if (!this.registry.has(executionId)) {\n // Racing a dispatch still in flight; hold it briefly for the drain above.\n this.#early.set(executionId, answer)\n setTimeout(() => this.#early.delete(executionId), 5000).unref?.()\n return false\n }\n return this.#applyAnswer(executionId, answer)\n }\n\n #applyAnswer(executionId: string, answer: BridgeAnswer): boolean {\n return 'output' in answer\n ? this.registry.settle(executionId, answer, 'client')\n : this.registry.fail(executionId, answer.reason, answer.error, 'client')\n }\n}\n\n/** Map a registry outcome onto the executor's result contract. */\nexport function toExecutionResult(outcome: PendingOutcome<BridgeAnswer>): ToolExecutionResult {\n if (outcome.ok && 'output' in outcome.value) {\n const { output, logs } = outcome.value\n return { status: 'ok', output: output.type === 'text' ? output.value : output.value, logs }\n }\n if (outcome.ok) {\n const failure = outcome.value as { reason: string; error: string; logs?: string[] }\n return { status: 'failed', reason: failure.reason, error: failure.error, logs: failure.logs }\n }\n return { status: 'failed', reason: outcome.reason, error: outcome.error }\n}\n","import type { ToolExecutionBackend } from '@workerdeck/protocol'\nimport type {\n ToolExecutionCall,\n ToolExecutionDispatch,\n ToolExecutionProfile,\n ToolExecutor,\n} from './tool-executor.ts'\n\n/** A dispatched execution, as handed to the backend that will run it. */\nexport type DeferredDispatch = {\n /** Correlation id. The result is delivered under it — `POST\n * {basePath}/executions/:executionId/result` — and applied idempotently. */\n executionId: string\n sessionId: string\n tool: string\n input: unknown\n /** The session's scratch filesystem at dispatch time, by value. */\n vfsSeed?: Record<string, string>\n limits?: { timeoutMs?: number; memoryLimitBytes?: number }\n /** Epoch ms the host's execution watchdog fires at, when a timeout was configured. */\n expiresAt?: number\n}\n\nexport type DeferredExecutorOptions = {\n /**\n * Hand the call to whatever actually runs it — enqueue it, POST it to a worker,\n * page a human. Called synchronously during dispatch; throwing fails the\n * execution (the failure reaches the agent as ordinary tool output).\n */\n onDispatch: (call: DeferredDispatch) => void | Promise<void>\n /** How long the result may take before the host's watchdog fails the execution.\n * Unset = no deadline; the execution then relies on the job's parked cap. */\n timeoutMs?: number\n /** Reported on `execution_dispatched`. Default 'remote'. */\n backend?: ToolExecutionBackend\n}\n\n/**\n * The executor for work that outlives the session's process residency: dispatch\n * hands the call off and returns `pending` **without holding a promise**, because\n * the runner it would resolve into is about to be torn down. The result can only\n * come back through the host — the execution-result route → `settleExecution` on a\n * rehydrated runner — which is exactly what makes a park durable rather than a\n * long in-memory await.\n *\n * Contrast {@link BrowserBridgeExecutor}, which is also `pending` but keeps its\n * answer in memory for the ~60s the tab has to reply.\n */\nexport class DeferredExecutor implements ToolExecutor {\n readonly backend: ToolExecutionBackend\n readonly timeoutMs: number | undefined\n #options: DeferredExecutorOptions\n\n constructor(options: DeferredExecutorOptions) {\n this.#options = options\n this.backend = options.backend ?? 'remote'\n this.timeoutMs = options.timeoutMs\n }\n\n /** Every call this executor takes is deferred — route only the tools that\n * belong on the remote side to it. */\n describe(): ToolExecutionProfile {\n return { backend: this.backend, deferred: true, timeoutMs: this.timeoutMs }\n }\n\n async dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch> {\n await this.#options.onDispatch({\n executionId: call.executionId,\n sessionId: call.sessionId,\n tool: call.tool,\n input: call.input,\n vfsSeed: call.vfs?.snapshot(),\n limits: call.limits,\n expiresAt: this.timeoutMs === undefined ? undefined : Date.now() + this.timeoutMs,\n })\n return { executionId: call.executionId, status: 'pending' }\n }\n}\n","import { tool, type Tool, type ToolSet } from 'ai'\nimport { z } from 'zod'\nimport { createVfs, type SandboxVfs } from '@workerdeck/sandbox'\nimport type { ToolExecutionResult, ToolExecutor } from '../../executors/tool-executor.ts'\nimport type { WebFetchFn } from './web-fetch.ts'\n\n/**\n * How much authority a tool carries, which decides where it may run.\n *\n * - `sandboxed` — no ambient authority; safe to execute anywhere, including an\n * untrusted browser tab. Its results are untrusted input.\n * - `authoritative` — runs server-side with server credentials (MCP, secret-bearing\n * APIs). **Never bridged to a client**: bridging it would hand a browser the\n * ability to forge authoritative results.\n */\nexport type ToolTrust = 'sandboxed' | 'authoritative'\n\nexport type ToolDefinition = {\n name: string\n trust: ToolTrust\n /** The AI SDK tool. Sandboxed tools are declared WITHOUT `execute` so the loop\n * hands them to the ToolExecutor seam rather than running them inline. */\n tool: Tool\n}\n\nexport type ToolContextOptions = {\n /** Executor for sandboxed tools. Selected per call by the host (browser bridge\n * when a client is attached, server QuickJS otherwise). */\n executor: ToolExecutor\n sessionId: string\n /** Scratch filesystem shared by this session's sandboxed tools. */\n vfs?: SandboxVfs\n /** Search backend. Omitted = `web_search` is not granted at all. */\n search?: (query: string, limit: number) => Promise<Array<{ title: string; url: string; snippet?: string }>>\n /** Document fetcher for `download`. Omitted = the tool is not granted. */\n download?: (url: string) => Promise<{ contentType?: string; text: string }>\n /** Page digester for `web_fetch` (see {@link createWebFetch}). Omitted = the\n * tool is not granted. */\n webFetch?: WebFetchFn\n /** Notified when the agent hands over a VFS file via `deliver_file`, so the\n * host can emit the `file_delivered` session event. The tool is only granted\n * when this is set — a delivery nobody hears is not a delivery. */\n onFileDelivered?: (file: { path: string; bytes: number; description?: string }) => void\n /** Per-call sandbox limits. */\n limits?: { timeoutMs?: number; memoryLimitBytes?: number }\n /** Notified when a sandboxed execution is dispatched and when it settles, so\n * the host can emit execution_* events. */\n onDispatch?: (executionId: string, toolName: string) => void\n onSettle?: (executionId: string, result: ToolExecutionResult) => void\n}\n\n/** Everything a session's tools need, plus the tool set to hand the runner. */\nexport type ToolContext = {\n vfs: SandboxVfs\n tools: ToolSet\n definitions: ToolDefinition[]\n /** Names the loop must not execute inline (they go through the executor). */\n sandboxedToolNames: string[]\n}\n\nconst MAX_FILE_BYTES = 1024 * 1024\n\n/**\n * Build the capability-scoped tool set for a session.\n *\n * The agent's authority is exactly what is granted here — there are no built-in\n * filesystem or shell tools, and nothing reaches the host filesystem: `fs_*`\n * operate on an in-memory scratch VFS. Tools whose backend is not supplied are\n * simply absent rather than present-and-failing, so a model cannot be tempted\n * by a capability the operator did not grant.\n */\nexport function createToolContext(options: ToolContextOptions): ToolContext {\n const vfs = options.vfs ?? createVfs()\n const definitions: ToolDefinition[] = []\n\n // --- Scratch filesystem (server-side, in-memory; never the host disk) -----\n definitions.push({\n name: 'fs_list',\n trust: 'authoritative',\n tool: tool({\n description: 'List files in the scratch filesystem.',\n inputSchema: z.object({ dir: z.string().default('/').describe('Directory to list') }),\n execute: async ({ dir }) => ({ files: vfs.list(dir) }),\n }),\n })\n definitions.push({\n name: 'fs_read',\n trust: 'authoritative',\n tool: tool({\n description: 'Read a file from the scratch filesystem.',\n inputSchema: z.object({ path: z.string() }),\n execute: async ({ path }) => {\n const content = vfs.read(path)\n if (content === undefined) return { error: `no such file: ${path}` }\n return { content: truncate(content) }\n },\n }),\n })\n definitions.push({\n name: 'fs_write',\n trust: 'authoritative',\n tool: tool({\n description: 'Write a file to the scratch filesystem.',\n inputSchema: z.object({ path: z.string(), content: z.string() }),\n execute: async ({ path, content }) => {\n vfs.write(path, content)\n return { path, bytes: content.length }\n },\n }),\n })\n\n // --- File hand-over: only when the host listens for deliveries ------------\n if (options.onFileDelivered) {\n const onFileDelivered = options.onFileDelivered\n definitions.push({\n name: 'deliver_file',\n trust: 'authoritative',\n tool: tool({\n description:\n 'Hand a file from the scratch filesystem over to the user as a deliverable. ' +\n 'Write it with fs_write first, then deliver it.',\n inputSchema: z.object({\n path: z.string().describe('Path of an existing file in the scratch filesystem'),\n description: z.string().optional().describe('What this file is, for the recipient'),\n }),\n execute: async ({ path, description }) => {\n const content = vfs.read(path)\n if (content === undefined) return { error: `no such file: ${path}` }\n const file = { path, bytes: content.length, description }\n onFileDelivered(file)\n return { delivered: true, ...file }\n },\n }),\n })\n }\n\n // --- Network capabilities: only when the host supplied a backend ----------\n if (options.search) {\n const search = options.search\n definitions.push({\n name: 'web_search',\n trust: 'authoritative',\n tool: tool({\n description: 'Search the web for pages relevant to a query.',\n inputSchema: z.object({\n query: z.string(),\n limit: z.number().int().min(1).max(25).default(5),\n }),\n execute: async ({ query, limit }) => ({ results: await search(query, limit) }),\n }),\n })\n }\n if (options.download) {\n const download = options.download\n definitions.push({\n name: 'download',\n trust: 'authoritative',\n tool: tool({\n description:\n 'Fetch a URL and store its text in the scratch filesystem for later evaluation.',\n inputSchema: z.object({\n url: z.string().describe('Absolute http(s) URL'),\n path: z.string().describe('Where to store it in the scratch filesystem'),\n }),\n execute: async ({ url, path }) => {\n try {\n const { text, contentType } = await download(url)\n const stored = truncate(text)\n vfs.write(path, stored)\n return { path, bytes: stored.length, contentType }\n } catch (error) {\n // A failed fetch is data the agent can react to, not a turn-ending throw.\n return { error: error instanceof Error ? error.message : String(error) }\n }\n },\n }),\n })\n }\n\n if (options.webFetch) {\n const webFetch = options.webFetch\n definitions.push({\n name: 'web_fetch',\n trust: 'authoritative',\n tool: tool({\n description:\n 'Fetch a web page and process its content against a prompt. Returns the answer ' +\n '(or the page as markdown). Distinct from download: use web_fetch to answer a ' +\n 'question about a page, download to store raw text for eval_script.',\n inputSchema: z.object({\n url: z.string().describe('Absolute http(s) URL'),\n prompt: z.string().describe('What to extract or answer from the page'),\n }),\n execute: async ({ url, prompt }) => {\n try {\n return await webFetch(url, prompt)\n } catch (error) {\n // A failed fetch is data the agent can react to, not a turn-ending throw.\n return { error: error instanceof Error ? error.message : String(error) }\n }\n },\n }),\n })\n }\n\n // --- Untrusted evaluation: no `execute`, so it rides the executor seam ----\n definitions.push({\n name: 'eval_script',\n trust: 'sandboxed',\n tool: tool({\n description:\n 'Evaluate a JavaScript snippet in a sandbox to parse, score, or extract from files. ' +\n 'Globals: vfs.read(path), vfs.write(path, text), vfs.list(dir), console.log. ' +\n 'The value of the last expression is returned. No network or host access.',\n inputSchema: z.object({ script: z.string() }),\n }),\n })\n\n const tools: ToolSet = {}\n for (const definition of definitions) tools[definition.name] = definition.tool\n\n return {\n vfs,\n tools,\n definitions,\n sandboxedToolNames: definitions.filter((d) => d.trust === 'sandboxed').map((d) => d.name),\n }\n}\n\n/** Add host-side MCP tools to a context. They are ALWAYS authoritative: they run\n * server-side with server credentials, and must never be handed to a browser. */\nexport function withMcpTools(context: ToolContext, mcpTools: ToolSet): ToolContext {\n return withHostTools(\n context,\n Object.fromEntries(\n Object.entries(mcpTools).map(([name, mcpTool]) => [\n name,\n { tool: mcpTool, trust: 'authoritative' as const },\n ]),\n ),\n 'MCP tool',\n )\n}\n\n/** A tool the host supplies, with the trust level it is to run at. */\nexport type HostToolDefinition = {\n tool: Tool\n /**\n * Where this tool may run. `authoritative` tools execute inline in the\n * gateway and MUST declare `execute`; `sandboxed` ones must NOT, because the\n * loop hands them to the {@link ToolExecutor} seam instead — which is what\n * makes them bridgeable to an untrusted tab.\n */\n trust: ToolTrust\n}\n\n/**\n * Add host-supplied tools to a context at an explicit trust level.\n *\n * The trust level is the whole point of the seam: {@link withMcpTools} can only\n * produce authoritative tools, so a host tool that *should* be sandboxed — and\n * therefore executable in the browser tab that asked for it — had no way to be\n * expressed at all. Here the host says which it is, and the contradictions are\n * refused rather than silently resolved:\n *\n * - a `sandboxed` tool carrying `execute` would run inline in this process with\n * the gateway's ambient authority, which is exactly what sandboxing it was\n * meant to prevent;\n * - an `authoritative` tool *without* `execute` would park the turn on a call no\n * executor claims, and the session would simply stop.\n */\nexport function withHostTools(\n context: ToolContext,\n hostTools: Record<string, HostToolDefinition>,\n /** What to call these in error messages ('MCP tool', 'host tool'). */\n kind = 'host tool',\n): ToolContext {\n const entries = Object.entries(hostTools)\n if (entries.length === 0) return context\n const definitions = [...context.definitions]\n const tools: ToolSet = { ...context.tools }\n const sandboxedToolNames = [...context.sandboxedToolNames]\n for (const [name, { tool: hostTool, trust }] of entries) {\n if (name in tools) {\n // Silently overwriting would let a host tool shadow `eval_script` — or an\n // MCP name promote untrusted execution to authoritative. Refuse instead.\n throw new Error(`${kind} '${name}' collides with an existing tool of the same name`)\n }\n const executes = typeof (hostTool as { execute?: unknown }).execute === 'function'\n if (trust === 'sandboxed' && executes) {\n throw new Error(\n `${kind} '${name}' is declared sandboxed but has an \\`execute\\` — it would run in ` +\n 'this process with full authority. Drop `execute` so it rides the ToolExecutor seam.',\n )\n }\n if (trust === 'authoritative' && !executes) {\n throw new Error(\n `${kind} '${name}' is declared authoritative but has no \\`execute\\` — nothing would ` +\n 'ever answer its calls and the turn would stall.',\n )\n }\n definitions.push({ name, trust, tool: hostTool })\n tools[name] = hostTool\n if (trust === 'sandboxed') sandboxedToolNames.push(name)\n }\n return { ...context, tools, definitions, sandboxedToolNames }\n}\n\nfunction truncate(text: string): string {\n return text.length > MAX_FILE_BYTES ? text.slice(0, MAX_FILE_BYTES) : text\n}\n","import { lookup } from 'node:dns/promises'\n\n/**\n * `web_fetch` backend, close to Claude Code's original WebFetch: fetch a URL,\n * convert HTML to markdown, and (optionally) digest it with a model against the\n * caller's prompt. Server-side only — this runs with server egress, which is\n * exactly why it is an authoritative capability the operator grants explicitly.\n */\n\nexport type WebFetchResult = {\n /** The URL that was fetched (after same-host redirects). */\n url: string\n /** Model digest of the page against the prompt (when a digest fn is wired). */\n digest?: string\n /** Page content as markdown (when no digest fn is wired, or digesting failed). */\n markdown?: string\n /** True when the markdown was cut at the size cap. */\n truncated?: boolean\n /** Redirect-to-a-different-host notice: the redirect is surfaced, not followed\n * (the agent can decide to fetch `redirectUrl` itself). */\n notice?: string\n redirectUrl?: string\n error?: string\n}\n\nexport type WebFetchFn = (url: string, prompt: string) => Promise<WebFetchResult>\n\n/** Runs the digest pass over the fetched markdown. Wire the session's own model\n * here (see createEngineSession) so its tokens land in the turn's usage. */\nexport type WebFetchDigest = (markdown: string, prompt: string) => Promise<string>\n\nexport type WebFetchOptions = {\n fetchImpl?: typeof fetch\n /** Raw-body cap, enforced while streaming (before any conversion). Default 1 MiB. */\n maxContentBytes?: number\n /** Markdown cap handed to the model. Default 50 KB. */\n maxMarkdownBytes?: number\n /** Fetched-page cache TTL (keyed by URL; the digest is per-prompt and never\n * cached). Default 15 minutes. */\n cacheTtlMs?: number\n /** Optional hostname allowlist on top of the SSRF guard (exact or `*.example.com`).\n * Unset = any public host. */\n allowedHosts?: string[]\n /** Per-request timeout. Default 30000. */\n timeoutMs?: number\n digest?: WebFetchDigest\n}\n\nconst MAX_CACHE_ENTRIES = 64\nconst MAX_REDIRECTS = 5\n\ntype CacheEntry = { expiresAt: number; page: WebFetchResult }\n\nexport function createWebFetch(options: WebFetchOptions = {}): WebFetchFn {\n const fetchImpl = options.fetchImpl ?? fetch\n const maxContentBytes = options.maxContentBytes ?? 1024 * 1024\n const maxMarkdownBytes = options.maxMarkdownBytes ?? 50 * 1024\n const cacheTtlMs = options.cacheTtlMs ?? 15 * 60 * 1000\n const cache = new Map<string, CacheEntry>()\n\n const fetchPage = async (rawUrl: string): Promise<WebFetchResult> => {\n const cached = cache.get(rawUrl)\n if (cached && cached.expiresAt > Date.now()) return cached.page\n\n let url = parseUrl(rawUrl)\n if (!url) return { url: rawUrl, error: 'only absolute http(s) URLs are supported' }\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 30_000)\n try {\n let response: Response\n for (let hop = 0; ; hop++) {\n const denied = await denyReason(url, options.allowedHosts)\n if (denied) return { url: url.href, error: denied }\n response = await fetchImpl(url.href, {\n redirect: 'manual',\n signal: controller.signal,\n })\n if (response.status < 300 || response.status >= 400) break\n const location = response.headers.get('location')\n if (!location) return { url: url.href, error: `redirect (${response.status}) without a location` }\n const target = parseUrl(new URL(location, url).href)\n if (!target) return { url: url.href, error: `redirect to unsupported URL: ${location}` }\n if (target.host !== url.host) {\n // Like the original: surface a cross-host redirect instead of silently\n // following it — the agent may fetch the new URL explicitly.\n return {\n url: url.href,\n redirectUrl: target.href,\n notice: `redirected to a different host (${target.host}); not followed automatically`,\n }\n }\n if (hop >= MAX_REDIRECTS) return { url: url.href, error: 'too many redirects' }\n url = target\n }\n if (!response.ok) {\n return { url: url.href, error: `request failed: ${response.status}` }\n }\n const declared = Number(response.headers.get('content-length') ?? '')\n if (declared > maxContentBytes) {\n return { url: url.href, error: `response too large (${declared} bytes)` }\n }\n const body = await readCapped(response, maxContentBytes)\n if (body === undefined) {\n return { url: url.href, error: `response too large (> ${maxContentBytes} bytes)` }\n }\n const contentType = response.headers.get('content-type') ?? ''\n const text =\n contentType.includes('html') || looksLikeHtml(body) ? htmlToMarkdown(body) : body\n const truncated = text.length > maxMarkdownBytes\n const page: WebFetchResult = {\n url: url.href,\n markdown: truncated ? text.slice(0, maxMarkdownBytes) : text,\n truncated: truncated || undefined,\n }\n if (cache.size >= MAX_CACHE_ENTRIES) {\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n cache.set(rawUrl, { expiresAt: Date.now() + cacheTtlMs, page })\n return page\n } catch (error) {\n const message = controller.signal.aborted\n ? 'request timed out'\n : error instanceof Error\n ? error.message\n : String(error)\n return { url: url.href, error: message }\n } finally {\n clearTimeout(timer)\n }\n }\n\n return async (rawUrl, prompt) => {\n const page = await fetchPage(rawUrl)\n if (page.error || page.notice || !options.digest || page.markdown === undefined) return page\n try {\n const digest = await options.digest(page.markdown, prompt)\n return { url: page.url, digest, truncated: page.truncated }\n } catch {\n // Digest is best-effort sugar over the fetch: fall back to the markdown.\n return page\n }\n }\n}\n\nfunction parseUrl(raw: string): URL | undefined {\n try {\n const url = new URL(raw)\n return url.protocol === 'https:' || url.protocol === 'http:' ? url : undefined\n } catch {\n return undefined\n }\n}\n\n/** SSRF guard: resolve the hostname and refuse private, loopback, and link-local\n * destinations. Checked per redirect hop. Resolution happens once here and again\n * inside fetch (a DNS-rebinding TOCTOU); this tier accepts that — operators who\n * need pinning can supply `fetchImpl` with a pinned agent. */\nasync function denyReason(url: URL, allowedHosts: string[] | undefined): Promise<string | null> {\n const host = url.hostname.toLowerCase()\n if (allowedHosts && allowedHosts.length > 0 && !hostMatches(host, allowedHosts)) {\n return `host not allowed: ${host}`\n }\n if (host === 'localhost' || host.endsWith('.localhost')) return `host not allowed: ${host}`\n const literal = host.replace(/^\\[|\\]$/g, '')\n if (isPrivateAddress(literal)) return `address not allowed: ${literal}`\n if (/^[\\d.]+$/.test(literal) || literal.includes(':')) return null // public literal IP\n let addresses: Array<{ address: string }>\n try {\n addresses = await lookup(literal, { all: true })\n } catch {\n return `cannot resolve host: ${host}`\n }\n for (const { address } of addresses) {\n if (isPrivateAddress(address)) return `host resolves to a private address: ${host}`\n }\n return null\n}\n\nfunction hostMatches(host: string, allowedHosts: string[]): boolean {\n return allowedHosts.some((entry) => {\n const pattern = entry.trim().toLowerCase()\n if (!pattern) return false\n if (pattern.startsWith('*.')) return host.endsWith(pattern.slice(1))\n return host === pattern\n })\n}\n\n/** Private / loopback / link-local / unspecified, IPv4 and IPv6 (incl. v4-mapped). */\nexport function isPrivateAddress(address: string): boolean {\n const ip = address.toLowerCase()\n if (ip.includes(':')) {\n if (ip === '::' || ip === '::1') return true\n const mapped = /^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/.exec(ip)\n if (mapped) return isPrivateAddress(mapped[1]!)\n return ip.startsWith('fc') || ip.startsWith('fd') || /^fe[89ab]/.test(ip)\n }\n const parts = ip.split('.').map(Number)\n if (parts.length !== 4 || parts.some((n) => Number.isNaN(n) || n < 0 || n > 255)) return false\n const [a, b] = parts as [number, number, number, number]\n if (a === 0 || a === 10 || a === 127) return true\n if (a === 100 && b! >= 64 && b! <= 127) return true // CGNAT\n if (a === 169 && b === 254) return true\n if (a === 172 && b! >= 16 && b! <= 31) return true\n if (a === 192 && b === 168) return true\n return a >= 224 // multicast + reserved\n}\n\nasync function readCapped(response: Response, maxBytes: number): Promise<string | undefined> {\n if (!response.body) {\n const text = await response.text()\n return text.length > maxBytes ? undefined : text\n }\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n let out = ''\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n out += decoder.decode(value, { stream: true })\n if (out.length > maxBytes) {\n await reader.cancel().catch(() => {})\n return undefined\n }\n }\n return out + decoder.decode()\n}\n\nfunction looksLikeHtml(body: string): boolean {\n return /<(!doctype|html|head|body)[\\s>]/i.test(body.slice(0, 1024))\n}\n\n/**\n * Dependency-free HTML → markdown, tuned for \"give the model readable text\":\n * drops non-content subtrees, keeps headings/lists/links/emphasis/code, strips\n * everything else. Not a spec-grade converter on purpose — a small predictable\n * transform beats dragging a DOM into core.\n */\nexport function htmlToMarkdown(html: string): string {\n let text = html\n .replace(/<!--[\\s\\S]*?-->/g, '')\n .replace(/<(script|style|noscript|svg|template|iframe)\\b[\\s\\S]*?<\\/\\1>/gi, '')\n .replace(/<(head)\\b[\\s\\S]*?<\\/\\1>/gi, '')\n text = text\n .replace(/<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi, (_, level: string, body: string) => {\n return `\\n\\n${'#'.repeat(Number(level))} ${stripTags(body).trim()}\\n\\n`\n })\n .replace(/<pre[^>]*>([\\s\\S]*?)<\\/pre>/gi, (_, body: string) => {\n return `\\n\\n\\`\\`\\`\\n${decodeEntities(body.replace(/<[^>]+>/g, ''))}\\n\\`\\`\\`\\n\\n`\n })\n .replace(/<a\\s[^>]*href=[\"']([^\"']+)[\"'][^>]*>([\\s\\S]*?)<\\/a>/gi, (_, href: string, body: string) => {\n const label = stripTags(body).trim()\n // Skip anchors/scripts and empty labels; keep the label when it IS the URL.\n if (!label || href.startsWith('#') || href.startsWith('javascript:')) return label\n return label === href ? label : `[${label}](${href})`\n })\n .replace(/<li[^>]*>/gi, '\\n- ')\n .replace(/<\\/(p|div|section|article|tr|table|ul|ol|blockquote|figure)>/gi, '\\n\\n')\n .replace(/<(br|hr)\\s*\\/?>/gi, '\\n')\n .replace(/<(strong|b)>([\\s\\S]*?)<\\/\\1>/gi, '**$2**')\n .replace(/<(em|i)>([\\s\\S]*?)<\\/\\1>/gi, '*$2*')\n .replace(/<code[^>]*>([\\s\\S]*?)<\\/code>/gi, '`$1`')\n text = decodeEntities(text.replace(/<[^>]+>/g, ''))\n return text\n .replace(/[ \\t]+\\n/g, '\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .replace(/[ \\t]{2,}/g, ' ')\n .trim()\n}\n\nfunction stripTags(html: string): string {\n return decodeEntities(html.replace(/<[^>]+>/g, ''))\n}\n\nfunction decodeEntities(text: string): string {\n return text\n .replace(/&#(\\d+);/g, (_, code: string) => String.fromCodePoint(Number(code)))\n .replace(/&#x([\\da-f]+);/gi, (_, code: string) => String.fromCodePoint(parseInt(code, 16)))\n .replace(/&nbsp;/g, ' ')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;|&apos;/g, \"'\")\n .replace(/&amp;/g, '&')\n}\n","import type { LanguageModel, ToolSet } from 'ai'\nimport type {\n McpServerConfigWire,\n McpServerStatusInfo,\n McpServerToolInfo,\n ProfileInfo,\n SessionCapability,\n} from '@workerdeck/protocol'\nimport { createVfs } from '@workerdeck/sandbox'\nimport { AiSdkRunner, type AiSdkRunnerConfig } from './runner.ts'\nimport {\n createToolContext,\n withHostTools,\n withMcpTools,\n type HostToolDefinition,\n type ToolContextOptions,\n} from './tools.ts'\nimport type { ToolExecutor } from '../../executors/tool-executor.ts'\nimport { createWebFetch, type WebFetchFn, type WebFetchOptions } from './web-fetch.ts'\n\nexport type EngineSessionOptions = {\n /** Resolved session config (profile defaults already applied). */\n config: AiSdkRunnerConfig\n /** The profile that selected this engine, when there was one. */\n profile?: ProfileInfo\n /**\n * Resolve the profile's provider config into a model instance. The host owns\n * this so core never imports a provider SDK and never reads credentials —\n * they come from the operator's environment, exactly like the Claude chain.\n */\n resolveModel: (profile: ProfileInfo | undefined, config: AiSdkRunnerConfig) => LanguageModel\n /**\n * Executor for sandboxed tools. Return the browser bridge when a client is\n * attached and the server sandbox otherwise; the seam makes them\n * interchangeable, so this is the only place the choice is made.\n */\n selectExecutor: () => ToolExecutor\n /** Which backend `selectExecutor` returned, for the execution_* events. */\n backend?: 'server' | 'browser' | 'managed' | 'remote'\n /** Backends for the granted capabilities. Omitted ones are simply not granted. */\n capabilities?: {\n search?: ToolContextOptions['search']\n download?: ToolContextOptions['download']\n /**\n * Grants `web_fetch`. Pass options (or `{}`) to use the built-in\n * {@link createWebFetch} backend — its digest pass then runs on the\n * session's own model, billed into the turn's usage. Pass `digest: false`\n * to skip the digest (the tool returns page markdown), a custom digest fn\n * to bring your own model, or a complete {@link WebFetchFn} to replace the\n * backend outright.\n */\n webFetch?: WebFetchFn | (Omit<WebFetchOptions, 'digest'> & { digest?: WebFetchOptions['digest'] | false })\n /** Grants `deliver_file`: the agent can hand VFS files over to the user\n * (emitting `file_delivered`, downloadable via the server's file routes).\n * Default true — set false to withhold it. */\n deliverFiles?: boolean\n }\n /**\n * A live MCP connection from {@link connectMcpTools} — the preferred way to\n * hand MCP to a session, and the only one that can fail loudly.\n *\n * With this set, the session knows *which servers connected*, so two things\n * that were previously silent become impossible: a profile naming a server\n * that never connected refuses to build (see {@link mcpTools} for what that\n * used to look like), and `runner.mcpServers()` answers `GET\n * /sessions/:id/mcp` with the real per-server status instead of 501.\n */\n mcp?: McpConnection\n /** Authoritative tools that run server-side with server credentials (MCP).\n * Never bridged to a client. Namespaced `<server>__<tool>` by\n * {@link connectMcpTools}, which is how a profile grants servers by name.\n *\n * The bare tool set, for a host assembling one itself. Prefer {@link mcp}:\n * a tool set alone cannot distinguish \"this server connected and exposes no\n * tools\" from \"this server never connected\", so the check here has to be the\n * cruder one — a declared server contributing no tools is refused. */\n mcpTools?: ToolSet\n /**\n * Extra host tools, each at an explicit trust level (see\n * {@link withHostTools}). This is the seam for a tool that is neither one of\n * the built-in capabilities nor MCP — including a **sandboxed** one, which\n * `mcpTools` cannot express because everything in it is authoritative by\n * construction.\n *\n * A sandboxed tool here rides the same {@link ToolExecutor} seam\n * `eval_script` does, so it executes wherever `selectExecutor` points — an\n * in-process QuickJS guest, or the browser tab that asked the question.\n */\n tools?: Record<string, HostToolDefinition>\n /** Extra instructions prepended to the session's system prompt. Overridden by\n * the profile's `session.instructions` when it declares one. */\n instructions?: string\n executionLimits?: { timeoutMs?: number; memoryLimitBytes?: number }\n /**\n * Initial scratch-filesystem contents for a **new** session, and the safe way\n * to seed one: it is ignored outright when `config.restore` is set, because a\n * rehydrated session brings back the files its parked turn already wrote and\n * seeding over them destroys exactly the work that was preserved.\n *\n * (Hand-building `config.vfs` still works and still wins — but then the\n * `restore ? undefined : createVfs(...)` dance is yours to get right.)\n */\n seedVfs?: Record<string, string>\n /**\n * Build the session under this id rather than minting one.\n *\n * Forward the server's `EngineRunnerContext.id` here, always: it is set when\n * the gateway is rehydrating a session across a restart, and a runner that\n * ignores it comes back as a *different* session — the rebuild is refused,\n * and every client's route and unread watermark is stranded. Ignored when\n * `config.restore` is present, which carries its own id.\n */\n id?: string\n}\n\n/** Which capability a wired backend yields, for grant filtering. */\nconst CAPABILITY_TOOLS = {\n search: 'web_search',\n download: 'download',\n webFetch: 'web_fetch',\n deliverFiles: 'deliver_file',\n} as const satisfies Record<string, SessionCapability>\n\n/**\n * Assemble a model-agnostic session: provider model, capability-scoped tools,\n * a scratch VFS, and the executor that runs the sandboxed ones.\n *\n * This is the piece an operator wires into the server's `createEngineRunner`.\n *\n * The host wires the *backends*; the profile and the session request decide which\n * of them are actually granted (`profile.session`, `config.capabilities`). A\n * backend that isn't granted is simply not built into the tool set, so withholding\n * a capability costs the host no branching. No declaration anywhere = everything\n * the host wired, which is what a host that ignores profiles gets.\n */\nexport function createEngineSession(options: EngineSessionOptions): AiSdkRunner {\n // A rehydrated session brings its scratch filesystem back with it — the\n // deliverables and working files the parked turn already produced. `seedVfs`\n // is for a *new* session only, which is the whole reason it exists here\n // rather than at each call site.\n const vfs =\n options.config.vfs ?? createVfs(options.config.restore ? options.config.restore.vfs : options.seedVfs)\n const executor = options.selectExecutor()\n // Narrowing only: the gateway has already refused a request naming a capability\n // its profile doesn't grant, so the request value wins when present.\n const granted = options.config.capabilities ?? options.profile?.session?.capabilities\n const isGranted = (key: keyof typeof CAPABILITY_TOOLS): boolean =>\n granted === undefined || granted.includes(CAPABILITY_TOOLS[key])\n // The runner doesn't exist yet while the tools are being built; these\n // capabilities reach back into it lazily (they only ever run mid-turn).\n let runner: AiSdkRunner | undefined\n const webFetchCap = isGranted('webFetch') ? options.capabilities?.webFetch : undefined\n const webFetch =\n typeof webFetchCap === 'function'\n ? webFetchCap\n : webFetchCap\n ? createWebFetch({\n ...webFetchCap,\n digest:\n webFetchCap.digest === false\n ? undefined\n : (webFetchCap.digest ??\n ((markdown, prompt) =>\n runner!.generateDigest(\n 'Answer the request below using ONLY this web page content.\\n\\n' +\n `<page>\\n${markdown}\\n</page>\\n\\nRequest: ${prompt}`,\n ))),\n })\n : undefined\n const base = createToolContext({\n executor,\n sessionId: 'pending',\n vfs,\n search: isGranted('search') ? options.capabilities?.search : undefined,\n download: isGranted('download') ? options.capabilities?.download : undefined,\n webFetch,\n onFileDelivered:\n options.capabilities?.deliverFiles === false || !isGranted('deliverFiles')\n ? undefined\n : (file) => runner?.emitFileDelivered(file),\n })\n const declaredServers = options.profile?.session?.mcpServers\n const connected = options.mcp?.tools ?? options.mcpTools\n requireDeclaredServers(options.profile?.name ?? '(unnamed)', declaredServers, options.mcp, connected)\n const mcpTools = selectMcpTools(connected, declaredServers)\n const withMcp = mcpTools ? withMcpTools(base, mcpTools) : base\n const context = options.tools ? withHostTools(withMcp, options.tools) : withMcp\n\n runner = new AiSdkRunner({\n ...options.config,\n languageModel: options.resolveModel(options.profile, options.config),\n instructions:\n options.profile?.session?.instructions ?? options.instructions ?? options.config.instructions,\n tools: context.tools,\n vfs,\n executor,\n executableTools: context.sandboxedToolNames,\n executionBackend: options.backend ?? 'server',\n executionLimits: options.executionLimits,\n // Only the servers this profile was granted: the /mcp screen must not\n // report a connection the session cannot actually reach.\n reportMcpServers: options.mcp\n ? () =>\n Promise.resolve(\n declaredServers === undefined\n ? options.mcp!.servers\n : options.mcp!.servers.filter((s) => declaredServers.includes(s.name)),\n )\n : undefined,\n }, options.id)\n return runner\n}\n\n/**\n * Refuse to build a session whose profile names an MCP server that isn't there.\n *\n * A profile's `mcpServers` list is a **declaration**, not a filter: an embedder\n * who wrote it meant the agent to have those tools. Honouring it partially is\n * the worst failure mode this engine has — the session starts, reports healthy,\n * and the agent apologises its way through every request that needed the server,\n * with one warning line in a log nobody is reading.\n *\n * With a {@link McpConnection} the check is exact (did this server connect?).\n * With a bare tool set all we can see is whether any tool carries the server's\n * namespace, so a genuinely tool-less server would trip it — the fix there is to\n * pass `mcp` rather than to weaken this.\n */\nfunction requireDeclaredServers(\n profileName: string,\n declared: string[] | undefined,\n mcp: McpConnection | undefined,\n tools: ToolSet | undefined,\n): void {\n if (!declared || declared.length === 0) return\n const missing = declared.filter((name) => {\n if (mcp) {\n const server = mcp.servers.find((s) => s.name === name)\n return !server || server.status !== 'connected'\n }\n return !Object.keys(tools ?? {}).some((tool) => tool.split('__')[0] === name)\n })\n if (missing.length === 0) return\n const reasons = missing\n .map((name) => {\n const error = mcp?.servers.find((s) => s.name === name)?.error\n return error ? `${name} (${error})` : name\n })\n .join(', ')\n throw new Error(\n `profile '${profileName}' declares MCP server(s) that are not connected: ${reasons}. ` +\n 'A session missing a declared server is a session whose agent silently cannot do its job.',\n )\n}\n\n/**\n * Restrict a connected tool set to the MCP servers a profile grants, by the\n * `<server>__<tool>` namespace {@link connectMcpTools} assigns. Undefined `servers`\n * = no declaration, so every connected server passes through.\n *\n * This is how one process-wide MCP connection serves a mixed fleet: the host\n * connects everything once, each profile grants a subset. The transport configs —\n * and any credentials in their headers — never leave the host for a profile.\n */\nfunction selectMcpTools(tools: ToolSet | undefined, servers: string[] | undefined): ToolSet | undefined {\n if (!tools || servers === undefined) return tools\n const allowed = new Set(servers)\n return Object.fromEntries(\n Object.entries(tools).filter(([name]) => allowed.has(name.split('__')[0]!)),\n )\n}\n\nexport type McpConnection = {\n tools: ToolSet\n /**\n * One entry per configured server, connected or not — the truth a session was\n * assembled against. Handed to {@link createEngineSession} as `mcp`, it is\n * what `GET /sessions/:id/mcp` answers with and what makes a half-connected\n * session refuse to build rather than run degraded.\n */\n servers: McpServerStatusInfo[]\n close: () => Promise<void>\n}\n\n/**\n * Connect to MCP servers and return their tools, ready for {@link withMcpTools}.\n *\n * Server-side only, with server credentials: these tools are authoritative and\n * must never be bridged to a browser. `@ai-sdk/mcp` is imported lazily and is an\n * optional dependency — an operator who wires no MCP servers never needs it.\n *\n * **A stateless MCP server must answer `GET` with 405.** The client opens the\n * SSE stream with a `GET` before it sends anything, and a POST-only server\n * mounted under a framework's default 404 makes the whole connect fail with an\n * error that names neither the method nor the route. This is the single most\n * common way an otherwise-correct MCP mount fails.\n */\nexport async function connectMcpTools(\n servers: Record<string, McpServerConfigWire>,\n options: {\n /** `onError` may fire more than once for a single server: transport-level\n * failures surface through the client's own uncaught-error channel as well as\n * the connect failure. Treat it as a report, not a count. */\n onError?: (name: string, error: unknown) => void\n /**\n * Reject if any server fails to connect, after closing the ones that did.\n *\n * Off by default, which is right for an operator's fleet — one unreachable\n * server should not take a whole gateway's sessions down. Turn it **on**\n * when the servers are the app's own: an embedder who mounts one wiki server\n * and gets a session without it has a session that cannot do its job, and\n * finding that out at connect time beats finding it out from a transcript\n * where the agent apologises.\n */\n required?: boolean\n } = {},\n): Promise<McpConnection> {\n const entries = Object.entries(servers)\n if (entries.length === 0) return { tools: {}, servers: [], close: async () => {} }\n\n const { createMCPClient } = await import('@ai-sdk/mcp')\n const clients: Array<{ close: () => Promise<void> }> = []\n const tools: ToolSet = {}\n const statuses: McpServerStatusInfo[] = []\n const closeAll = async (): Promise<void> => {\n await Promise.allSettled(clients.map((c) => c.close()))\n }\n\n for (const [name, server] of entries) {\n const identity = describeServer(server)\n try {\n const client = await createMCPClient({\n transport: toTransport(server),\n onUncaughtError: (error) => options.onError?.(name, error),\n })\n clients.push(client as unknown as { close: () => Promise<void> })\n const connected = await client.tools()\n // Namespaced so two servers exposing the same tool name cannot collide\n // (and so a tool's origin stays legible in the transcript).\n for (const [toolName, mcpTool] of Object.entries(connected)) {\n tools[`${name}__${toolName}`] = mcpTool as ToolSet[string]\n }\n statuses.push({\n name,\n status: 'connected',\n ...identity,\n // Unnamespaced here: this is the server's own view of itself, and the\n // `<server>__` prefix is this engine's routing detail.\n tools: Object.entries(connected).map(([toolName, mcpTool]) => toToolInfo(toolName, mcpTool)),\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n statuses.push({ name, status: 'failed', error: message, ...identity })\n options.onError?.(name, error)\n if (options.required) {\n // Nothing is half-open: the clients already connected are closed before\n // this leaves, or an embedder's failed create leaks a socket per attempt.\n await closeAll()\n throw new Error(`MCP server '${name}' failed to connect: ${message}`)\n }\n // Otherwise one unreachable server must not take down the session; the\n // agent simply does not get those tools.\n }\n }\n\n return { tools, servers: statuses, close: closeAll }\n}\n\n/** The connection's identity, minus its secrets — `headers` never travel. */\nfunction describeServer(\n server: McpServerConfigWire,\n): Pick<McpServerStatusInfo, 'transport' | 'url' | 'command' | 'args'> {\n if ('url' in server) return { transport: server.type === 'sse' ? 'sse' : 'http', url: server.url }\n return { transport: 'stdio', command: server.command, args: server.args }\n}\n\n/**\n * The AI SDK hands back its own `Tool`, whose `inputSchema` may be a zod schema\n * or a `jsonSchema()` wrapper. Only the latter carries a JSON Schema document,\n * so that is the only case where parameters are reported — `McpServerToolInfo`\n * models the absence deliberately, and inventing one here would be worse.\n */\nfunction toToolInfo(name: string, mcpTool: unknown): McpServerToolInfo {\n const { description, inputSchema } = (mcpTool ?? {}) as {\n description?: unknown\n inputSchema?: { jsonSchema?: unknown }\n }\n return {\n name,\n description: typeof description === 'string' ? description : undefined,\n inputSchema: inputSchema?.jsonSchema,\n }\n}\n\n/**\n * Only http/sse: the AI SDK's built-in transports are the remote ones, and its\n * own docs mark stdio local-only and not deployable. A stdio server here is a\n * misconfiguration worth surfacing rather than silently dropping — the Claude\n * engine still supports stdio, since the CLI spawns those itself.\n */\nfunction toTransport(server: McpServerConfigWire) {\n if (!('url' in server)) {\n throw new Error(\n 'stdio MCP servers are not supported by the model-agnostic engine (use an http or sse ' +\n 'server, or run this session under a Claude profile)',\n )\n }\n return server.type === 'sse'\n ? { type: 'sse' as const, url: server.url, headers: server.headers }\n : { type: 'http' as const, url: server.url, headers: server.headers }\n}\n","import type { ModelCatalog } from '../adapter.ts'\n\n/**\n * The Claude engine's model catalog — what a create form offers before any\n * session has run.\n *\n * **Refresh procedure** (release checklist): run `supportedModels()` on a\n * throwaway SDK query (no tokens spent) and re-apply the shaping rules of\n * `modelOptionsFromSdk` (`src/normalize.ts`) at authoring time: drop the\n * `default` sentinel row, derive display names from resolved ids where\n * unambiguous, mark the newest of each family `primary`, sort by family rank.\n * A unit test replays the raw extraction through `modelOptionsFromSdk` and\n * asserts these rows match, so the rules cannot drift.\n *\n * Two things the live `capabilities` event can never offer:\n * - rows for **older models** the CLI no longer reports (hand-maintained, the\n * accepted cost of a static catalog; the CLI silently downgrades an effort a\n * model doesn't support, so `reasoningEfforts` is omitted on them and the\n * engine default set applies);\n * - an answer on a **cold server**. The live event still exists and remains\n * the in-session truth for the model switcher; this catalog is the\n * create-form truth.\n *\n * `defaultModel` is deliberately NOT here: a claude profile's default is the\n * operator's CLI config, unknowable statically.\n */\nexport const CLAUDE_CATALOG: ModelCatalog = {\n provenance:\n 'supportedModels() of @anthropic-ai/claude-agent-sdk 0.3.221 (Claude Code CLI), ' +\n 'extracted 2026-08-05; older-model rows hand-maintained',\n models: [\n {\n value: 'claude-fable-5[1m]',\n resolvedModel: 'claude-fable-5',\n displayName: 'Fable 5',\n description: 'Fable 5 · Most capable for your hardest and longest-running tasks',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],\n },\n {\n value: 'opus[1m]',\n resolvedModel: 'claude-opus-5[1m]',\n displayName: 'Opus 5',\n description: 'Opus 5 with 1M context · Best for everyday, complex tasks',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],\n },\n // Older, still-servable ids the CLI no longer lists (\"more models\").\n {\n value: 'claude-opus-4-8',\n resolvedModel: 'claude-opus-4-8',\n displayName: 'Opus 4.8',\n description: 'Opus 4.8 · Previous Opus generation',\n },\n {\n value: 'sonnet',\n resolvedModel: 'claude-sonnet-5',\n displayName: 'Sonnet 5',\n description: 'Sonnet 5 · Efficient for routine tasks',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],\n },\n {\n value: 'claude-sonnet-4-6',\n resolvedModel: 'claude-sonnet-4-6',\n displayName: 'Sonnet 4.6',\n description: 'Sonnet 4.6 · Previous Sonnet generation',\n },\n {\n value: 'haiku',\n resolvedModel: 'claude-haiku-4-5-20251001',\n displayName: 'Haiku 4.5',\n description: 'Haiku 4.5 · Fastest for quick answers',\n primary: true,\n // Explicitly none: the CLI reports no effort support for Haiku 4.5, and\n // an absent field would wrongly imply the engine's default set.\n reasoningEfforts: [],\n },\n ],\n}\n","import { listSessions as sdkListSessions } from '@anthropic-ai/claude-agent-sdk'\nimport { ENGINE_CAPABILITIES } from '@workerdeck/protocol'\nimport { checkClaudeAuth } from './auth.ts'\nimport { SessionRunner } from './runner.ts'\nimport type { EngineAdapter } from '../adapter.ts'\nimport { CLAUDE_CATALOG } from './catalog.ts'\n\n/**\n * The Claude engine as an adapter — a thin, behaviourally inert wrapper:\n * `SessionRunner` unchanged, `checkClaudeAuth` as the probe, the static\n * catalog for create forms. Exists so catalogs, capabilities and availability\n * have one shape across engines; the runner itself is exactly what\n * `registry.prepare()` builds.\n */\nexport const claudeAdapter: EngineAdapter = {\n engine: 'claude',\n capabilities: ENGINE_CAPABILITIES.claude,\n catalog: CLAUDE_CATALOG,\n async checkAvailability(profile, env) {\n const status = await checkClaudeAuth(env)\n if (status === 'logged_in') return { available: true }\n if (status === 'logged_out') {\n return {\n available: false,\n reason:\n `no usable Claude credentials for this profile's environment — log in under its ` +\n `config dir (CLAUDE_CONFIG_DIR=${profile.configDir ?? '~/.claude'} claude auth login) ` +\n 'or set ANTHROPIC_API_KEY',\n }\n }\n return { available: 'unknown' }\n },\n createRunner({ config, restore, id }) {\n if (restore) throw new Error('the Claude engine cannot rebuild a parked session')\n return new SessionRunner(config, id)\n },\n /**\n * The Agent SDK's on-disk session store, mapped browser-safe. The SDK reads\n * the store of the *process* environment — it takes no config dir — so a\n * profile pin cannot narrow this listing; that matches the route's\n * pre-adapter behavior exactly (the listing was always process-global).\n */\n async listSessions({ dir, limit, offset }) {\n const sessions = await sdkListSessions({ dir, limit, offset })\n return sessions.map((s) => ({\n sessionId: s.sessionId,\n summary: s.summary,\n lastModified: s.lastModified,\n createdAt: s.createdAt,\n customTitle: s.customTitle,\n firstPrompt: s.firstPrompt,\n gitBranch: s.gitBranch,\n cwd: s.cwd,\n }))\n },\n}\n","import { appendFileSync } from 'node:fs'\nimport type { Readable, Writable } from 'node:stream'\n\n/**\n * Set to a file path to append every inbound app-server notification and\n * server→client request as JSONL. Off unless set; see `docs/GOTCHAS.md` §codex.\n */\nexport const CODEX_TRACE_ENV = 'WORKERDECK_CODEX_TRACE'\n\n/**\n * A JSON-RPC error response from the peer, or one we return to it. `code`\n * follows the JSON-RPC 2.0 reserved ranges (-32601 = method not found).\n */\nexport class JsonRpcError extends Error {\n readonly code: number\n constructor(code: number, message: string) {\n super(message)\n this.name = 'JsonRpcError'\n this.code = code\n }\n}\n\ntype Pending = {\n method: string\n resolve: (result: unknown) => void\n reject: (error: Error) => void\n}\n\n/**\n * JSON-RPC over a `codex app-server` child's stdio: **newline-delimited JSON**,\n * one message per line, and — verified against 0.146.0 — an envelope *without*\n * the `jsonrpc: \"2.0\"` field (`{id, method, params}` / `{id, result}` /\n * `{id, error}`; the binary's own schema marks only those required). Server→\n * client notifications additionally carry a top-level `emittedAtMs`, ignored\n * here.\n *\n * Transport only: no method knowledge, no process ownership. The process\n * wrapper (`process.ts`) owns the child and calls {@link fail} when it dies so\n * every in-flight request rejects instead of hanging.\n */\nexport class JsonRpcStdioConnection {\n #output: Writable\n #nextId = 1\n #pending = new Map<number, Pending>()\n #buffer = ''\n #closed = false\n #notificationHandler: ((method: string, params: unknown) => void) | undefined\n /** Where {@link CODEX_TRACE_ENV} pointed, or undefined — read once. */\n #trace: string | undefined\n #requestHandler:\n | ((method: string, params: unknown, id: string | number) => Promise<unknown>)\n | undefined\n\n constructor(options: { input: Readable; output: Writable }) {\n this.#output = options.output\n this.#trace = process.env[CODEX_TRACE_ENV] || undefined\n options.input.on('data', (chunk: Buffer | string) => this.#feed(String(chunk)))\n // Stream errors surface via the process wrapper's exit handling; swallowing\n // here just prevents an unhandled 'error' crash between the two.\n options.input.on('error', () => {})\n options.output.on('error', () => {})\n }\n\n request(method: string, params?: unknown): Promise<unknown> {\n if (this.#closed) return Promise.reject(new Error(`codex app-server is closed (${method})`))\n const id = this.#nextId++\n return new Promise((resolve, reject) => {\n this.#pending.set(id, { method, resolve, reject })\n this.#write({ id, method, ...(params === undefined ? {} : { params }) })\n })\n }\n\n notify(method: string, params?: unknown): void {\n if (this.#closed) return\n this.#write({ method, ...(params === undefined ? {} : { params }) })\n }\n\n onNotification(handler: (method: string, params: unknown) => void): void {\n this.#notificationHandler = handler\n }\n\n onRequest(handler: (method: string, params: unknown, id: string | number) => Promise<unknown>): void {\n this.#requestHandler = handler\n }\n\n /** Reject everything in flight and refuse new traffic — the child is gone\n * (or the session is over). Idempotent. */\n fail(message: string): void {\n if (this.#closed) return\n this.#closed = true\n const pending = [...this.#pending.values()]\n this.#pending.clear()\n for (const entry of pending) {\n entry.reject(new Error(`${message} (awaiting ${entry.method})`))\n }\n }\n\n #write(payload: object): void {\n try {\n this.#output.write(JSON.stringify(payload) + '\\n')\n } catch {\n // A broken pipe races the exit event; the wrapper's fail() explains it.\n }\n }\n\n #feed(chunk: string): void {\n this.#buffer += chunk\n let newline: number\n while ((newline = this.#buffer.indexOf('\\n')) >= 0) {\n const line = this.#buffer.slice(0, newline).trim()\n this.#buffer = this.#buffer.slice(newline + 1)\n if (!line) continue\n let message: Record<string, unknown>\n try {\n message = JSON.parse(line) as Record<string, unknown>\n } catch {\n continue // never let one garbled line kill the session\n }\n this.#traceLine(message)\n this.#dispatch(message)\n }\n }\n\n /**\n * Append one inbound message to the trace file, when the operator asked for\n * one. **Notifications and server→client requests only** — a response body is\n * not needed to answer the questions this exists for, and `account/*` results\n * are the one place app-server traffic can carry a masked credential\n * fragment, which nothing of ours writes to disk (see the auth red lines).\n * Best-effort and synchronous: a debug sink that loses lines proves nothing,\n * and a debug sink that throws must not take the session with it.\n */\n #traceLine(message: Record<string, unknown>): void {\n if (!this.#trace) return\n const method = message.method\n if (typeof method !== 'string') return\n if (method.startsWith('account/') || method.startsWith('login')) return\n try {\n appendFileSync(this.#trace, JSON.stringify(message) + '\\n')\n } catch {\n // Unwritable path, full disk: tracing is never worth failing a session.\n }\n }\n\n #dispatch(message: Record<string, unknown>): void {\n const { id, method } = message\n if (typeof method === 'string') {\n if (id === undefined || id === null) {\n this.#notificationHandler?.(method, message.params)\n return\n }\n // Server→client request: the handler's resolution is the response. No\n // handler (or a throw) becomes a JSON-RPC error, never a hang — an\n // unanswered approval would wedge the turn.\n const respond = (payload: object) => this.#write({ id: id as string | number, ...payload })\n const handler = this.#requestHandler\n if (!handler) {\n respond({ error: { code: -32601, message: `no handler for server request '${method}'` } })\n return\n }\n handler(method, message.params, id as string | number).then(\n (result) => respond({ result: result ?? {} }),\n (error: unknown) =>\n respond({\n error: {\n code: error instanceof JsonRpcError ? error.code : -32603,\n message: error instanceof Error ? error.message : String(error),\n },\n }),\n )\n return\n }\n if (id === undefined || id === null) return\n const pending = this.#pending.get(id as number)\n if (!pending) return\n this.#pending.delete(id as number)\n if (message.error !== undefined && message.error !== null) {\n const error = message.error as { code?: number; message?: string }\n pending.reject(\n new JsonRpcError(error.code ?? -32603, error.message ?? `request '${pending.method}' failed`),\n )\n return\n }\n pending.resolve(message.result)\n }\n}\n","import { SUBAGENT_HISTORY, type SubagentInfo } from '@workerdeck/protocol'\n\n/**\n * The codex side of `SessionInfo.subagents` — and the attribution table that\n * gives every event a spawned agent produces its `parentToolUseId`.\n *\n * Codex's signal is stronger than the claude engine's, so this is deliberately\n * NOT that tracker generalised (`engines/claude/subagents.ts` infers spawns\n * from tool names and verdicts from result-text sniffing, ~290 lines of module\n * doc explaining the inference). Here nothing is inferred: `subAgentActivity\n * {kind: 'started'}` on the owning thread positively announces an agent, names\n * it (`agentPath`), keys it (`agentThreadId` — the id every one of its later\n * notifications carries) and hands over the model's own `spawn_agent` call id;\n * the agent's end is its own thread's `turn/completed`, status included. So a\n * record is keyed by **thread id** — the wire's handle — while exposing a\n * **tool-use id** — the protocol's: `parentToolUseId` on nested events must\n * equal the anchor `tool_use`'s id for `subagentItems` (the frame membership\n * rule every client shares) to reassemble the sidechain, and this map is where\n * the two vocabularies meet.\n *\n * Two decisions worth their prose:\n *\n * **A record survives the runner's turns.** Codex agents are designed to\n * outlive the root turn that spawned them (`sendInput`/`resumeAgent` address a\n * thread that kept existing), so — unlike a pending approval — nothing here is\n * swept when a root turn ends. What does end every agent is the app-server\n * process itself: the runner calls {@link sweep} when the child dies or the\n * session closes, because an agent whose host process is gone can never report,\n * and `running` on a closed session would be a lie a polled list re-renders\n * forever (the claude tracker's argument, inherited whole).\n *\n * **The settled tail is bounded, running records never are** — the same\n * {@link SUBAGENT_HISTORY} discipline as the claude tracker, and enforced at\n * settle time for the same reason: a settle happens once per agent, `list()`\n * once per row of a 1.2s-polled sessions list.\n */\nexport class CodexAgentTracker {\n #byThread = new Map<string, CodexAgent>()\n #settleCounter = 0\n\n /** The record whose thread this is — the attribution lookup. */\n get(agentThreadId: string): CodexAgent | undefined {\n return this.#byThread.get(agentThreadId)\n }\n\n /** Open (or return) the record for a thread. Fill-in, never overwrite: a\n * label-less fallback record keeps its accumulated count and its already\n * published toolUseId when the announcing item arrives late. */\n open(\n agentThreadId: string,\n toolUseId: string,\n agentType: string | undefined,\n ts: number,\n ): CodexAgent {\n let record = this.#byThread.get(agentThreadId)\n if (!record) {\n record = {\n agentThreadId,\n toolUseId,\n status: 'running',\n startedAt: ts,\n toolCount: 0,\n counted: new Set(),\n }\n this.#byThread.set(agentThreadId, record)\n }\n record.agentType ??= agentType\n return record\n }\n\n /** The agent's thread ran again (`kind: 'interacted'`, or a fresh\n * `turn/started` on its thread): a settled verdict no longer describes it. */\n revive(record: CodexAgent): void {\n record.status = 'running'\n record.settledOrder = undefined\n }\n\n #settle(record: CodexAgent, status: 'done' | 'failed'): void {\n record.status = status\n record.settledOrder = ++this.#settleCounter\n let settled = 0\n for (const r of this.#byThread.values()) {\n if (r.settledOrder !== undefined) settled++\n }\n while (settled > SUBAGENT_HISTORY) {\n let oldest: CodexAgent | undefined\n for (const r of this.#byThread.values()) {\n if (r.settledOrder === undefined) continue\n if (!oldest || r.settledOrder < oldest.settledOrder!) oldest = r\n }\n if (!oldest) break\n this.#byThread.delete(oldest.agentThreadId)\n settled--\n }\n }\n\n /** A real verdict for one agent — its thread's `turn/completed`, or the\n * `interrupted` activity edge. */\n settle(record: CodexAgent, status: 'done' | 'failed'): void {\n if (record.status === status) return\n this.#settle(record, status)\n }\n\n /** The process the agents lived in is gone (child death, session close):\n * everything still running is settled as failed — the report can never come. */\n sweep(): void {\n for (const record of this.#byThread.values()) {\n if (record.status === 'running') this.#settle(record, 'failed')\n }\n }\n\n /** The rollup as `SessionInfo.subagents` serves it — spawn order, fresh\n * objects, and `undefined` when there is nothing to say (absent and empty\n * mean the same thing to a client, and bytes on a polled list are paid for). */\n list(): SubagentInfo[] | undefined {\n if (this.#byThread.size === 0) return undefined\n const out: SubagentInfo[] = []\n for (const r of this.#byThread.values()) {\n out.push({\n toolUseId: r.toolUseId,\n agentType: r.agentType,\n status: r.status,\n startedAt: r.startedAt,\n toolCount: r.toolCount,\n })\n }\n return out\n }\n}\n\nexport type CodexAgent = {\n /** The wire's handle: the thread every one of this agent's notifications names. */\n agentThreadId: string\n /** The protocol's handle: the anchor `tool_use`'s id, which nested events\n * carry as `parentToolUseId` and `SubagentInfo.toolUseId` publishes. */\n toolUseId: string\n /** The agent's name — `agentPath`'s basename ('date_one'). Doubles as the\n * anchor input's `subagent_type`, which is the field `isAgentRecord` and\n * `taskIdentity` key a pressable, labelled row off. */\n agentType?: string\n status: 'running' | 'done' | 'failed'\n startedAt: number\n /** Nested tool calls emitted so far — the running progress reading. */\n toolCount: number\n /** Tool-use ids already counted: `imageGeneration` re-emits its card with the\n * finished input (an upsert, one row), and a count that ticked twice for one\n * picture would make two agents' readings incomparable. */\n counted: Set<string>\n /** Whether the anchor `tool_use` row exists in the transcript yet. */\n anchored?: boolean\n /** Monotonic settle stamp; the retention bound evicts the smallest. Insertion\n * order cannot stand in: records open in spawn order, and a slow early agent\n * settles after a fast late one. */\n settledOrder?: number\n}\n","/**\n * Codex project trust: will this session's cwd get its `.codex/config.toml`?\n *\n * Codex only layers a project's `.codex/config.toml` onto the operator's base\n * config when the project is *trusted* (a `[projects.\"<path>\"]\n * trust_level = \"trusted\"` entry in `$CODEX_HOME/config.toml`), and the\n * app-server surface has no trust prompt — that lives in the TUI. So under\n * WorkerDeck an untrusted project's config, MCP servers included, is silently\n * ignored: no error, no notice, servers just missing. The runner asks this\n * module at session start whether that is about to happen, so the transcript\n * can say so.\n *\n * Semantics, all measured against both the bundled 0.146.0 and 0.149.0\n * (2026-08-22, via `codex mcp list` from probe cwds and via `thread/start` +\n * `mcpServerStatus/list` on the app-server surface — identical answers):\n *\n * - **Discovery**: config layers come from the cwd and its ancestors up to and\n * including the nearest directory containing `.git` (dir or file). With no\n * git anywhere above, the cwd alone is consulted. Directories above the\n * nearest git root never contribute, trusted or not.\n * - **Trust per layer**: an exact entry for the layer's own canonical path\n * decides (an explicit `\"untrusted\"` beats inherited trust); without one the\n * layer inherits from the chain's git root — trusted iff the git root has a\n * trusted entry, where a linked worktree's root also counts its main\n * repository's entry (the `.git` file's gitdir names it). A trusted\n * mid-chain directory does NOT trust its children, and plain path\n * containment without git confers nothing.\n * - **Canonical paths**: codex matches entries against the canonicalized cwd —\n * a macOS `/tmp/...` entry never matches the `/private/tmp/...` it points\n * at, while the reverse spelling works (and the app-server canonicalizes its\n * `cwd` param too). Both sides here are realpath'd, which can only err\n * toward silence.\n * - **The gate is sandbox-scoped**: `thread/start` under `workspace-write` or\n * `danger-full-access` (permission modes `acceptEdits`/`bypassPermissions`)\n * WRITES the trust entry itself and loads the config — only `read-only`\n * (mode `default`) leaves the project untrusted and the config ignored. A\n * later `turn/start` with a wider sandboxPolicy does not heal the thread\n * (measured): the caller probes `default`-mode sessions only, and the notice\n * stays true for the session it opens.\n * - `trust_level`'s vocabulary is exactly `trusted`/`untrusted`; any other\n * value fails codex's bootstrap outright (\"unknown variant\"), so a config\n * carrying one probes silent — that session announces its own failure.\n *\n * The correctness bar for every degrade path: a FALSE notice — warning about a\n * project codex actually trusts — is worse than a missed one. The narrow TOML\n * reader below refuses (→ silence) anything it cannot interpret with\n * certainty, rather than guessing.\n */\nimport { existsSync, readFileSync, realpathSync, statSync } from 'node:fs'\nimport { dirname, join, resolve, sep } from 'node:path'\n\nconst BARE_KEY = /[A-Za-z0-9_-]/\n\nfunction skipWs(text: string, pos: number): number {\n let i = pos\n while (i < text.length && (text[i] === ' ' || text[i] === '\\t')) i++\n return i\n}\n\ntype Parsed<T> = { value: T; end: number } | undefined\n\n/** One-line TOML basic string starting at `pos` (which must be `\"`). Undefined\n * on an escape TOML doesn't define or a close quote that never comes — the\n * caller refuses the file rather than guessing what codex would read. */\nfunction parseBasicString(text: string, pos: number): Parsed<string> {\n let out = ''\n let i = pos + 1\n while (i < text.length) {\n const ch = text[i]\n if (ch === '\"') return { value: out, end: i + 1 }\n if (ch === '\\\\') {\n const esc = text[i + 1]\n if (esc === 'b') out += '\\b'\n else if (esc === 't') out += '\\t'\n else if (esc === 'n') out += '\\n'\n else if (esc === 'f') out += '\\f'\n else if (esc === 'r') out += '\\r'\n else if (esc === '\"') out += '\"'\n else if (esc === '\\\\') out += '\\\\'\n else if (esc === 'u' || esc === 'U') {\n const width = esc === 'u' ? 4 : 8\n const hex = text.slice(i + 2, i + 2 + width)\n if (hex.length !== width || !/^[0-9A-Fa-f]+$/.test(hex)) return undefined\n const code = Number.parseInt(hex, 16)\n if (code > 0x10ffff) return undefined\n out += String.fromCodePoint(code)\n i += width\n } else return undefined\n i += 2\n continue\n }\n out += ch\n i++\n }\n return undefined\n}\n\n/** One-line TOML literal string starting at `pos` (which must be `'`). */\nfunction parseLiteralString(text: string, pos: number): Parsed<string> {\n const close = text.indexOf(\"'\", pos + 1)\n if (close === -1) return undefined\n return { value: text.slice(pos + 1, close), end: close + 1 }\n}\n\n/** A dotted key path — bare, `\"basic\"` and `'literal'` keys, whitespace around\n * the dots — as found in table headers and on the left of assignments. */\nfunction parseKeyPath(text: string, pos: number): Parsed<string[]> {\n const keys: string[] = []\n let i = pos\n for (;;) {\n i = skipWs(text, i)\n const ch = text[i]\n if (ch === '\"' || ch === \"'\") {\n const str = ch === '\"' ? parseBasicString(text, i) : parseLiteralString(text, i)\n if (!str) return undefined\n keys.push(str.value)\n i = str.end\n } else if (ch !== undefined && BARE_KEY.test(ch)) {\n let end = i\n while (end < text.length && BARE_KEY.test(text[end])) end++\n keys.push(text.slice(i, end))\n i = end\n } else {\n return undefined\n }\n i = skipWs(text, i)\n if (text[i] !== '.') return { value: keys, end: i }\n i++\n }\n}\n\n/**\n * Scan an assignment's value (or the continuation line of a multi-line array),\n * confirming where it ends. Returns the bracket depth carried onto the next\n * line (0 = the value is complete) plus the string itself when the whole value\n * was one plain one-line string. Undefined refuses the file: multi-line\n * strings are where a line reader starts misreading string *content* as\n * sections and entries — the exact mistake that could flip a real trust entry\n * — so they are not parsed around, they end the attempt.\n */\nfunction scanValueLine(\n text: string,\n pos: number,\n depth: number,\n): { depth: number; value?: string } | undefined {\n let i = skipWs(text, pos)\n if (depth === 0 && (text[i] === '\"' || text[i] === \"'\")) {\n if (text.startsWith('\"\"\"', i) || text.startsWith(\"'''\", i)) return undefined\n const str = text[i] === '\"' ? parseBasicString(text, i) : parseLiteralString(text, i)\n if (!str) return undefined\n const rest = skipWs(text, str.end)\n if (rest < text.length && text[rest] !== '#') return undefined\n return { depth: 0, value: str.value }\n }\n while (i < text.length) {\n const ch = text[i]\n if (ch === '#') break\n if (ch === '\"' || ch === \"'\") {\n if (text.startsWith('\"\"\"', i) || text.startsWith(\"'''\", i)) return undefined\n const str = ch === '\"' ? parseBasicString(text, i) : parseLiteralString(text, i)\n if (!str) return undefined\n i = str.end\n continue\n }\n if (ch === '[' || ch === '{') depth++\n else if (ch === ']' || ch === '}') {\n depth--\n if (depth < 0) return undefined\n }\n i++\n }\n return { depth }\n}\n\n/**\n * The `[projects.\"<path>\"] trust_level = \"...\"` entries of a codex\n * `config.toml`, by a deliberately narrow reader (core takes no TOML\n * dependency for this). Handles what codex itself writes plus the reasonable\n * hand-edits — comments, CRLF, whitespace, quoted keys with escapes, literal\n * and bare keys, `[projects]`-with-dotted-keys and top-level dotted forms,\n * single-line inline tables, multi-line arrays — and returns **undefined for\n * anything else it meets anywhere in the file** (multi-line strings,\n * `projects` as an inline table, array-of-tables, junk): the caller treats\n * undefined as \"cannot know\" and stays silent. Conflicting duplicate entries\n * also refuse — invalid for TOML, and guessing wrong is a false notice.\n */\nexport function parseProjectTrustEntries(source: string): Map<string, string> | undefined {\n const entries = new Map<string, string>()\n let section: string[] = []\n let carryDepth = 0\n for (const rawLine of source.split('\\n')) {\n const line = rawLine.endsWith('\\r') ? rawLine.slice(0, -1) : rawLine\n if (carryDepth > 0) {\n const scanned = scanValueLine(line, 0, carryDepth)\n if (!scanned) return undefined\n carryDepth = scanned.depth\n continue\n }\n const start = skipWs(line, 0)\n if (start >= line.length || line[start] === '#') continue\n if (line[start] === '[') {\n const array = line.startsWith('[[', start)\n const path = parseKeyPath(line, start + (array ? 2 : 1))\n if (!path) return undefined\n const close = array ? ']]' : ']'\n if (!line.startsWith(close, path.end)) return undefined\n const rest = skipWs(line, path.end + close.length)\n if (rest < line.length && line[rest] !== '#') return undefined\n if (array && path.value[0] === 'projects') return undefined\n section = path.value\n continue\n }\n const key = parseKeyPath(line, start)\n if (!key) return undefined\n if (line[key.end] !== '=') return undefined\n const scanned = scanValueLine(line, key.end + 1, 0)\n if (!scanned) return undefined\n carryDepth = scanned.depth\n const full = [...section, ...key.value]\n if (full[0] !== 'projects') continue\n // `projects = {...}` / `projects.\"<p>\" = {...}`: whole-entry forms this\n // reader does not interpret — refuse rather than miss a trust_level inside.\n if (full.length < 3) return undefined\n if (full.length === 3 && full[2] === 'trust_level') {\n if (carryDepth !== 0 || scanned.value === undefined) return undefined\n const project = full[1] as string\n const existing = entries.get(project)\n if (existing !== undefined && existing !== scanned.value) return undefined\n entries.set(project, scanned.value)\n }\n }\n if (carryDepth > 0) return undefined\n return entries\n}\n\n/**\n * A linked worktree inherits trust from its main repository's entry (measured:\n * trusting the main repo path loads the worktree's project config). The\n * worktree's `.git` is a FILE whose `gitdir:` line names\n * `<main>/.git/worktrees/<name>`; the directory owning that `.git` is the\n * anchor to look up. Anything unreadable or shaped differently resolves false\n * — this route can only ADD trust, i.e. silence, never a false notice.\n */\nfunction mainRepositoryTrusted(gitRootDir: string, canonical: Map<string, string>): boolean {\n const gitPath = join(gitRootDir, '.git')\n try {\n if (!statSync(gitPath).isFile()) return false\n const match = /^gitdir:[ \\t]*(.+?)[ \\t]*$/m.exec(readFileSync(gitPath, 'utf8'))\n if (!match) return false\n const gitdir = resolve(gitRootDir, match[1])\n const at = gitdir.lastIndexOf(`${sep}.git${sep}`)\n if (at <= 0) return false\n let main = gitdir.slice(0, at)\n try {\n main = realpathSync(main)\n } catch {\n // a main repo that moved still compares by the name the gitdir uses\n }\n return canonical.get(main) === 'trusted'\n } catch {\n return false\n }\n}\n\n/**\n * The notice for a codex session about to run on a cwd whose\n * `.codex/config.toml` codex will ignore, or undefined when there is nothing\n * to say — no project config anywhere codex would look, the project is\n * trusted, or the situation cannot be established with certainty. Read-only\n * throughout: WorkerDeck never writes trust entries (adjacent to the auth red\n * lines — trusting a directory is the operator's decision, made in codex's\n * own prompt or by their own hand).\n */\nexport function untrustedProjectNotice(options: {\n cwd: string\n codexHome: string\n}): string | undefined {\n let cwd: string\n try {\n cwd = realpathSync(options.cwd)\n } catch {\n // A cwd that doesn't resolve is the engine's own loud failure, not ours.\n return undefined\n }\n let home = resolve(options.codexHome)\n try {\n home = realpathSync(options.codexHome)\n } catch {\n // Keep the resolved spelling; only used to recognize the home-as-layer case.\n }\n // Discovery: cwd up to and including the nearest `.git` holder; without one,\n // the cwd alone (measured — no-git ancestors are never consulted).\n const chain: string[] = []\n let dir = cwd\n for (;;) {\n chain.push(dir)\n if (existsSync(join(dir, '.git'))) break\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n const anchor = chain[chain.length - 1] as string\n const gitRoot = existsSync(join(anchor, '.git')) ? anchor : undefined\n const layers = (gitRoot ? chain : [cwd]).filter((layer) => {\n if (!existsSync(join(layer, '.codex', 'config.toml'))) return false\n try {\n // The cwd whose `.codex` IS the codex home: that config is the base\n // config and always loads — nothing is being ignored there.\n return realpathSync(join(layer, '.codex')) !== home\n } catch {\n return false\n }\n })\n // Only read the operator's config once a project config exists to be ignored\n // — the common session touches nothing outside its own cwd chain.\n if (layers.length === 0) return undefined\n const homeConfigPath = join(options.codexHome, 'config.toml')\n let source = ''\n try {\n source = readFileSync(homeConfigPath, 'utf8')\n } catch (error) {\n // Absent = knowably no trust entries; unreadable = unknowable, silence.\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') return undefined\n }\n const entries = parseProjectTrustEntries(source)\n if (!entries) return undefined\n for (const value of entries.values()) {\n if (value !== 'trusted' && value !== 'untrusted') return undefined\n }\n // Entries land under their canonical path, matching codex's canonical-cwd\n // comparison. Two spellings of one directory with conflicting verdicts keep\n // the trusted one — the direction that stays silent.\n const canonical = new Map<string, string>()\n for (const [key, value] of entries) {\n let path = key\n try {\n path = realpathSync(key)\n } catch {\n // an entry for a path that no longer exists still compares literally\n }\n if (canonical.get(path) === 'trusted') continue\n canonical.set(path, value)\n }\n const rootTrusted =\n gitRoot !== undefined &&\n (canonical.get(gitRoot) === 'trusted' || mainRepositoryTrusted(gitRoot, canonical))\n const ignored = layers.filter((layer) => {\n const entry = canonical.get(layer)\n // An explicit verdict on the layer's own path beats inherited trust\n // (measured); absent one, the git root's trust covers the whole chain.\n if (entry !== undefined) return entry !== 'trusted'\n return !rootTrusted\n })\n if (ignored.length === 0) return undefined\n const trustDir = gitRoot ?? cwd\n const configs = ignored.map((layer) => join(layer, '.codex', 'config.toml'))\n const what =\n configs.length === 1\n ? `its project config (${configs[0]}) is`\n : `its project configs (${configs.join(', ')}) are`\n return (\n `codex does not trust this directory, so ${what} being ignored — MCP servers and ` +\n `settings declared there will be missing from this session. To trust it, run codex once ` +\n `in ${trustDir} and accept the trust prompt, or add [projects.\"${trustDir}\"] with ` +\n `trust_level = \"trusted\" to ${homeConfigPath}.`\n )\n}\n","import { createHash, randomUUID } from 'node:crypto'\nimport { mkdirSync, rmSync, statSync, writeFileSync } from 'node:fs'\nimport { homedir, tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport {\n ENGINE_CAPABILITIES,\n PROTOCOL_VERSION,\n contextReading,\n type ContextReading,\n transcriptActivity,\n type ContentBlock,\n type CreateSessionRequest,\n type FilePatch,\n type McpServerStatusInfo,\n type PermissionDecisionSource,\n type PermissionMode,\n type PermissionRequest,\n type SessionEvent,\n type SessionEventBody,\n type SessionInfo,\n type SessionStatus,\n type SkillInfo,\n type UserQuestion,\n} from '@workerdeck/protocol'\nimport {\n attachmentKind,\n attachmentRef,\n normalizeMediaType,\n type AttachmentInput,\n} from '../../lib/attachments.ts'\nimport { parseUnifiedDiff } from '../../lib/patch.ts'\nimport type { PermissionDecision, Runner, SessionEventListener } from '../../runner-interface.ts'\nimport { SubscriberSet, type SubscribeOptions } from '../../lib/subscribers.ts'\nimport { JsonRpcError } from './jsonrpc.ts'\nimport { CodexAgentTracker, type CodexAgent } from './subagents.ts'\nimport { untrustedProjectNotice } from './trust.ts'\nimport type {\n AppServerCollabAgentToolCallItem,\n AppServerCommandApprovalParams,\n AppServerConnection,\n AppServerConnectFn,\n AppServerElicitationParams,\n AppServerFileChangeApprovalParams,\n AppServerHistoryTurn,\n AppServerImageGenerationItem,\n AppServerItem,\n AppServerMcpServerStatus,\n AppServerMcpServerStatusResponse,\n AppServerMcpStatusUpdate,\n AppServerPermissionsApprovalParams,\n AppServerPlanUpdate,\n AppServerRateLimits,\n AppServerSkillMetadata,\n AppServerSkillsListResponse,\n AppServerTokenUsage,\n AppServerTokenUsageUpdate,\n AppServerTurn,\n AppServerUnknownItem,\n AppServerUserInput,\n AppServerUserInputParams,\n AppServerUserInputQuestion,\n AppServerUserMessageItem,\n} from './types.ts'\n\n/**\n * thread/start's sandbox axis (string form) — our permission modes as codex\n * sandbox modes: `default` → read-only (reads run; any mutation is refused by\n * the OS sandbox and — with the ask policy below — escalates to a real\n * question), `acceptEdits` → workspace-write (in-workspace writes sail\n * through, the acceptEdits grant), `bypassPermissions` → danger-full-access.\n * `auto` rides the SAME sandbox as acceptEdits — it is not a wider grant, it\n * only moves *who answers* the approvals (see {@link APPROVALS_REVIEWER_BY_MODE}).\n */\nconst THREAD_SANDBOX_BY_MODE: Partial<Record<PermissionMode, string>> = {\n default: 'read-only',\n acceptEdits: 'workspace-write',\n auto: 'workspace-write',\n bypassPermissions: 'danger-full-access',\n}\n\n/**\n * turn/start's sandboxPolicy axis (object form — same policy, second shape).\n *\n * The `workspaceWrite` entries here are a SHAPE, not the whole policy: every\n * unstated field of that variant is serde-defaulted by the app-server, so\n * sending it bare silently overrides the operator's `[sandbox_workspace_write]`\n * — `network_access` back to false, `writable_roots` back to empty — on every\n * turn. {@link CodexRunner.#turnSandboxPolicy} restates those fields from\n * `config/read`; nothing else may send this map's `workspaceWrite` entries\n * directly.\n */\nconst TURN_SANDBOX_BY_MODE: Partial<Record<PermissionMode, { type: string }>> = {\n default: { type: 'readOnly' },\n acceptEdits: { type: 'workspaceWrite' },\n auto: { type: 'workspaceWrite' },\n bypassPermissions: { type: 'dangerFullAccess' },\n}\n\n/**\n * `[sandbox_workspace_write]` as codex resolves it FOR THIS CWD (project\n * layers included), read once per child from `config/read` and restated on\n * every `turn/start` — see {@link CodexRunner.#readWorkspaceWrite}.\n *\n * Note the axis this represents: network access is **not** an approval\n * question, it is a property of the workspace-write sandbox, off by default,\n * and no approval policy turns it on. WorkerDeck sets it nowhere — the\n * operator's `config.toml` is the only source, exactly as codex documents.\n * All this type does is stop us from clobbering their answer.\n */\ntype CodexWorkspaceWrite = {\n writableRoots: string[]\n networkAccess: boolean\n excludeTmpdirEnvVar: boolean\n excludeSlashTmp: boolean\n}\n\n/**\n * The approval axis, stated as the GRANULAR object on both thread/start and\n * turn/start — never the string vocabulary, deliberately and unconditionally:\n * measured against 0.146.0, plain `'untrusted'` never asked anything (a\n * sandbox-violating write was silently refused, a safe echo auto-approved),\n * while the granular flags make a blocked action a real server→client\n * question. Granular policies are gated on `capabilities.experimentalApi` at\n * initialize; WorkerDeck declares it always and keeps NO non-experimental\n * fallback — a future binary that rejects either gate fails loudly (see\n * {@link CodexRunner.#ensureThread}) instead of quietly not asking.\n *\n * `default`/`acceptEdits` ask (all flags on — the sandbox axis above already\n * decides *what needs asking*); `bypassPermissions` asks nothing, same shape.\n */\nconst GRANULAR_ASK = {\n granular: {\n sandbox_approval: true,\n rules: true,\n mcp_elicitations: true,\n request_permissions: true,\n skill_approval: true,\n },\n}\nconst GRANULAR_NEVER = {\n granular: {\n sandbox_approval: false,\n rules: false,\n mcp_elicitations: false,\n request_permissions: false,\n skill_approval: false,\n },\n}\n/**\n * Notifications whose meaning is scoped to ONE thread, and which are therefore\n * only ever read off the session's own. Everything else (items, deltas) is\n * accepted from any thread on the connection — see `#handleNotification`.\n */\nconst THREAD_SCOPED_NOTIFICATIONS = new Set([\n 'turn/started',\n 'turn/completed',\n 'thread/tokenUsage/updated',\n])\n\nconst APPROVAL_POLICY_BY_MODE: Partial<Record<PermissionMode, object>> = {\n default: GRANULAR_ASK,\n acceptEdits: GRANULAR_ASK,\n // `auto` still ASKS — the flags are what produce an approval request at all.\n // Without them there would be nothing for the reviewer below to answer.\n auto: GRANULAR_ASK,\n bypassPermissions: GRANULAR_NEVER,\n}\n\n/**\n * The THIRD approval axis — *who reviews*, independent of the sandbox axis and\n * the ask axis above. Codex's `approvalsReviewer` (thread/start and turn/start,\n * present since 0.146.0) routes every approval request either to the user\n * (`'user'`, codex's own default) or to `'auto_review'`: a prompted subagent\n * that gathers context and applies a risk framework before allowing or denying.\n * That is codex's \"Approve for me\" preset, and our `auto` mode is exactly it.\n *\n * Sent explicitly for EVERY mode rather than omitted for the default — a thread\n * inherits `approvalsReviewer` across turns (\"this turn and subsequent turns\"),\n * so leaving it unset would let a stale reviewer from an earlier turn survive a\n * mode switch back to a user-reviewed mode. Stating it every time makes the\n * mode the single source of truth.\n *\n * NOTE the asymmetry with the Claude engine's `auto`: that classifier is\n * operator-configurable (`autoMode.environment`, allow/soft_deny/hard_deny);\n * this reviewer has no configuration surface at all.\n */\nconst APPROVALS_REVIEWER_BY_MODE: Partial<Record<PermissionMode, string>> = {\n default: 'user',\n acceptEdits: 'user',\n auto: 'auto_review',\n bypassPermissions: 'user',\n}\n\n/** Fallback timeout for a pending approval nobody answers — the SessionRunner\n * default, so unattended codex sessions land the same way Claude ones do. */\nconst DEFAULT_APPROVAL_TIMEOUT_MS = 300_000\n\n/**\n * Tool name for codex's built-in `image_gen`. A stable string because it is a\n * rendering contract: both clients key an icon (and, where they can reach the\n * host filesystem, an inline preview) off it.\n */\nexport const CODEX_IMAGE_TOOL = 'CodexImageGeneration'\n\n/**\n * Tool name for a spawned agent's anchor `tool_use` — the claude engine's\n * `Task` in this engine's vocabulary. Codex never sends such a call: the model's\n * `spawn_agent` surfaces only as the `subAgentActivity` marker item, so the\n * runner authors the call itself, because everything downstream is built on a\n * top-level `tool_use` existing — `terminalBlocks` absorbs a sidechain into the\n * call whose id its events carry as `parentToolUseId`, the takeover frames by\n * it, and `taskIdentity` labels it from the input's `subagent_type`. Not a new\n * wire idea, just a row: the same shape every other codex tool card uses.\n */\nexport const CODEX_AGENT_TOOL = 'CodexAgent'\n\n/** Tool name for the model's collab-agent calls (`wait`, `sendInput`, …), the\n * `tool` field carried in the input. One name for the whole open axis rather\n * than a name per verb, so a future verb renders instead of vanishing. */\nexport const CODEX_COLLAB_TOOL = 'CodexCollab'\n\n/** An agent's name is its path's basename: '/root/date_one' → 'date_one'. */\nfunction agentName(agentPath: string | null | undefined): string | undefined {\n if (typeof agentPath !== 'string') return undefined\n const name = agentPath.split('/').filter(Boolean).at(-1)\n return name || undefined\n}\n\n/** The collab card's input: the verb always, the rich fields only when codex\n * actually filled them (measured against 0.146.0 they arrive empty — the card\n * must not render five null columns to say 'wait'). */\nfunction collabInput(item: AppServerCollabAgentToolCallItem): Record<string, unknown> {\n return {\n tool: item.tool,\n ...(item.receiverThreadIds?.length ? { receiverThreadIds: item.receiverThreadIds } : {}),\n ...(item.prompt ? { prompt: item.prompt } : {}),\n ...(item.model ? { model: item.model } : {}),\n }\n}\n\n/** A completed turn's answer, from its summary `items` page — the last\n * `agentMessage` text. For a sub-agent's thread this is the agent's report,\n * which is exactly what belongs in the anchor's `tool_result`. */\nfunction turnReport(turn: AppServerTurn): string | undefined {\n const items = Array.isArray(turn.items) ? turn.items : []\n for (let index = items.length - 1; index >= 0; index--) {\n const item = items[index]\n if (item?.type === 'agentMessage' && typeof item.text === 'string' && item.text) {\n return item.text\n }\n }\n return undefined\n}\n\n/** Longest `result` worth putting in a tool card. The field is free-form and\n * undocumented; anything past this is assumed to be an encoded image rather\n * than a sentence, and encoded images do not go in the event log. */\nconst MAX_IMAGE_RESULT_CHARS = 512\n\nconst shortResult = (result: string): boolean =>\n result.length > 0 && result.length <= MAX_IMAGE_RESULT_CHARS && !result.startsWith('data:')\n\n/**\n * `file_produced.fileId` — derived from the path, not minted fresh.\n *\n * Two properties fall out of that and both are load-bearing: codex reports the\n * same `savedPath` on the progress item and again on the completed one, so a\n * derived id makes the second emission a no-op instead of a duplicate row; and\n * a session rebuilt from a snapshot re-derives the same ids, so a client's\n * cached URL still resolves after a park/restore.\n */\nfunction producedFileId(path: string): string {\n return createHash('sha256').update(path).digest('hex').slice(0, 32)\n}\n\n/** Media type from the extension, for the handful a client renders inline.\n * Undefined for everything else — the route sniffs, and guessing here is how a\n * text file ends up labelled `image/png`. */\nfunction producedMediaType(path: string): string | undefined {\n const extension = path.slice(path.lastIndexOf('.') + 1).toLowerCase()\n return PRODUCED_MEDIA_TYPES[extension]\n}\n\nconst PRODUCED_MEDIA_TYPES: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n pdf: 'application/pdf',\n}\n\n/**\n * Codex's `SkillMetadata` as the protocol states it. `interface.shortDescription`\n * beats the legacy top-level one (codex's own comment says to prefer it), and\n * `enabled` defaults to true — an entry codex listed without the field is one it\n * considers live, and defaulting to false would hide working skills.\n */\nfunction skillInfo(skill: AppServerSkillMetadata): SkillInfo {\n return {\n name: skill.name,\n ...(skill.description ? { description: skill.description } : {}),\n ...(skill.interface?.shortDescription ?? skill.shortDescription\n ? { shortDescription: skill.interface?.shortDescription ?? skill.shortDescription }\n : {}),\n ...(skill.interface?.displayName ? { displayName: skill.interface.displayName } : {}),\n ...(skill.interface?.defaultPrompt ? { defaultPrompt: skill.interface.defaultPrompt } : {}),\n ...(skill.scope ? { scope: skill.scope } : {}),\n enabled: skill.enabled !== false,\n }\n}\n\n/**\n * Codex's MCP status → the protocol's, which is Claude Code's vocabulary\n * ('connected' | 'failed' | 'needs-auth' | 'pending' | 'disabled').\n *\n * Two inputs, and the auth one wins where it applies: a server that started\n * fine but has no credential is *needs-auth*, not connected, because that is\n * the thing the operator has to act on. `notLoggedIn` is the only auth value\n * that means \"unusable\" — `unsupported` is the normal answer for a stdio server\n * that has no auth concept at all.\n *\n * A server with no startup notification yet is 'pending', not 'connected':\n * `mcpServerStatus/list` alone only proves it is *configured*.\n */\nfunction mcpStatusOf(\n authStatus: string | undefined,\n update: { status: string; failureReason?: string } | undefined,\n hasTools: boolean,\n): string {\n if (update?.status === 'failed') {\n return update.failureReason === 'reauthenticationRequired' ? 'needs-auth' : 'failed'\n }\n // codex's 'cancelled' has no Claude equivalent; it means the startup was\n // abandoned, which for a reader is the same actionable state as failed.\n if (update?.status === 'cancelled') return 'failed'\n if (authStatus === 'notLoggedIn') return 'needs-auth'\n if (update?.status === 'ready') return 'connected'\n // **Tools imply connected, and this branch is not a nicety.** The startup\n // notifications only fire for servers that come up *while we are attached*;\n // a session whose child already had its servers running receives none at all\n // (measured against the real binary — a working server with three tools and\n // no notification). Tools can only have been enumerated over a completed\n // handshake, so their presence is direct evidence the server is up, and\n // without this a healthy server would read as 'pending' forever.\n if (hasTools) return 'connected'\n // No notification and nothing exposed. Genuinely ambiguous: not started yet,\n // or switched off in config — and `mcpServerStatus/list` cannot tell the two\n // apart (it lists disabled servers too, also toolless). 'pending' is the\n // honest one of the two; claiming 'disabled' would be a guess.\n return 'pending'\n}\n\n/** One `mcpServerStatus/list` entry as the protocol states it. */\nfunction mcpServerInfo(\n server: AppServerMcpServerStatus,\n update: { status: string; error?: string; failureReason?: string } | undefined,\n): McpServerStatusInfo {\n // A map keyed by tool name, not an array — and the key is authoritative when\n // the value omits its own `name`.\n const tools = Object.entries(server.tools ?? {}).flatMap(([key, tool]) => {\n if (!tool) return []\n const annotations = tool.annotations\n return [\n {\n name: tool.name ?? key,\n ...(tool.description ? { description: tool.description } : {}),\n ...(tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {}),\n ...(annotations\n ? {\n annotations: {\n ...(annotations.readOnlyHint != null ? { readOnly: annotations.readOnlyHint } : {}),\n ...(annotations.destructiveHint != null\n ? { destructive: annotations.destructiveHint }\n : {}),\n ...(annotations.openWorldHint != null\n ? { openWorld: annotations.openWorldHint }\n : {}),\n },\n }\n : {}),\n },\n ]\n })\n return {\n name: server.name,\n status: mcpStatusOf(server.authStatus ?? undefined, update, tools.length > 0),\n ...(update?.error ? { error: update.error } : {}),\n ...(server.serverInfo?.name\n ? { serverInfo: { name: server.serverInfo.name, version: server.serverInfo.version ?? '' } }\n : {}),\n // Deliberately no `transport`/`command`/`args`/`url`: the list response\n // carries none of them. Inventing a transport from the server's name would\n // be a guess rendered as a fact, and the panel already omits what is absent.\n ...(tools.length > 0 ? { tools } : {}),\n }\n}\n\n/** What the card shows while the picture is being made, and after. `savedPath`\n * only exists once it lands — a client keys its preview off it, so it is a\n * field rather than a sentence in the result text. */\nfunction imageGenerationInput(item: AppServerImageGenerationItem): Record<string, unknown> {\n return {\n ...(item.revisedPrompt ? { prompt: item.revisedPrompt } : {}),\n ...(item.savedPath ? { savedPath: item.savedPath } : {}),\n }\n}\n\n/**\n * The experimental per-request decision list, normalized to names: a string\n * entry is its own name, a structured entry (`{acceptWithExecpolicyAmendment:\n * …}`) is named by its key. Undefined = the request stated no list and the\n * channel's schema enum applies. Present only under `experimentalApi: true` —\n * which WorkerDeck always declares.\n */\nfunction offeredDecisions(params: unknown): Set<string> | undefined {\n const raw = (params as { availableDecisions?: unknown })?.availableDecisions\n if (!Array.isArray(raw)) return undefined\n const names = new Set<string>()\n for (const entry of raw) {\n if (typeof entry === 'string') names.add(entry)\n else if (entry && typeof entry === 'object') {\n for (const key of Object.keys(entry)) names.add(key)\n }\n }\n return names.size > 0 ? names : undefined\n}\n\n/**\n * Decision picking for the `{decision: …}` channels (commandExecution,\n * fileChange), honoring the request's own `availableDecisions`:\n *\n * - allow → 'accept' when offered (or when no list was stated). A request\n * offering only the broader accepts ('acceptForSession',\n * 'acceptWithExecpolicyAmendment') yields undefined: a one-shot allow must\n * not be silently widened into a session-wide or persistent policy grant, so\n * the caller answers with the denial and says why.\n * - deny → 'decline', always: the response schema declares it unconditionally,\n * and it was verified live against 0.146.0 answering a request whose\n * availableDecisions omitted it — the turn completed cleanly. The list's job\n * is to gate the accept variants, not to take \"no, but keep going\" away\n * (its own alternative, 'cancel', would interrupt the whole turn).\n * - deny+interrupt → 'cancel' (codex's deny-and-interrupt) when offered;\n * otherwise 'decline', and the caller interrupts the turn itself.\n */\nfunction pickDecision(\n behavior: 'allow' | 'deny',\n interrupt: boolean,\n offered: Set<string> | undefined,\n): string | undefined {\n const has = (name: string) => !offered || offered.has(name)\n if (behavior === 'allow') return has('accept') ? 'accept' : undefined\n if (interrupt && has('cancel')) return 'cancel'\n return 'decline'\n}\n\n/** Codex `requestUserInput` questions in the AskUserQuestion wire shape both\n * clients already render (QuestionPrompt / QuestionPromptView). */\nfunction userQuestionsFromCodex(questions: readonly AppServerUserInputQuestion[]): UserQuestion[] {\n return questions.map((question) => ({\n question: question.question,\n header: question.header ?? '',\n options: (question.options ?? []).map((option) => ({\n label: option.label,\n description: option.description,\n })),\n }))\n}\n\n/**\n * The text of a history `userMessage` item: its content entries' text parts\n * joined.\n *\n * Image parts have no replayable representation — the bytes went to the model,\n * not into the rollout we can render from — so they are named rather than\n * dropped. A prompt that was *only* an image used to produce an empty string,\n * which the caller read as \"nothing to replay\" and skipped: the turn lost its\n * user row and, with it, the prompt mark the scrubber navigates by, so a resumed\n * thread had answers with no visible question. A word in place of the picture is\n * a smaller lie than a turn that never happened.\n */\nfunction historyUserText(item: AppServerUserMessageItem): string {\n if (!Array.isArray(item.content)) return ''\n let images = 0\n const text = item.content\n .map((part) => {\n const candidate = part as { type?: string; text?: unknown } | null\n if (candidate?.type === 'text' && typeof candidate.text === 'string') return candidate.text\n // By name, because the part vocabulary is codex's and open ('image',\n // 'localImage', …). Anything else unnamed stays unrepresented rather than\n // counted as a picture it may not be.\n if (typeof candidate?.type === 'string' && candidate.type.toLowerCase().includes('image')) {\n images += 1\n }\n return ''\n })\n .filter(Boolean)\n .join('\\n')\n if (text) return text\n return images > 0 ? `[${images === 1 ? 'image' : `${images} images`}]` : ''\n}\n\n/** The AskUserQuestion answer convention (question text → chosen label(s),\n * comma-joined) mapped back to codex's id-keyed shape. Questions the client\n * did not answer are absent, not empty. */\nfunction codexAnswers(\n questions: readonly AppServerUserInputQuestion[],\n answers: Record<string, unknown> | undefined,\n): Record<string, { answers: string[] }> {\n const out: Record<string, { answers: string[] }> = {}\n for (const question of questions) {\n const value = answers?.[question.question] ?? answers?.[question.id]\n if (typeof value === 'string' && value.length > 0) out[question.id] = { answers: [value] }\n }\n return out\n}\n\ntype ApprovalSurface = Pick<\n PermissionRequest,\n 'toolName' | 'input' | 'title' | 'displayName' | 'description' | 'decisionReason'\n>\n\n/**\n * One server→client ask channel: how it surfaces as a {@link PermissionRequest}\n * and what its wire responses are. `allow` may return undefined — the request\n * offered no plain accept — in which case the caller answers with `deny` and\n * says so. `decision` names the wire decision when the channel has one, so the\n * caller knows whether a deny+interrupt still needs an explicit\n * `turn/interrupt` ('cancel' carries the interrupt itself).\n */\ntype ApprovalChannel = {\n describe(params: unknown): ApprovalSurface\n itemId(params: unknown): string | undefined\n allow(\n params: unknown,\n updatedInput: Record<string, unknown> | undefined,\n offered: Set<string> | undefined,\n ): { response: unknown; decision?: string } | undefined\n deny(\n params: unknown,\n interrupt: boolean,\n offered: Set<string> | undefined,\n ): { response: unknown; decision?: string }\n}\n\n/** The two channels whose response is `{decision: …}` share their pick logic. */\nfunction decisionChannel(\n describe: (params: unknown) => ApprovalSurface,\n itemId: (params: unknown) => string | undefined,\n): ApprovalChannel {\n return {\n describe,\n itemId,\n allow: (_params, _updatedInput, offered) => {\n const decision = pickDecision('allow', false, offered)\n return decision ? { response: { decision }, decision } : undefined\n },\n deny: (_params, interrupt, offered) => {\n const decision = pickDecision('deny', interrupt, offered)!\n return { response: { decision }, decision }\n },\n }\n}\n\n/**\n * The ask channels, wired to the permission surface. Anything not listed here\n * still gets a JSON-RPC -32601 — never a hang (an unanswered server request\n * wedges the turn).\n */\nconst APPROVAL_CHANNELS: Record<string, ApprovalChannel> = {\n 'item/commandExecution/requestApproval': decisionChannel(\n (raw) => {\n const params = raw as AppServerCommandApprovalParams\n const command = params.command ?? undefined\n return {\n toolName: 'CodexCommand',\n input: {\n ...(command !== undefined ? { command } : {}),\n ...(params.cwd ? { cwd: params.cwd } : {}),\n ...(params.reason ? { reason: params.reason } : {}),\n },\n // Codex's own sentence is the truth of what is being asked: for a\n // sandbox escalation it reads \"command failed; retry without sandbox?\"\n // — an after-the-refusal question, NOT a pre-execution gate — and the\n // clients render `title` verbatim, so the tense stays honest.\n title:\n params.reason ??\n (command ? `Codex wants to run: ${command}` : 'Codex wants to run a command'),\n displayName: 'Run command',\n description: params.reason && command ? command : (params.cwd ?? undefined),\n decisionReason: params.reason ?? undefined,\n }\n },\n (raw) => (raw as AppServerCommandApprovalParams).itemId,\n ),\n 'item/fileChange/requestApproval': decisionChannel(\n (raw) => {\n const params = raw as AppServerFileChangeApprovalParams\n return {\n toolName: 'CodexFileChange',\n input: {\n ...(params.grantRoot ? { grantRoot: params.grantRoot } : {}),\n ...(params.reason ? { reason: params.reason } : {}),\n },\n title: params.reason ?? 'Codex wants to apply file changes',\n displayName: 'Apply file changes',\n description: params.grantRoot ? `write access under ${params.grantRoot}` : undefined,\n decisionReason: params.reason ?? undefined,\n }\n },\n (raw) => (raw as AppServerFileChangeApprovalParams).itemId,\n ),\n 'item/permissions/requestApproval': {\n describe: (raw) => {\n const params = raw as AppServerPermissionsApprovalParams\n return {\n toolName: 'CodexPermissions',\n input: {\n ...(params.permissions ? { permissions: params.permissions } : {}),\n ...(params.cwd ? { cwd: params.cwd } : {}),\n ...(params.reason ? { reason: params.reason } : {}),\n },\n title: params.reason ?? 'Codex requests additional permissions',\n displayName: 'Grant permissions',\n description: undefined,\n decisionReason: params.reason ?? undefined,\n }\n },\n itemId: (raw) => (raw as AppServerPermissionsApprovalParams).itemId,\n // Allow grants exactly what was asked (or the client's narrowed rewrite via\n // `updatedInput.permissions`), scoped to the turn — the response's default\n // scope, never 'session'.\n allow: (raw, updatedInput) => ({\n response: {\n permissions:\n (updatedInput?.permissions as Record<string, unknown> | undefined) ??\n (raw as AppServerPermissionsApprovalParams).permissions ??\n {},\n },\n }),\n // This channel's \"no\" is an empty grant.\n deny: () => ({ response: { permissions: {} } }),\n },\n 'item/tool/requestUserInput': {\n describe: (raw) => ({\n toolName: 'AskUserQuestion',\n input: {\n questions: userQuestionsFromCodex((raw as AppServerUserInputParams).questions ?? []),\n },\n title: 'Codex asks a question',\n displayName: 'Answer questions',\n description: undefined,\n decisionReason: undefined,\n }),\n itemId: (raw) => (raw as AppServerUserInputParams).itemId,\n allow: (raw, updatedInput) => ({\n response: {\n answers: codexAnswers(\n (raw as AppServerUserInputParams).questions ?? [],\n updatedInput?.answers as Record<string, unknown> | undefined,\n ),\n },\n }),\n deny: () => ({ response: { answers: {} } }),\n },\n 'mcpServer/elicitation/request': {\n describe: (raw) => {\n const params = raw as AppServerElicitationParams\n return {\n toolName: 'CodexMcpElicitation',\n input: {\n ...(params.serverName ? { serverName: params.serverName } : {}),\n ...(params.message ? { message: params.message } : {}),\n ...(params.mode ? { mode: params.mode } : {}),\n ...(params.requestedSchema !== undefined\n ? { requestedSchema: params.requestedSchema }\n : {}),\n ...(params.url ? { url: params.url } : {}),\n },\n title: params.serverName\n ? `MCP server '${params.serverName}' requests input`\n : 'An MCP server requests input',\n displayName: 'MCP elicitation',\n description: params.message ?? undefined,\n decisionReason: undefined,\n }\n },\n itemId: () => undefined,\n // An allow's `updatedInput` IS the elicitation content (the filled form);\n // content is nullable in the schema, so an allow without one is an accept\n // with no content and the MCP server judges it.\n allow: (_raw, updatedInput) => ({\n response: {\n action: 'accept',\n ...(updatedInput !== undefined ? { content: updatedInput } : {}),\n },\n }),\n // 'cancel' here cancels the ELICITATION, not the codex turn — no\n // `decision` is reported, so a deny+interrupt still interrupts the turn\n // explicitly.\n deny: (_raw, interrupt) => ({ response: { action: interrupt ? 'cancel' : 'decline' } }),\n },\n}\n\n/** One pending server→client approval: the surfaced request, the channel that\n * knows its wire vocabulary, and the resolver that answers the JSON-RPC\n * request when a decision lands. */\ntype PendingCodexApproval = {\n request: PermissionRequest\n channel: ApprovalChannel\n params: unknown\n offered: Set<string> | undefined\n /** JSON-RPC wire id — `serverRequest/resolved` names it when codex settles\n * the request itself. */\n wireId: string | number | undefined\n timer: ReturnType<typeof setTimeout>\n respond: (response: unknown) => void\n}\n\nexport type CodexRunnerConfig = CreateSessionRequest & {\n /** The injectable connection factory. The codex adapter passes\n * `connectAppServer` under the resolved binary; unit tests pass a scripted\n * peer. Required — this class never spawns anything itself. */\n connectFn: AppServerConnectFn\n /** Base environment for the codex child. Defaults to process.env. Passed to\n * spawn **complete** — a child env replaces, never merges. */\n env?: Record<string, string | undefined>\n /** CODEX_HOME pin from the profile (auth, config.toml, thread storage). */\n codexHome?: string\n /** Timeout for pending approvals when the request itself doesn't set one.\n * Default 300000 — the SessionRunner default. */\n defaultApprovalTimeoutMs?: number\n /** With `resume`: replay the thread's prior turns as `replay: true` events\n * before anything else, so late-attaching clients get a full transcript —\n * the SessionRunner option, same name, same default (true). */\n backfillHistory?: boolean\n}\n\n/** One queued user message: the input for exactly one turn. */\ntype QueuedTurn = { input: AppServerUserInput[] }\n\n/**\n * Name a subscription window by its measured length, so codex's positional\n * windows land in the protocol's named vocabulary. The two names clients\n * already understand are exact matches for codex's durations (300 min = 5h,\n * 10080 min = 7d); anything else keeps a self-describing key rather than\n * borrowing a name that would size it wrongly.\n */\nfunction rateLimitWindowName(minutes: number | null | undefined): string | undefined {\n if (typeof minutes !== 'number' || !Number.isFinite(minutes) || minutes <= 0) return undefined\n if (minutes === 300) return 'five_hour'\n if (minutes === 10_080) return 'seven_day'\n return `window_${minutes}m`\n}\n\n/** Everything one in-flight turn accumulates between `turn/start` and its\n * terminal `turn/completed`. */\ntype ActiveTurn = {\n /** Per-turn namespace for item-derived ids, kept unconditionally (the retired\n * exec transport's id-collision bug, b026e70): app-server item-id uniqueness\n * across turns (and across a respawned child) is not something we rely on. */\n nonce: string\n turnId?: string\n interrupted: boolean\n finalText?: string\n /** Last `error` notification, explaining a turn that fails without a message. */\n lastError?: string\n usage: AppServerTokenUsage\n sawUsage: boolean\n /** Context occupancy from the most recent model request, with the window it\n * was measured against. NOT `total` — see {@link CodexRunner.emitContextUsage}. */\n contextTokens?: number\n contextWindow?: number\n toolUseEmitted: Set<string>\n /** Last seen reasoning section index per item+kind, for '\\n\\n' separators. */\n sectionIndex: Map<string, number>\n settled: boolean\n resolve: (outcome: AppServerTurn) => void\n reject: (error: Error) => void\n}\n\n/**\n * The Codex engine, over the binary's `app-server` JSON-RPC surface: ONE\n * `codex app-server` child per *session* (spawned lazily, held across turns),\n * streaming `item/agentMessage/delta` and the reasoning deltas token-by-token\n * (`streaming: 'token'`). Follows `SessionRunner`'s event-log/seq/status\n * discipline with `AiSdkRunner`'s turn-chain (one turn at a time; sendMessage\n * queues). The first codex transport was `codex exec --experimental-json` (one\n * child per turn) — retired because its JSONL carries no partial messages, so\n * a turn could never stream.\n *\n * A dead child is a failed *turn*, not a failed session: the thread persists\n * on disk, the connection is dropped, and the next message spawns a fresh\n * child that `thread/resume`s the same thread id.\n */\nexport class CodexRunner implements Runner {\n readonly id: string\n readonly createdAt: number\n\n #config: CodexRunnerConfig\n /** {@link CodexRunnerConfig.cwd}, checked once in the constructor. */\n readonly #cwd: string\n #events: SessionEvent[] = []\n #subscribers = new SubscriberSet()\n #seq = 0\n /**\n * Latest context-window reading, retained from the last `context_usage` this\n * runner emitted so `GET /sessions` can answer it without an attach — see\n * `SessionInfo.contextUsage`. Folded in the emit path, so it is by\n * construction the same number the transcript last drew.\n */\n #contextUsage: ContextReading | undefined\n #activityCount = 0\n #status: SessionStatus = 'starting'\n #sdkSessionId: string | undefined\n #model: string | undefined\n #permissionMode: PermissionMode\n #reasoningEffort: string | undefined\n /** What the binary said the profile's defaults resolve to (thread/start\n * response) — lets `setModel(undefined)` mean \"back to the default\" even\n * though a turn/start override persists for subsequent turns. */\n #resolvedModel: string | undefined\n /** Last reported ChatGPT plan, so `plan_info` is emitted once per change. */\n #planType: string | undefined\n #resolvedEffort: string | undefined\n #queue: QueuedTurn[] = []\n #turnChain: Promise<void> = Promise.resolve()\n #activeTurn: ActiveTurn | undefined\n #connection: AppServerConnection | undefined\n /** Per-child, from `config/read`; undefined = read failed, send the bare shape. */\n #workspaceWrite: CodexWorkspaceWrite | undefined\n #threadLoaded = false\n #numTurns = 0\n #totalCostUsd: number | undefined\n #lastActivityAt: number | undefined\n #started = false\n #closed = false\n /** Session temp dir for image attachments (`localImage` takes host paths). */\n #imageDir: string | undefined\n /** Pending server→client approvals, keyed by the surfaced request id. */\n #approvals = new Map<string, PendingCodexApproval>()\n /** True from start() until the resume backfill (the turn chain's first link)\n * settles — while set, sendMessage defers its user_message echo behind the\n * chain so a new turn can never precede or interleave the replayed history. */\n #backfillPending = false\n /** The resumed thread's prior turns, stashed by {@link #ensureThread} from\n * the ONE thread/resume the backfill consumes (`partial` = the response's\n * turnsBackwardsCursor said older turns exist beyond this page). A mid-life\n * reconnect also goes through thread/resume, but with no backfill pending\n * nothing is stashed — history is never replayed twice. */\n #resumedHistory: { turns: AppServerHistoryTurn[]; partial: boolean } | undefined\n /** Set around history replay: {@link #emit} stamps `replay: true` onto the\n * message events the live item mapping produces. */\n #replayingHistory = false\n /** Last `skills` payload emitted, serialized — the comparison that keeps a\n * `skills/changed` storm (the watcher fires per touched file) from filling\n * the event log with identical lists. */\n #skillsFingerprint: string | undefined\n /** In-flight `skills/list`, so a burst of `skills/changed` makes one call.\n * The pending promise is reused rather than queued: the request has no\n * arguments, so a second one would ask the same question. */\n #skillsRefresh: Promise<void> | undefined\n /** Host paths already announced via `file_produced`, so the same picture\n * reported on both the progress and the completed item registers once. */\n #producedPaths = new Set<string>()\n /** Per-server liveness, accumulated from `mcpServer/startupStatus/updated`.\n * `mcpServerStatus/list` does not carry a status field at all, so without\n * this every server would read as \"configured\" and never as up or down. */\n #mcpStatus = new Map<string, { status: string; error?: string; failureReason?: string }>()\n /** The spawned agents, keyed by their thread ids — the attribution table\n * behind `parentToolUseId` and the rollup behind `info().subagents`. Runner-\n * level, not per-turn: an agent's thread outlives the root turn that spawned\n * it, and only the child process dying (or the session closing) ends them\n * all — see the module doc in `subagents.ts`. */\n #agents = new CodexAgentTracker()\n\n constructor(config: CodexRunnerConfig, id: string = randomUUID()) {\n const mode = config.permissionMode ?? 'default'\n if (!ENGINE_CAPABILITIES.codex.permissionModes.includes(mode)) {\n throw new Error(`permission mode '${mode}' is not supported by the codex engine`)\n }\n if (config.forkSession) {\n throw new Error('the codex engine cannot fork a resumed thread')\n }\n // Optional on the wire, required here — the codex binary runs in a real\n // directory (see the same check in `SessionRunner`).\n if (!config.cwd) throw new Error('the codex engine requires a cwd')\n this.#cwd = config.cwd\n this.#config = config\n this.#permissionMode = mode\n this.#model = config.model\n this.#reasoningEffort = config.reasoningEffort\n this.#sdkSessionId = config.resume\n this.id = id\n this.createdAt = Date.now()\n }\n\n /** The complete child environment — spawn env replaces process.env wholesale,\n * so this must carry everything a shell would, with the profile's CODEX_HOME\n * pin winning over operator env. */\n #childEnv(): Record<string, string> {\n const base = this.#config.env ?? process.env\n const env: Record<string, string> = {}\n for (const [key, value] of Object.entries(base)) {\n if (value !== undefined) env[key] = value\n }\n if (this.#config.codexHome) env.CODEX_HOME = this.#config.codexHome\n return env\n }\n\n get status(): SessionStatus {\n return this.#status\n }\n\n get sdkSessionId(): string | undefined {\n return this.#sdkSessionId\n }\n\n get lastSeq(): number {\n return this.#seq\n }\n\n get pendingApprovals(): PermissionRequest[] {\n return [...this.#approvals.values()].map((pending) => pending.request)\n }\n\n info(): SessionInfo {\n return {\n id: this.id,\n sdkSessionId: this.#sdkSessionId,\n status: this.#status,\n cwd: this.#cwd,\n profile: this.#config.profile,\n engine: 'codex',\n capabilities: ENGINE_CAPABILITIES.codex,\n model: this.#model ?? this.#resolvedModel,\n permissionMode: this.#permissionMode,\n canBypassPermissions: true,\n createdAt: this.createdAt,\n lastSeq: this.#seq,\n activityCount: this.#activityCount,\n contextUsage: this.#contextUsage,\n pendingPermissionCount: this.#approvals.size,\n meta: this.#config.meta,\n scope: this.#config.scope,\n title: this.#title(),\n totalCostUsd: this.#totalCostUsd,\n numTurns: this.#numTurns || undefined,\n lastActivityAt: this.#lastActivityAt,\n subagents: this.#agents.list(),\n }\n }\n\n #title(): string | undefined {\n const metaTitle = this.#config.meta?.title\n if (typeof metaTitle === 'string' && metaTitle.length > 0) return metaTitle\n const prompt = this.#config.prompt\n if (!prompt) return undefined\n return prompt.length > 80 ? prompt.slice(0, 77) + '…' : prompt\n }\n\n /** Host-facing rename: writes `meta.title`, which `#title()` prefers. Clearing\n * it (undefined) restores the derived title. The engine is never told. */\n setTitle(title: string | undefined): void {\n const meta = { ...this.#config.meta }\n if (title) meta.title = title\n else delete meta.title\n this.#config = { ...this.#config, meta }\n }\n\n start(): Promise<void> {\n if (this.#started) return this.#turnChain\n this.#started = true\n this.#warnUntrustedProject()\n if (this.#config.resume && this.#config.backfillHistory !== false) {\n // First link of the turn chain: connect, thread/resume, and replay the\n // thread's prior turns as `replay: true` events before any queued turn\n // runs (and before its echo — see sendMessage). This is also why a\n // promptless resume now connects eagerly rather than on first message:\n // its history is the whole point of attaching to it.\n this.#backfillPending = true\n this.#turnChain = this.#turnChain.then(() => this.#backfillHistory())\n } else {\n this.#setStatus('idle')\n }\n if (this.#config.prompt) this.sendMessage(this.#config.prompt)\n // A session that is about to connect anyway (a prompt to run, or a resume\n // to backfill) gets its skills from that connection a moment later. Only\n // the promptless, non-resume case — the dashboard's \"create, then type\" —\n // would otherwise sit with no child and therefore no skill list at all,\n // which is the one place codex's own TUI has them and we did not.\n if (!this.#config.prompt && !this.#config.resume) void this.#probeSkills()\n return this.#turnChain\n }\n\n /**\n * One-time transcript notice for the codex trust gap: a `default`-mode\n * session (read-only sandbox) on an untrusted cwd has its\n * `.codex/config.toml` — MCP servers included — silently ignored, and the\n * app-server surface has no trust prompt to say so (the TUI's prompt is\n * where the entry normally gets written). `acceptEdits`/`bypassPermissions`\n * sessions are exempt because their `thread/start` (workspace-write /\n * danger-full-access sandbox) writes the trust entry itself and loads the\n * config — measured against 0.146.0 and 0.149.0; a notice there would be\n * false. Emitted as `session_error`, which both clients render as an inline\n * notice while the session keeps running (the backfill-history precedent),\n * so nothing new rides the wire. Every degrade path is silence: a false\n * warning on a trusted project is worse than a missed one.\n */\n #warnUntrustedProject(): void {\n if (this.#permissionMode !== 'default') return\n try {\n const env = this.#childEnv()\n // Mirror the child's own home resolution: the profile pin already won\n // inside #childEnv, then the session env's CODEX_HOME, then ~/.codex\n // under the env's HOME (codex reads $HOME, not the process owner's).\n const pin = env.CODEX_HOME\n if (pin !== undefined && pin.length === 0) return\n const codexHome = pin ?? join(env.HOME ?? homedir(), '.codex')\n const message = untrustedProjectNotice({ cwd: this.#cwd, codexHome })\n if (message) this.#emit({ type: 'session_error', message })\n } catch {\n // Silence, deliberately — a failed probe must neither warn nor break\n // the start.\n }\n }\n\n /**\n * List skills over a **throwaway** connection, for a session with nothing else\n * to do yet.\n *\n * `skills/list` needs a live child but not a thread, so this spawns one, asks,\n * and closes it — rather than bringing up the session's own child early and\n * leaving a codex process parked behind every session someone created and\n * never typed into. The session's real connection re-lists when it arrives;\n * the fingerprint compare in {@link #refreshSkills} makes that a no-op.\n *\n * Entirely best-effort and never awaited: a missing binary, a failed spawn or\n * a rejected handshake here must not turn a session that has not started into\n * a session that failed.\n */\n async #probeSkills(): Promise<void> {\n let connection: AppServerConnection | undefined\n try {\n connection = await this.#openScratchConnection()\n if (this.#closed) return\n await this.#refreshSkills(connection)\n } catch {\n // The session is fine; it simply has no skill list until its own child\n // comes up and asks again.\n } finally {\n connection?.close()\n }\n }\n\n /**\n * A handshaken child that is **not** the session's — for the questions a\n * client can ask before the session has anything to run (its skills, its MCP\n * servers). The caller owns it and must close it.\n *\n * No onNotification/onRequest/onClose wiring on purpose: this child answers\n * one question and goes away, so its notifications are noise and its death is\n * not the session's problem. The alternative — bringing the session's real\n * child up early — would park a codex process behind every session someone\n * created and never typed into.\n */\n async #openScratchConnection(): Promise<AppServerConnection> {\n const connection = this.#config.connectFn({ env: this.#childEnv() })\n try {\n await connection.request('initialize', {\n clientInfo: {\n name: 'workerdeck',\n title: 'WorkerDeck',\n version: `protocol-${PROTOCOL_VERSION}`,\n },\n capabilities: { experimentalApi: true },\n })\n connection.notify('initialized')\n return connection\n } catch (error) {\n connection.close()\n throw error\n }\n }\n\n sendMessage(text: string, attachments?: readonly AttachmentInput[]): void {\n if (this.#closed) throw new Error('session is closed')\n const input = this.#buildInput(text, attachments ?? [])\n const echo = () =>\n this.#emit({\n type: 'user_message',\n message: { role: 'user', content: text },\n parentToolUseId: null,\n attachments: attachments?.length ? attachments.map(attachmentRef) : undefined,\n uuid: randomUUID(),\n })\n // While a resume's history replay is still pending, the echo rides the\n // turn chain (which the replay heads), so the new turn's user message can\n // never precede the history it follows. Otherwise it is immediate — a\n // message queued behind a running turn still echoes right away.\n if (this.#backfillPending) this.#turnChain = this.#turnChain.then(echo)\n else echo()\n this.#queue.push({ input })\n this.#scheduleTurn()\n }\n\n /**\n * App-server input for a message with attachments: images land in a session\n * temp dir and travel as `localImage` host paths, text files inline into the\n * prompt in the shared named envelope, PDF has no representation (the\n * gateway's 415 normally refuses it first).\n */\n #buildInput(text: string, attachments: readonly AttachmentInput[]): AppServerUserInput[] {\n const parts: AppServerUserInput[] = []\n for (const attachment of attachments) {\n const mediaType = normalizeMediaType(attachment.mediaType)\n switch (attachmentKind(mediaType)) {\n case 'image': {\n this.#imageDir ??= join(tmpdir(), `workerdeck-codex-${this.id}`)\n mkdirSync(this.#imageDir, { recursive: true })\n const ext = mediaType.split('/')[1] ?? 'bin'\n const path = join(this.#imageDir, `${attachment.id}.${ext}`)\n writeFileSync(path, Buffer.from(attachment.data, 'base64'))\n parts.push({ type: 'localImage', path })\n break\n }\n case 'text':\n parts.push({\n type: 'text',\n text:\n `<attachment name=\"${attachment.name}\" type=\"${mediaType}\">\\n` +\n `${Buffer.from(attachment.data, 'base64').toString('utf8')}\\n</attachment>`,\n })\n break\n default:\n throw new Error(\n `unsupported attachment media type for the codex engine: ${attachment.mediaType}`,\n )\n }\n }\n if (text) parts.push({ type: 'text', text })\n return parts\n }\n\n /** Resolve a pending approval. Returns false if the id is unknown (e.g.\n * timed out, or already settled by codex itself). */\n resolvePermission(requestId: string, decision: PermissionDecision): boolean {\n const pending = this.#approvals.get(requestId)\n if (!pending) return false\n this.#settleApproval(requestId, pending, decision, 'client')\n return true\n }\n\n async interrupt(): Promise<void> {\n // A pending approval is what's holding the turn open — settle each as a\n // denied interrupt first (codex's 'cancel' where the request offers it,\n // which itself ends the turn).\n for (const [id, pending] of this.#approvals) {\n this.#settleApproval(\n id,\n pending,\n { behavior: 'deny', message: 'interrupted', interrupt: true },\n 'policy',\n )\n }\n await this.#interruptTurn()\n await this.#turnChain\n }\n\n /** Address the in-flight turn only (no approval sweep) — also the follow-up\n * for a deny+interrupt whose wire decision couldn't carry the interrupt. */\n async #interruptTurn(): Promise<void> {\n const active = this.#activeTurn\n const connection = this.#connection\n if (active && !active.settled) {\n active.interrupted = true\n if (connection && active.turnId && this.#sdkSessionId) {\n try {\n await connection.request('turn/interrupt', {\n threadId: this.#sdkSessionId,\n turnId: active.turnId,\n })\n // The terminal turn/completed (status 'interrupted') settles the turn.\n } catch {\n // The turn may already be over, or the child gone — both settle it.\n }\n } else if (connection) {\n // No turn id yet (interrupted before turn/started): there is nothing\n // to address the request to, so end the child — the thread survives on\n // disk and the next message respawns into it.\n // The onClose rejection settles the turn; `interrupted` explains it.\n connection.close()\n if (this.#connection === connection) this.#connection = undefined\n active.reject(new Error('interrupted'))\n }\n }\n }\n\n async setPermissionMode(mode: PermissionMode): Promise<void> {\n if (!ENGINE_CAPABILITIES.codex.permissionModes.includes(mode)) {\n throw new Error(`permission mode '${mode}' is not supported by the codex engine`)\n }\n if (this.#activeTurn) {\n throw new Error(\"cannot change the permission mode mid-turn (the running turn's sandbox is fixed)\")\n }\n this.#permissionMode = mode\n this.#emit({ type: 'permission_mode_changed', mode })\n }\n\n async setModel(model?: string): Promise<void> {\n if (this.#activeTurn) {\n throw new Error(\"cannot change the model mid-turn (the running turn's model is fixed)\")\n }\n this.#model = model\n this.#emit({ type: 'model_changed', model })\n }\n\n fail(message: string): void {\n if (this.#closed) return\n this.#emit({ type: 'session_error', message })\n this.#setStatus('failed')\n this.close('error')\n }\n\n close(reason: 'client' | 'server' | 'error' = 'client'): void {\n if (this.#closed) return\n this.#closed = true\n this.#queue.length = 0\n // Settle pending approvals before the connection goes: each gets its\n // channel's own \"no\" on the wire and a permission_resolved in the log.\n for (const [id, pending] of this.#approvals) {\n this.#settleApproval(id, pending, { behavior: 'deny', message: 'Session closed' }, 'policy')\n }\n this.#connection?.close()\n this.#connection = undefined\n this.#agents.sweep()\n this.#activeTurn?.reject(new Error('session closed'))\n if (this.#imageDir) {\n try {\n rmSync(this.#imageDir, { recursive: true, force: true })\n } catch {\n // Temp-dir cleanup must never break teardown.\n }\n }\n this.#emit({ type: 'session_closed', reason })\n this.#setStatus('closed')\n }\n\n /** See `Runner.eventAt`. A linear scan: the one caller is a reader pressing\n * \"show everything\" on one row, so a per-runner seq index would be a map\n * maintained on every emit to save a walk nobody makes twice a minute. */\n eventAt(seq: number): SessionEvent | undefined {\n return this.#events.find((event) => event.seq === seq)\n }\n\n subscribe(\n listener: SessionEventListener,\n afterSeq = 0,\n options?: SubscribeOptions,\n ): () => void {\n return this.#subscribers.subscribe(this.#events, listener, afterSeq, options)\n }\n\n #scheduleTurn(): void {\n this.#turnChain = this.#turnChain.then(() => this.#runTurn())\n }\n\n /**\n * Read `[sandbox_workspace_write]` as codex resolves it for this session's\n * cwd, once per child, so {@link CodexRunner.#turnSandboxPolicy} can restate\n * it verbatim.\n *\n * Why this exists at all: `turn/start`'s object-form sandbox policy is\n * serde-defaulted field by field, so `{type: 'workspaceWrite'}` bare means\n * `networkAccess: false, writableRoots: []` NO MATTER what the operator\n * configured — and we must keep sending the object every turn, because\n * restating it is what makes a between-turns permission-mode switch take\n * effect. Measured against 0.149.0 with `network_access = true` set: the\n * bare object produced `curl: (6) Could not resolve host`, the fully-stated\n * object and an omitted policy both produced `200`. `read-only` is not\n * affected — the setting is scoped to workspace-write, as its name says, and\n * a read-only sandbox has no network either way.\n *\n * A failure here is not fatal: `#workspaceWrite` stays undefined and we send\n * the bare shape, which is exactly the behaviour that shipped before.\n */\n async #readWorkspaceWrite(connection: AppServerConnection): Promise<void> {\n this.#workspaceWrite = undefined\n try {\n const result = (await connection.request('config/read', { cwd: this.#cwd })) as {\n config?: { sandbox_workspace_write?: Record<string, unknown> | null } | null\n }\n const block = result?.config?.sandbox_workspace_write\n if (!block) return\n const roots = block.writable_roots\n this.#workspaceWrite = {\n writableRoots: Array.isArray(roots) ? roots.filter((r): r is string => typeof r === 'string') : [],\n networkAccess: block.network_access === true,\n excludeTmpdirEnvVar: block.exclude_tmpdir_env_var === true,\n excludeSlashTmp: block.exclude_slash_tmp === true,\n }\n } catch {\n // Older binary, or no readable config layer — the bare shape it is.\n }\n }\n\n /** The mode's turn-level sandbox policy, with the operator's workspace-write settings intact. */\n #turnSandboxPolicy(): { type: string } | undefined {\n const policy = TURN_SANDBOX_BY_MODE[this.#permissionMode]\n if (policy?.type !== 'workspaceWrite' || !this.#workspaceWrite) return policy\n return { type: 'workspaceWrite', ...this.#workspaceWrite }\n }\n\n /**\n * The session's live connection with its thread loaded, (re)building both as\n * needed: spawn + `initialize`/`initialized` on a fresh child, then\n * `thread/start` (new) or `thread/resume` (a create-request `resume`, or a\n * thread orphaned by a dead child). The response's resolved model/effort are\n * kept so per-turn overrides can name \"the profile default\" explicitly.\n */\n async #ensureThread(): Promise<AppServerConnection> {\n if (this.#closed) throw new Error('session is closed')\n let connection = this.#connection\n if (!connection) {\n connection = this.#config.connectFn({ env: this.#childEnv() })\n this.#connection = connection\n this.#threadLoaded = false\n connection.onNotification((method, params) => this.#handleNotification(method, params))\n connection.onRequest((method, params, id) => this.#answerServerRequest(method, params, id))\n connection.onClose((message) => {\n if (this.#connection === connection) {\n this.#connection = undefined\n this.#threadLoaded = false\n }\n // Approvals pending against a dead child can never be answered on the\n // wire — retire their cards and timers.\n for (const [id, pending] of this.#approvals) {\n this.#settleApproval(id, pending, { behavior: 'deny', message }, 'policy')\n }\n // Spawned agents lived in that process; their reports can never come.\n this.#agents.sweep()\n // A child dying mid-turn fails that turn (with the exit diagnostic);\n // idle, there is nothing to settle and the next turn respawns.\n this.#activeTurn?.reject(new Error(message))\n })\n try {\n // `experimentalApi` is load-bearing, not a nicety: granular approval\n // policies are rejected without it, and WorkerDeck ships ONE code path\n // (no string-policy fallback). A binary that rejects the capability\n // must fail loudly here — a session that quietly stops asking for\n // approvals is worse than one that refuses to start and says why.\n await connection.request('initialize', {\n clientInfo: {\n name: 'workerdeck',\n title: 'WorkerDeck',\n version: `protocol-${PROTOCOL_VERSION}`,\n },\n capabilities: { experimentalApi: true },\n })\n } catch (error) {\n // Don't leave a half-initialized child around — the next message must\n // respawn from scratch, not talk to a child that refused the handshake.\n connection.close()\n if (this.#connection === connection) this.#connection = undefined\n if (error instanceof JsonRpcError) {\n throw new Error(\n 'codex app-server rejected initialize (capabilities.experimentalApi: true — required ' +\n 'for the granular approval policy, and WorkerDeck has no non-experimental fallback): ' +\n error.message,\n )\n }\n throw error\n }\n connection.notify('initialized')\n await this.#readWorkspaceWrite(connection)\n }\n if (!this.#threadLoaded) {\n const options: Record<string, unknown> = {\n cwd: this.#cwd,\n approvalPolicy: APPROVAL_POLICY_BY_MODE[this.#permissionMode],\n sandbox: THREAD_SANDBOX_BY_MODE[this.#permissionMode],\n approvalsReviewer: APPROVALS_REVIEWER_BY_MODE[this.#permissionMode],\n }\n if (this.#model) options.model = this.#model\n const resuming = this.#sdkSessionId !== undefined\n const result = (resuming\n ? await connection.request('thread/resume', { threadId: this.#sdkSessionId, ...options })\n : await connection.request('thread/start', options)) as {\n thread?: { id?: string; turns?: AppServerHistoryTurn[] }\n model?: string | null\n reasoningEffort?: string | null\n /** Non-null: `thread.turns` is one PAGE and older turns exist beyond it. */\n turnsBackwardsCursor?: string | null\n }\n if (typeof result?.thread?.id === 'string') this.#sdkSessionId = result.thread.id\n if (typeof result?.model === 'string') this.#resolvedModel = result.model\n if (typeof result?.reasoningEffort === 'string') this.#resolvedEffort = result.reasoningEffort\n // The resume that backfill is waiting on carries the thread's prior\n // turns — stash them for it. A reconnect after a dead child resumes the\n // same thread but has no backfill pending, so nothing is stashed and\n // history is never replayed twice.\n if (resuming && this.#backfillPending && !this.#resumedHistory) {\n this.#resumedHistory = {\n turns: Array.isArray(result?.thread?.turns) ? result.thread.turns : [],\n partial: typeof result?.turnsBackwardsCursor === 'string',\n }\n }\n this.#threadLoaded = true\n }\n // Fire and forget, and only now: `skills/list` needs a live child, and a\n // codex session does not spawn one until it has something to do. So the\n // skill list arrives with the first turn rather than at create time —\n // which is why clients gate the affordance on having received a `skills`\n // event, not on the capability flag alone.\n void this.#refreshSkills(connection)\n return connection\n }\n\n /**\n * Re-read `skills/list` and publish it, if it changed.\n *\n * **`cwds` is passed explicitly, and must be.** The schema documents the empty\n * case as \"the current session working directory\", which reads like the\n * thread's — it is not. Measured against 0.146.0: with no `cwds`, and *after*\n * a `thread/start` carrying this session's cwd, the response comes back keyed\n * to the app-server child's own process directory (for WorkerDeck, wherever\n * the gateway was launched) and reports no repo-scoped skills at all. So a\n * project's own `.codex/skills/**` were invisible until this argument existed.\n *\n * Best-effort throughout. A binary too old to know the method, a broken\n * manifest, a child that died mid-call — none of that is worth failing a\n * session over, and the panel simply stays absent.\n */\n async #refreshSkills(connection: AppServerConnection): Promise<void> {\n if (this.#skillsRefresh) return this.#skillsRefresh\n const run = (async () => {\n try {\n const result = (await connection.request('skills/list', {\n cwds: [this.#cwd],\n })) as AppServerSkillsListResponse\n if (this.#closed) return\n const entries = Array.isArray(result?.data) ? result.data : []\n const seen = new Set<string>()\n const skills: SkillInfo[] = []\n for (const entry of entries) {\n for (const skill of entry?.skills ?? []) {\n // The same skill can be reported under several cwds; the first\n // wins, matching how codex itself resolves a name collision.\n if (typeof skill?.name !== 'string' || seen.has(skill.name)) continue\n seen.add(skill.name)\n skills.push(skillInfo(skill))\n }\n }\n skills.sort((a, b) => a.name.localeCompare(b.name))\n const fingerprint = JSON.stringify(skills)\n if (fingerprint === this.#skillsFingerprint) return\n this.#skillsFingerprint = fingerprint\n this.#emit({ type: 'skills', skills })\n } catch {\n // Nothing to say: an engine that cannot list its skills is an engine\n // whose skills panel does not appear.\n } finally {\n this.#skillsRefresh = undefined\n }\n })()\n this.#skillsRefresh = run\n return run\n }\n\n /**\n * The session's MCP servers, live from the binary.\n *\n * Two sources merged, because codex splits them: `mcpServerStatus/list` says\n * what is configured and what each server exposes (including every tool's\n * full JSON Schema, which the Agent SDK does not give us), and the\n * `mcpServer/startupStatus/updated` notifications say which of them are\n * actually up.\n *\n * Answers **before the session has connected**, over a throwaway child, for\n * the same reason the skill list does: a codex session spawns nothing until\n * it has work, and a panel that said \"no MCP servers configured\" until the\n * first turn would be stating something false about the operator's config.\n * The request blocks until the servers are enumerated (measured: complete on\n * the very first call), so there is no half-populated answer to race.\n *\n * Resolves undefined only when there is genuinely nothing to say — the\n * session is closed, or the child could not be spoken to. The route turns\n * that into a 501.\n *\n * **Listing only.** There is no per-server reconnect or toggle on this\n * transport — hence no `reconnectMcpServer`/`setMcpServerEnabled` here, and\n * `ENGINE_CAPABILITIES.codex.mcpServerActions: false` so clients render the\n * panel read-only instead of offering buttons that cannot work.\n */\n async mcpServers(): Promise<McpServerStatusInfo[] | undefined> {\n if (this.#closed) return undefined\n const live = this.#connection\n let scratch: AppServerConnection | undefined\n try {\n // The session's own child when it has one — its accumulated\n // `#mcpStatus` makes the answer sharper — and a throwaway otherwise.\n const connection = live ?? (scratch = await this.#openScratchConnection())\n const result = (await connection.request(\n 'mcpServerStatus/list',\n {},\n )) as AppServerMcpServerStatusResponse\n return (result?.data ?? []).map((server) =>\n mcpServerInfo(server, this.#mcpStatus.get(server.name)),\n )\n } catch {\n return undefined\n } finally {\n scratch?.close()\n }\n }\n\n /**\n * Announce a file the ENGINE wrote on the host, so a client can fetch it\n * without the operator having declared its directory as a host-file root.\n *\n * Deliberately narrow: only paths codex reports as *written by its own tool*\n * belong here. A path the model merely read (`imageView`) is an agent-chosen\n * claim, and those keep going through `/fs/*` and its root allowlist — see\n * the note on `file_produced` in the protocol.\n */\n #emitFileProduced(path: string, toolUseId: string): void {\n if (this.#producedPaths.has(path)) return\n this.#producedPaths.add(path)\n let bytes: number | undefined\n try {\n const stat = statSync(path)\n if (stat.isFile()) bytes = stat.size\n // A `savedPath` that is not a regular file is still announced: the route\n // re-checks before serving, and a client showing the path it was given\n // beats one silently dropping it.\n } catch {\n // Reported but not there (yet, or at all) — announce it anyway and let\n // the fetch be the thing that fails.\n }\n this.#emit({\n type: 'file_produced',\n fileId: producedFileId(path),\n path,\n ...(producedMediaType(path) ? { mediaType: producedMediaType(path) } : {}),\n ...(bytes !== undefined ? { bytes } : {}),\n toolUseId,\n })\n }\n\n /**\n * On resume, replay the thread's prior turns as `replay: true` events,\n * seq'd before any live turn — the SessionRunner backfill contract, fed\n * from `thread/resume`'s own `thread.turns`. When the resume response says\n * that page is partial (`turnsBackwardsCursor`), the FULL rollout history\n * is fetched via `thread/read {includeTurns: true}` instead — and if even\n * that fails, the partial page is replayed under a visible notice rather\n * than silently posing as the whole thread. Best-effort like the Claude\n * backfill: an unreadable history never blocks the resume itself.\n */\n async #backfillHistory(): Promise<void> {\n try {\n if (this.#closed) return\n const connection = await this.#ensureThread()\n const resumed = this.#resumedHistory\n this.#resumedHistory = undefined\n let turns = resumed?.turns ?? []\n let partialReason: string | undefined\n if (resumed?.partial) {\n try {\n const read = (await connection.request('thread/read', {\n threadId: this.#sdkSessionId,\n includeTurns: true,\n })) as { thread?: { turns?: AppServerHistoryTurn[] } }\n const full = read?.thread?.turns\n if (Array.isArray(full) && full.length >= turns.length) turns = full\n else partialReason = 'thread/read returned less history than the resume page'\n } catch (error) {\n partialReason = error instanceof Error ? error.message : String(error)\n }\n }\n if (partialReason) {\n // Rendered as an inline notice by both clients (the session keeps\n // running) — a truthful-but-partial transcript must say so, above the\n // part it does show.\n this.#emit({\n type: 'session_error',\n message: `Resumed thread history is incomplete — older turns could not be loaded (${partialReason})`,\n })\n }\n this.#replayTurns(turns)\n } catch {\n // A missing/unreadable thread must not block the resume: the next real\n // turn retries the connection and surfaces its own failure loudly.\n } finally {\n this.#backfillPending = false\n this.#setStatus('idle')\n }\n }\n\n /** Replay historical turns through the SAME item mapping the live path uses. */\n #replayTurns(turns: readonly AppServerHistoryTurn[]): void {\n for (const turn of turns) {\n if (this.#closed) return\n // \"Per turn\" means per HISTORICAL turn: each replayed turn gets its own\n // nonce exactly as each live turn does — codex item ids restart per turn\n // (\"item-1\", …), so one shared namespace would fold turn N's items into\n // turn 1's bubbles (b026e70), and a fresh random nonce per turn also\n // keeps replayed ids disjoint from every future live turn's.\n const state = this.#newTurnState()\n this.#replayingHistory = true\n try {\n for (const item of turn.items ?? []) {\n if (item.type === 'userMessage') {\n // Dropped on the live path (sendMessage already echoed it); in\n // history this IS the turn's user message.\n const text = historyUserText(item)\n if (!text) continue\n this.#emit({\n type: 'user_message',\n message: { role: 'user', content: text },\n parentToolUseId: null,\n uuid: `${state.nonce}:${item.id}`,\n })\n continue\n }\n this.#handleItemCompleted(item, state)\n }\n } finally {\n this.#replayingHistory = false\n }\n }\n }\n\n /** Fresh per-turn state — one per live turn, and one per REPLAYED turn (the\n * nonce is the item-id namespace, and its per-turn-ness is the invariant). */\n #newTurnState(): ActiveTurn {\n return {\n nonce: randomUUID(),\n interrupted: false,\n usage: {\n inputTokens: 0,\n cachedInputTokens: 0,\n cacheWriteInputTokens: 0,\n outputTokens: 0,\n reasoningOutputTokens: 0,\n totalTokens: 0,\n },\n sawUsage: false,\n toolUseEmitted: new Set(),\n sectionIndex: new Map(),\n settled: false,\n resolve: () => {},\n reject: () => {},\n }\n }\n\n async #runTurn(): Promise<void> {\n if (this.#closed) return\n const turn = this.#queue.shift()\n if (!turn) return\n this.#setStatus('running')\n const startedAt = Date.now()\n const active: ActiveTurn = this.#newTurnState()\n const outcome = new Promise<AppServerTurn>((resolve, reject) => {\n active.resolve = (turnResult) => {\n if (active.settled) return\n active.settled = true\n resolve(turnResult)\n }\n active.reject = (error) => {\n if (active.settled) return\n active.settled = true\n reject(error)\n }\n })\n this.#activeTurn = active\n try {\n const connection = await this.#ensureThread()\n const params: Record<string, unknown> = {\n threadId: this.#sdkSessionId,\n input: turn.input,\n cwd: this.#cwd,\n approvalPolicy: APPROVAL_POLICY_BY_MODE[this.#permissionMode],\n sandboxPolicy: this.#turnSandboxPolicy(),\n approvalsReviewer: APPROVALS_REVIEWER_BY_MODE[this.#permissionMode],\n }\n // Overrides persist \"for this turn and subsequent turns\", so name the\n // model/effort explicitly every turn — the resolved default when no\n // override is set, which is what makes setModel(undefined) a real reset.\n const model = this.#model ?? this.#resolvedModel\n if (model) params.model = model\n const effort = this.#reasoningEffort ?? this.#resolvedEffort\n if (effort) params.effort = effort\n // The terminal signal is the turn/completed NOTIFICATION; the response's\n // timing is unspecified, so it only contributes its turn id, a JSON-RPC\n // error (no turn ran → fail now), or — defensively — a terminal status.\n connection.request('turn/start', params).then(\n (result) => {\n const started = (result as { turn?: AppServerTurn })?.turn\n if (!started) return\n active.turnId ??= started.id\n if (started.status && started.status !== 'inProgress') active.resolve(started)\n },\n (error: unknown) => active.reject(error instanceof Error ? error : new Error(String(error))),\n )\n const result = await outcome\n if (this.#closed) return\n if (result.status === 'completed') {\n this.#finishTurn('success', startedAt, active)\n } else {\n const reason =\n result.status === 'interrupted'\n ? 'interrupted'\n : (result.error?.message ??\n active.lastError ??\n 'codex app-server ended the turn without a result')\n this.#finishTurn('failure', startedAt, active, [reason])\n }\n } catch (error) {\n if (this.#closed) return\n // A failed turn is not a failed session: the thread persists on disk and\n // the next message reconnects and resumes it.\n const message = error instanceof Error ? error.message : String(error)\n this.#finishTurn('failure', startedAt, active, [active.interrupted ? 'interrupted' : message])\n } finally {\n if (this.#activeTurn === active) this.#activeTurn = undefined\n }\n }\n\n // -------------------------------------------------------------------------\n // Server→client traffic\n // -------------------------------------------------------------------------\n\n #handleNotification(method: string, params: unknown): void {\n if (this.#closed) return\n // A sub-agent runs in its OWN thread, and its notifications arrive on this\n // same connection carrying that thread's id. Turn lifecycle and token usage\n // are per-thread facts and must not be read off a child's: measured against\n // 0.146.0, a spawned agent's `turn/completed` arrives while the root turn is\n // still running, and taking it ended the session's turn early — reporting\n // the sub-agent's last line as the session's result and dropping everything\n // the root said afterwards (`_docs/codex-subagent-trace.jsonl`). Items and\n // deltas are deliberately NOT filtered here: a sub-agent's work belongs in\n // the transcript, attributed to its agent by `#agentFor`. A child thread's\n // turn lifecycle still means something — to the AGENT, not the session:\n // its `turn/completed` is the agent's completion signal (there is no\n // 'completed' kind on `subAgentActivity`), and a fresh `turn/started` on a\n // settled agent's thread means it is working again.\n if (THREAD_SCOPED_NOTIFICATIONS.has(method) && !this.#isRootThread(params)) {\n if (method === 'turn/completed') this.#settleAgentTurn(params)\n else if (method === 'turn/started') {\n const threadId = this.#threadIdOf(params)\n const record = threadId ? this.#agents.get(threadId) : undefined\n if (record && record.status !== 'running') this.#agents.revive(record)\n }\n return\n }\n // The app-server surface is wide (mcpServer/*, account/*, thread\n // housekeeping…) — everything unmapped is deliberately dropped.\n this.#notifications[method]?.(params)\n }\n\n /** Whether a notification is about the session's own thread. A notification\n * with no `threadId` counts as the root's: every thread-scoped method the\n * schema defines carries one, so an absent id means an older or narrower\n * shape, not a sub-agent. */\n #isRootThread(params: unknown): boolean {\n const threadId = this.#threadIdOf(params)\n if (threadId === undefined) return true\n return threadId === this.#sdkSessionId\n }\n\n #threadIdOf(params: unknown): string | undefined {\n const threadId = (params as { threadId?: unknown })?.threadId\n return typeof threadId === 'string' ? threadId : undefined\n }\n\n /**\n * The agent behind a notification's `threadId` — the attribution every item\n * and delta handler asks before emitting, so two agents streaming\n * concurrently into this one connection come apart again by the id each\n * frame carries, never by any mutable \"current agent\".\n *\n * A non-root thread with no record still gets one: a thread emitting items on\n * this connection *is* an agent, whatever announced it (codex runs threads of\n * its own for review/compact, and a `subAgentActivity` could in principle be\n * missed) — the claude tracker's nested-event fallback, on a stronger signal.\n * The minted record is label-less and its anchor is authored here, because an\n * attributed event whose parent id matches no top-level `tool_use` would\n * render inline rather than as a frame; a late `started` edge fills the name\n * in. Root-thread traffic — and, defensively, the pre-thread shapes with no\n * id at all — stays unattributed (`undefined`).\n */\n #agentFor(params: unknown): CodexAgent | undefined {\n const threadId = this.#threadIdOf(params)\n if (threadId === undefined || threadId === this.#sdkSessionId) return undefined\n const known = this.#agents.get(threadId)\n if (known) return known\n const nonce = this.#activeTurn?.nonce ?? 'codex'\n const record = this.#agents.open(threadId, `${nonce}:agent:${threadId}`, undefined, Date.now())\n record.anchored = true\n this.#emitToolUse(record.toolUseId, CODEX_AGENT_TOOL, { agentThreadId: threadId })\n return record\n }\n\n /**\n * A child thread's `turn/completed` is that AGENT's completion — the one\n * codex sends (`subAgentActivity` has no 'completed' kind, verified live).\n * The verdict is the turn's own status, and the report is the completed\n * turn's final message, delivered as the anchor's `tool_result` so the row\n * settles exactly the way a claude `Task`'s does. Deliberately not gated on\n * `#activeTurn`: an agent finishing between root turns still finished.\n */\n #settleAgentTurn(params: unknown): void {\n const threadId = this.#threadIdOf(params)\n const record = threadId ? this.#agents.get(threadId) : undefined\n if (!record || record.status !== 'running') return\n const turn = (params as { turn?: AppServerTurn })?.turn\n const status = turn?.status === 'completed' ? 'done' : 'failed'\n this.#agents.settle(record, status)\n const report =\n (turn ? turnReport(turn) : undefined) ??\n turn?.error?.message ??\n (status === 'done' ? '' : (turn?.status ?? 'failed'))\n this.#emitToolResult(record.toolUseId, report, status === 'failed')\n }\n\n /** Reasoning deltas arrive on two methods that differ only in which section\n * counter they advance; the section key carries the method so the two streams\n * never share a boundary (and item ids are per-thread, so two agents' streams\n * never share one either). Section boundaries (a new summary/content entry)\n * render as paragraph breaks — the completed item joins sections with '\\n\\n'. */\n #reasoningDelta(method: string): (params: unknown) => void {\n return (params) => {\n const active = this.#activeTurn\n if (!active) return\n const payload = params as {\n delta?: string\n itemId?: string\n contentIndex?: number\n summaryIndex?: number\n }\n if (typeof payload?.delta !== 'string' || !payload.delta) return\n const index = payload.contentIndex ?? payload.summaryIndex ?? 0\n const key = `${payload.itemId ?? ''}:${method}`\n const previous = active.sectionIndex.get(key)\n active.sectionIndex.set(key, index)\n const separator = previous !== undefined && index > previous ? '\\n\\n' : ''\n this.#emitDelta(\n { type: 'thinking_delta', thinking: separator + payload.delta },\n this.#agentFor(params)?.toolUseId ?? null,\n )\n }\n }\n\n /** One item-progress handler serves `item/started` and `item/updated`. */\n #itemProgress = (params: unknown): void => {\n const active = this.#activeTurn\n if (!active) return\n const item = (params as { item?: AppServerItem })?.item\n if (item) this.#handleItemProgress(item, active, this.#agentFor(params))\n }\n\n /** The notification dispatch table — every method the child emits that this\n * runner maps, in one place. Handlers read `this.#activeTurn` themselves:\n * dispatch is synchronous, so the read is the same one the old switch made. */\n readonly #notifications: Record<string, (params: unknown) => void> = {\n 'thread/started': (params) => {\n const thread = (params as { thread?: { id?: string } })?.thread\n if (typeof thread?.id === 'string') this.#sdkSessionId = thread.id\n },\n 'turn/started': (params) => {\n const active = this.#activeTurn\n const turn = (params as { turn?: AppServerTurn })?.turn\n if (active && turn && !active.turnId) active.turnId = turn.id\n },\n 'turn/completed': (params) => {\n const active = this.#activeTurn\n const turn = (params as { turn?: AppServerTurn })?.turn\n if (!active || !turn) return\n // Defence in depth behind the root-thread gate: a turn that is not the\n // one being awaited never ends it.\n if (active.turnId && turn.id && turn.id !== active.turnId) return\n active.resolve(turn)\n },\n 'item/started': this.#itemProgress,\n 'item/updated': this.#itemProgress,\n 'item/completed': (params) => {\n const active = this.#activeTurn\n if (!active) return\n const item = (params as { item?: AppServerItem })?.item\n if (item) this.#handleItemCompleted(item, active, this.#agentFor(params))\n },\n 'item/agentMessage/delta': (params) => {\n if (!this.#activeTurn) return\n const delta = (params as { delta?: string })?.delta\n if (typeof delta === 'string' && delta) {\n // Two agents stream concurrently into this one connection, tokens\n // interleaved — each frame's own `threadId` is what pulls them apart,\n // so attribution rides the frame rather than any notion of \"current\".\n this.#emitDelta({ type: 'text_delta', text: delta }, this.#agentFor(params)?.toolUseId ?? null)\n }\n },\n 'item/reasoning/textDelta': this.#reasoningDelta('item/reasoning/textDelta'),\n 'item/reasoning/summaryTextDelta': this.#reasoningDelta('item/reasoning/summaryTextDelta'),\n 'thread/tokenUsage/updated': (params) => {\n const active = this.#activeTurn\n if (!active) return\n const last = (params as AppServerTokenUsageUpdate)?.tokenUsage?.last\n if (!last) return\n // `last` is one model request; a tool-looping turn makes several. The\n // per-turn number the Anthropic convention wants is their sum.\n active.sawUsage = true\n active.usage.inputTokens += last.inputTokens ?? 0\n active.usage.cachedInputTokens += last.cachedInputTokens ?? 0\n active.usage.cacheWriteInputTokens =\n (active.usage.cacheWriteInputTokens ?? 0) + (last.cacheWriteInputTokens ?? 0)\n active.usage.outputTokens += last.outputTokens ?? 0\n active.usage.reasoningOutputTokens += last.reasoningOutputTokens ?? 0\n // Context occupancy is the OPPOSITE choice from the accounting above:\n // `last` (overwritten, not summed) against the window, because a request's\n // input already contains the whole conversation. `total` is cumulative\n // billing — it grows every turn while the context stays where it is, so a\n // meter built on it would climb to 100% on an almost-empty thread\n // (measured: total 13931 → 27878 across two trivial turns, last 13931 →\n // 13947, window 258400).\n const update = params as AppServerTokenUsageUpdate\n active.contextTokens = last.totalTokens ?? undefined\n active.contextWindow = update.tokenUsage?.modelContextWindow ?? undefined\n },\n 'mcpServer/startupStatus/updated': (params) => {\n // The ONLY place a server's liveness comes from — `mcpServerStatus/list`\n // reports what is configured and what it exposes, never whether it is\n // up. Not gated on `active`: servers start with the child, well before\n // any turn.\n const update = params as AppServerMcpStatusUpdate\n if (typeof update?.name !== 'string') return\n this.#mcpStatus.set(update.name, {\n status: typeof update.status === 'string' ? update.status : 'starting',\n ...(update.error ? { error: update.error } : {}),\n ...(update.failureReason ? { failureReason: update.failureReason } : {}),\n })\n },\n 'skills/changed': () => {\n // An invalidation signal with no payload — codex's watcher saying\n // \"re-run skills/list\", which is exactly what this does. Not gated on\n // `active`: the operator can edit a skill between turns, and that is\n // in fact when they usually do.\n const connection = this.#connection\n if (connection) void this.#refreshSkills(connection)\n },\n 'account/rateLimits/updated': (params) => {\n // Pushed during a turn, so — unlike the Claude engine, whose CLI only\n // pushes on change and therefore needs an explicit poll — listening is\n // enough. Not gated on `active`: a window update is about the account,\n // not the turn.\n this.#emitRateLimits((params as { rateLimits?: AppServerRateLimits })?.rateLimits)\n },\n 'turn/plan/updated': (params) => {\n // v2's todo list, published as the codex.todo_list sdk_event payload\n // both clients already render.\n const active = this.#activeTurn\n if (!active) return\n const plan = (params as AppServerPlanUpdate)?.plan\n if (!Array.isArray(plan)) return\n this.#emit({\n type: 'sdk_event',\n payload: {\n type: 'codex.todo_list',\n id: `${active.nonce}:plan`,\n items: plan.map((step) => ({ text: step.step, completed: step.status === 'completed' })),\n },\n })\n },\n 'serverRequest/resolved': (params) => {\n // Codex settled one of its own asks (auto-resolution, e.g.\n // requestUserInput's autoResolutionMs) — retire the matching card. The\n // late JSON-RPC response we still send is ignored by the peer. The\n // resolved event reports 'deny' because we cannot know what codex\n // chose; the message says who really decided.\n const requestId = (params as { requestId?: string | number })?.requestId\n if (requestId === undefined) return\n for (const [id, pending] of this.#approvals) {\n if (pending.wireId === requestId) {\n this.#settleApproval(id, pending, { behavior: 'deny', message: 'resolved by codex' }, 'policy')\n return\n }\n }\n },\n 'error': (params) => {\n // Mostly retry noise (`willRetry: true`); keep the last message so a\n // turn that fails without its own error still explains itself.\n const active = this.#activeTurn\n const error = (params as { error?: { message?: string } })?.error\n if (active && typeof error?.message === 'string') active.lastError = error.message\n },\n }\n\n /** Answer a server→client request: the ask channels become pending\n * permission requests; anything else gets a JSON-RPC -32601 rather than a\n * hang (an unanswered server request wedges the turn). */\n async #answerServerRequest(\n method: string,\n params: unknown,\n wireId?: string | number,\n ): Promise<unknown> {\n const channel = APPROVAL_CHANNELS[method]\n if (channel) return this.#requestApproval(channel, method, params, wireId)\n throw new JsonRpcError(-32601, `workerdeck does not handle server request '${method}'`)\n }\n\n /**\n * Surface one ask-channel request as a pending {@link PermissionRequest};\n * the returned promise is the JSON-RPC response, resolved when a\n * `permission_decision` lands — or by the timeout, an interrupt, turn end,\n * session close, or codex resolving it itself. Never left hanging.\n */\n #requestApproval(\n channel: ApprovalChannel,\n method: string,\n params: unknown,\n wireId: string | number | undefined,\n ): Promise<unknown> {\n // AskUserQuestion policy resolution, the SessionRunner convention: 'auto'\n // picks each question's first (recommended) option, 'deny' sends the model\n // back to decide for itself — both visibly, neither pending.\n if (method === 'item/tool/requestUserInput') {\n const behavior = this.#config.questionBehavior ?? 'ask'\n if (behavior !== 'ask') {\n return Promise.resolve(this.#resolveQuestionByPolicy(channel, params, behavior))\n }\n }\n const id = randomUUID()\n const timeoutMs =\n this.#config.approvalTimeoutMs ??\n this.#config.defaultApprovalTimeoutMs ??\n DEFAULT_APPROVAL_TIMEOUT_MS\n const itemId = channel.itemId(params)\n const request: PermissionRequest = {\n id,\n ...channel.describe(params),\n // Anchored to the tool card the turn already emitted for this item (the\n // command that ran and was refused, the file change in flight); channels\n // with no item anchor on the request itself.\n toolUseId: itemId ? `${this.#activeTurn?.nonce ?? 'codex'}:${itemId}` : id,\n expiresAt: Date.now() + timeoutMs,\n }\n return new Promise<unknown>((resolve) => {\n const timer = setTimeout(() => {\n const pending = this.#approvals.get(id)\n if (pending) {\n this.#settleApproval(id, pending, { behavior: 'deny', message: 'Approval timed out' }, 'timeout')\n }\n }, timeoutMs)\n this.#approvals.set(id, {\n request,\n channel,\n params,\n offered: offeredDecisions(params),\n wireId,\n timer,\n respond: resolve,\n })\n this.#emit({ type: 'permission_requested', request })\n if (this.#activeTurn) this.#setStatus('awaiting_approval')\n })\n }\n\n /** 'auto'/'deny' sessions settle codex questions synchronously instead of\n * pending. Request/resolved events still fire so transcripts and job\n * webhooks show what was chosen. */\n #resolveQuestionByPolicy(\n channel: ApprovalChannel,\n params: unknown,\n mode: 'auto' | 'deny',\n ): unknown {\n const itemId = channel.itemId(params)\n const request: PermissionRequest = {\n id: randomUUID(),\n ...channel.describe(params),\n toolUseId: itemId ? `${this.#activeTurn?.nonce ?? 'codex'}:${itemId}` : randomUUID(),\n }\n this.#emit({ type: 'permission_requested', request })\n if (mode === 'deny') {\n this.#emit({\n type: 'permission_resolved',\n requestId: request.id,\n behavior: 'deny',\n resolvedBy: 'policy',\n message:\n 'Interactive questions are disabled for this session — choose the most reasonable option yourself and continue.',\n })\n return { answers: {} }\n }\n const answers: Record<string, { answers: string[] }> = {}\n for (const question of (params as AppServerUserInputParams).questions ?? []) {\n const first = question.options?.[0]?.label\n if (first) answers[question.id] = { answers: [first] }\n }\n this.#emit({\n type: 'permission_resolved',\n requestId: request.id,\n behavior: 'allow',\n resolvedBy: 'policy',\n })\n return { answers }\n }\n\n /**\n * Settle one pending approval: pick the channel's wire response for the\n * decision, answer the JSON-RPC request, and emit `permission_resolved`.\n * An allow the request offered no plain accept for becomes the channel's\n * denial, said out loud — never a silently widened grant, and never a\n * decision the request didn't offer.\n */\n #settleApproval(\n id: string,\n pending: PendingCodexApproval,\n decision: PermissionDecision,\n resolvedBy: PermissionDecisionSource,\n ): void {\n clearTimeout(pending.timer)\n this.#approvals.delete(id)\n let behavior = decision.behavior\n let message = decision.behavior === 'deny' ? (decision.message ?? 'Denied') : undefined\n let sent: { response: unknown; decision?: string }\n if (decision.behavior === 'allow') {\n const allowed = pending.channel.allow(pending.params, decision.updatedInput, pending.offered)\n if (allowed) {\n sent = allowed\n } else {\n behavior = 'deny'\n resolvedBy = 'policy'\n message =\n 'codex offered no plain accept for this request (only broader session/policy grants) — denied instead'\n sent = pending.channel.deny(pending.params, false, pending.offered)\n }\n } else {\n sent = pending.channel.deny(pending.params, decision.interrupt === true, pending.offered)\n }\n pending.respond(sent.response)\n this.#emit({ type: 'permission_resolved', requestId: id, behavior, resolvedBy, message })\n if (behavior === 'deny' && decision.behavior === 'deny' && decision.interrupt && sent.decision !== 'cancel') {\n // The wire decision couldn't carry the interrupt itself.\n void this.#interruptTurn()\n }\n if (!this.#closed && this.#approvals.size === 0 && this.#status === 'awaiting_approval') {\n this.#setStatus('running')\n }\n }\n\n // -------------------------------------------------------------------------\n // Item mapping (the v2 camelCase vocabulary → protocol events)\n // -------------------------------------------------------------------------\n\n /** Tool calls surface as tool_use when they start; text and reasoning stream\n * natively via the delta notifications. `agent` is the sub-agent whose thread\n * the item arrived on — undefined for the session's own. */\n #handleItemProgress(item: AppServerItem, active: ActiveTurn, agent?: CodexAgent): void {\n const id = `${active.nonce}:${item.id}`\n // The spawn marker is processed on sight rather than on completion, so the\n // agent's record exists before its thread's first delta can arrive — the\n // handler is idempotent (observed on the wire, started and completed carry\n // the same snapshot in the same batch, but that timing is not a contract).\n if (item.type === 'subAgentActivity') {\n this.#itemCompleted.subAgentActivity(item, active, id, agent)\n return\n }\n if (item.type === 'commandExecution' && !active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, 'CodexCommand', { command: item.command }, agent)\n return\n }\n if (item.type === 'mcpToolCall' && !active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, `mcp__${item.server}__${item.tool}`, item.arguments, agent)\n return\n }\n // A `wait` on spawned agents takes as long as the agents do — the card\n // exists while it blocks, like a command's, rather than appearing only\n // once every agent has answered.\n if (item.type === 'collabAgentToolCall' && !active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, CODEX_COLLAB_TOOL, collabInput(item), agent)\n return\n }\n // Generating a picture takes seconds — the card exists while it runs, like\n // a command's does, rather than appearing only once it is finished.\n if (item.type === 'imageGeneration' && !active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, CODEX_IMAGE_TOOL, imageGenerationInput(item), agent)\n // Rare but real: a progress item can already carry `savedPath`. Announce\n // it here too — `#emitFileProduced` dedupes by path, so the completed\n // item's second report costs nothing.\n if (item.savedPath) this.#emitFileProduced(item.savedPath, id)\n }\n }\n\n #handleItemCompleted(item: AppServerItem, active: ActiveTurn, agent?: CodexAgent): void {\n const id = `${active.nonce}:${item.id}`\n const handler = this.#itemCompleted[item.type] as\n | ((item: AppServerItem, active: ActiveTurn, id: string, agent?: CodexAgent) => void)\n | undefined\n if (handler) {\n handler(item, active, id, agent)\n return\n }\n // An item type the union does not model yet: passed through as an sdk_event\n // rather than dropped — an unmapped item must not be invisible.\n const unknown = item as AppServerUnknownItem\n this.#emit({ type: 'sdk_event', payload: { type: `codex.${unknown.type}`, item: unknown } })\n }\n\n /**\n * The completed-item mapping, one handler per member of the {@link AppServerItem}\n * union. The mapped type is the invariant made checkable: model a new item\n * type in `types.ts` and this table fails to compile until it says what the\n * item becomes on the wire — the old switch silently fell through to the\n * unknown-item passthrough instead. (The runtime still receives types the\n * union has never heard of; those take the passthrough above.)\n */\n readonly #itemCompleted: {\n [K in AppServerItem['type']]: (\n item: Extract<AppServerItem, { type: K }>,\n active: ActiveTurn,\n id: string,\n agent?: CodexAgent,\n ) => void\n } = {\n // On the session's own thread, the echo of our turn/start input — already\n // in the log. On an agent's thread it would be the agent's brief; none has\n // been observed on the wire (the prompt travels in the spawn call, not as\n // an item), but if one ever arrives it is the frame's opening row, exactly\n // where a claude sidechain puts its brief.\n userMessage: (item, active, _id, agent) => {\n if (!agent) return\n const text = historyUserText(item)\n if (!text) return\n this.#emit({\n type: 'user_message',\n message: { role: 'user', content: text },\n parentToolUseId: agent.toolUseId,\n uuid: `${active.nonce}:${item.id}`,\n })\n },\n agentMessage: (item, active, id, agent) => {\n const text = typeof item.text === 'string' ? item.text : ''\n this.#emitAssistant(id, [{ type: 'text', text }], agent?.toolUseId ?? null)\n // An agent's prose is its own report, never the session's final line.\n if (!agent) active.finalText = text\n },\n reasoning: (item, _active, id, agent) => {\n // `summary` is what streamed (the default config); raw `content` only\n // exists when the operator's config enables it. Joined the way the\n // deltas rendered: sections as paragraphs.\n const summary = Array.isArray(item.summary) ? item.summary.filter(Boolean) : []\n const content = Array.isArray(item.content) ? item.content.filter(Boolean) : []\n const thinking = (summary.length > 0 ? summary : content).join('\\n\\n')\n if (thinking) this.#emitAssistant(id, [{ type: 'thinking', thinking }], agent?.toolUseId ?? null)\n },\n commandExecution: (item, active, id, agent) => {\n if (!active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, 'CodexCommand', { command: item.command }, agent)\n }\n const exitCode = item.exitCode ?? undefined\n const failed =\n item.status === 'failed' ||\n item.status === 'declined' ||\n (exitCode !== undefined && exitCode !== 0)\n const output =\n (item.aggregatedOutput ?? '') +\n (exitCode !== undefined && exitCode !== 0 ? `\\n(exit code ${exitCode})` : '')\n this.#emitToolResult(id, output, failed, undefined, agent?.toolUseId ?? null)\n },\n fileChange: (item, _active, id, agent) => {\n // The completed item: by the time it lands the patch applied, failed,\n // or was declined (a pending proposal rides the approval channel, not\n // this item). v2's `kind` is an object (`{type: 'update', …}`), mapped\n // defensively.\n this.#emitToolUse(id, 'CodexFileChange', { changes: item.changes }, agent)\n const lines = item.changes.map((change) => {\n const kind = typeof change.kind === 'string' ? change.kind : change.kind?.type\n return `${kind ?? 'change'}: ${change.path}`\n })\n // Codex reports a unified diff per change, so the wire can carry the\n // same `FilePatch` the Claude engine sends and every client renders one\n // shape. Only for a single-file change: the patch names one file, and a\n // multi-file edit has no honest way to say which.\n const only = item.changes.length === 1 ? item.changes[0] : undefined\n this.#emitToolResult(\n id,\n lines.join('\\n') || item.status,\n item.status === 'failed' || item.status === 'declined',\n only?.diff ? parseUnifiedDiff(only.diff, only.path) : undefined,\n agent?.toolUseId ?? null,\n )\n },\n mcpToolCall: (item, active, id, agent) => {\n if (!active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, `mcp__${item.server}__${item.tool}`, item.arguments, agent)\n }\n const isError = (item.error !== undefined && item.error !== null) || item.status === 'failed'\n this.#emitToolResult(\n id,\n item.error?.message ??\n (item.result === undefined || item.result === null ? '' : JSON.stringify(item.result)),\n isError,\n undefined,\n agent?.toolUseId ?? null,\n )\n },\n webSearch: (item, _active, id, agent) => {\n this.#emitToolUse(id, 'CodexWebSearch', { query: item.query }, agent)\n this.#emitToolResult(id, '', false, undefined, agent?.toolUseId ?? null)\n },\n imageGeneration: (item, active, id, agent) => {\n // Re-emitted, not guarded by `toolUseEmitted`: `savedPath` only exists\n // now, and the reducer upserts a tool_use by id — so this replaces the\n // in-progress card's input with the finished one. The result event\n // follows immediately, which is what settles the status again.\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, CODEX_IMAGE_TOOL, imageGenerationInput(item), agent)\n // The path IS the deliverable — the bytes live on the host and no event\n // may carry them. `file_produced` is what makes those bytes reachable\n // anyway: the gateway serves a path its own runner reported, with no\n // host-file root to declare first.\n if (item.savedPath) this.#emitFileProduced(item.savedPath, id)\n const lines = [\n item.savedPath ? `Saved to ${item.savedPath}` : 'No saved path reported',\n ...(shortResult(item.result) ? [item.result] : []),\n ]\n this.#emitToolResult(id, lines.join('\\n'), item.status === 'failed', undefined, agent?.toolUseId ?? null)\n },\n imageView: (item, _active, id, agent) => {\n this.#emitToolUse(id, 'CodexImageView', { path: item.path }, agent)\n this.#emitToolResult(id, item.path, false, undefined, agent?.toolUseId ?? null)\n },\n subAgentActivity: (item, _active, id, agent) => {\n // The spawn signal — and the whole reason the takeover works on codex.\n // `kind: 'started'` announces an agent (verified live against 0.146.0:\n // the model's `spawn_agent` never produces a collab item, this is the\n // only birth certificate), its `id` is the model's own spawn call id,\n // and `agentThreadId` is the key every one of the agent's later\n // notifications carries. The anchor `tool_use` authored here is what\n // gives the sidechain a row: `terminalBlocks` absorbs by parent id into\n // a top-level call, so without it the attributed events would render\n // inline and there would be nothing to press. `agent` — the SPAWNING\n // thread's record — is normally undefined (the root spawns); a\n // grandchild spawn arriving on a child thread nests one level and\n // counts as that child's tool call, exactly like any other.\n if (this.#replayingHistory) {\n // A resumed thread's history is the root's items only: the agents'\n // work, and their outcomes, are in THEIR threads' rollouts, which the\n // backfill does not read. So the replayed row closes with a neutral\n // notice instead of dangling as running-forever, and the rollup stays\n // silent rather than invent verdicts for agents a dead process ran —\n // the one claim history cannot back is that they failed.\n if (item.kind !== 'started') return\n this.#emitToolUse(\n id,\n CODEX_AGENT_TOOL,\n {\n ...(agentName(item.agentPath) ? { subagent_type: agentName(item.agentPath) } : {}),\n agentThreadId: item.agentThreadId,\n ...(item.agentPath ? { agentPath: item.agentPath } : {}),\n },\n agent,\n )\n this.#emitToolResult(\n id,\n \"(ran in its own thread — its work is not part of this thread's stored history)\",\n false,\n undefined,\n agent?.toolUseId ?? null,\n )\n return\n }\n const record =\n this.#agents.get(item.agentThreadId) ??\n this.#agents.open(item.agentThreadId, id, undefined, Date.now())\n const name = agentName(item.agentPath)\n // Fill-in, and re-anchor when the name arrives late: the reducer upserts\n // a tool_use by id, so re-emitting the anchor relabels the row a\n // fallback record opened nameless.\n const relabel = record.agentType === undefined && name !== undefined\n if (relabel) record.agentType = name\n if (!record.anchored || relabel) {\n record.anchored = true\n this.#emitToolUse(\n record.toolUseId,\n CODEX_AGENT_TOOL,\n {\n ...(record.agentType ? { subagent_type: record.agentType } : {}),\n agentThreadId: item.agentThreadId,\n ...(item.agentPath ? { agentPath: item.agentPath } : {}),\n },\n agent,\n )\n }\n // 'interrupted': cut off before its report — which is the one thing\n // 'done' could have claimed. Anything else that is not the birth edge\n // ('interacted': the root sent it more work) means the agent is working\n // again, so a settled verdict no longer describes it.\n if (item.kind === 'interrupted') {\n if (record.status === 'running') {\n this.#agents.settle(record, 'failed')\n this.#emitToolResult(record.toolUseId, 'interrupted', true)\n }\n return\n }\n if (item.kind !== 'started' && record.status !== 'running') this.#agents.revive(record)\n },\n collabAgentToolCall: (item, active, id, agent) => {\n // The model's collab tool surface, mapped as an ordinary tool card —\n // and deliberately nothing more: on the wire it is decoration (only\n // `wait` has been observed, every rich field empty), so no tracker\n // state hangs off it. The card matters for one honest reason: a `wait`\n // blocks the root visibly for as long as its agents run.\n if (!active.toolUseEmitted.has(id)) {\n active.toolUseEmitted.add(id)\n this.#emitToolUse(id, CODEX_COLLAB_TOOL, collabInput(item), agent)\n }\n if (item.status === 'inProgress') return\n const failed = item.status === 'failed' || item.status === 'declined'\n this.#emitToolResult(id, failed ? item.status : '', failed, undefined, agent?.toolUseId ?? null)\n },\n }\n\n // -------------------------------------------------------------------------\n // Emission (the AiSdkRunner tool_result shape, so the reducer and both UIs\n // render their existing cards unchanged)\n // -------------------------------------------------------------------------\n\n // `parent` on the four emitters below is the owning agent's anchor id — the\n // protocol's sidechain key, resolved per-notification from the frame's own\n // `threadId` (`#agentFor`). Null is the session's own thread, and the\n // reducer's contract makes the distinction cheap to honor: it keys its\n // streaming buffers `streaming:<parentToolUseId>`, so two agents' interleaved\n // deltas accumulate apart as long as every frame says whose it is.\n\n #emitDelta(\n delta: { type: 'text_delta'; text: string } | { type: 'thinking_delta'; thinking: string },\n parent: string | null,\n ): void {\n if (this.#config.includePartialMessages === false) return\n this.#emit({\n type: 'stream_delta',\n event: { type: 'content_block_delta', delta },\n parentToolUseId: parent,\n uuid: randomUUID(),\n })\n }\n\n #emitAssistant(uuid: string, content: ContentBlock[], parent: string | null): void {\n this.#emit({\n type: 'assistant_message',\n message: { role: 'assistant', content, model: this.#model ?? this.#resolvedModel },\n parentToolUseId: parent,\n uuid,\n })\n }\n\n /** `agent` (rather than a bare parent id) because a nested call is also the\n * agent's progress reading: `SubagentInfo.toolCount` ticks here, once per\n * card — the `counted` set is what keeps an upserted re-emission (the\n * finished imageGeneration input) from counting one picture twice. */\n #emitToolUse(id: string, name: string, input: unknown, agent?: CodexAgent): void {\n if (agent && !agent.counted.has(id)) {\n agent.counted.add(id)\n agent.toolCount += 1\n }\n this.#emit({\n type: 'assistant_message',\n message: {\n role: 'assistant',\n content: [{ type: 'tool_use', id, name, input }],\n model: this.#model ?? this.#resolvedModel,\n },\n parentToolUseId: agent?.toolUseId ?? null,\n uuid: `${id}-use`,\n })\n }\n\n #emitToolResult(\n toolUseId: string,\n content: string,\n isError: boolean,\n patch?: FilePatch,\n parent: string | null = null,\n ): void {\n this.#emit({\n type: 'user_message',\n message: {\n role: 'user',\n content: [\n { type: 'tool_result', tool_use_id: toolUseId, content, is_error: isError || undefined },\n ],\n },\n parentToolUseId: parent,\n synthetic: true,\n patch,\n uuid: `${toolUseId}-result`,\n })\n }\n\n /**\n * Per-turn usage re-mapped to the Anthropic accounting convention the whole\n * stack assumes (GOTCHAS §Codex engine): OpenAI's `inputTokens` includes the\n * cached share, so input excludes it (else queue token budgets double-count\n * cache-heavy runs); reasoning tokens are billed output; `totalCostUsd: 0` =\n * unknown, the AiSdkRunner precedent. Usage is summed from the turn's\n * `thread/tokenUsage/updated` notifications — `turn/completed` carries none.\n */\n #finishTurn(\n kind: 'success' | 'failure',\n startedAt: number,\n active: ActiveTurn,\n errors?: string[],\n ): void {\n // Approvals that outlived the turn (codex moved on, or the turn failed\n // around them) are settled now — a card must never outlive what it gates,\n // and an unanswered timer must never fire into a finished turn.\n for (const [id, pending] of this.#approvals) {\n this.#settleApproval(id, pending, { behavior: 'deny', message: 'Turn ended' }, 'policy')\n }\n this.#numTurns += 1\n this.#totalCostUsd = 0\n const usage = active.sawUsage ? active.usage : undefined\n this.#emit({\n type: 'turn_result',\n subtype: kind === 'success' ? 'success' : 'error_during_execution',\n isError: kind !== 'success',\n durationMs: Date.now() - startedAt,\n numTurns: this.#numTurns,\n totalCostUsd: 0,\n result: kind === 'success' ? (active.finalText ?? '') : undefined,\n errors,\n usage: usage\n ? {\n input_tokens: Math.max(0, usage.inputTokens - usage.cachedInputTokens),\n output_tokens: usage.outputTokens + usage.reasoningOutputTokens,\n cache_creation_input_tokens: usage.cacheWriteInputTokens ?? 0,\n cache_read_input_tokens: usage.cachedInputTokens,\n }\n : undefined,\n })\n this.#emitContextUsage(active)\n this.#setStatus('idle')\n }\n\n /**\n * Subscription windows, mapped onto the protocol's named vocabulary.\n *\n * The shapes disagree: codex reports windows *positionally* (`primary` /\n * `secondary`) with a length in minutes, while `RateLimitInfo.rateLimitType`\n * is a name whose meaning clients already know — iOS labels `seven_day` as\n * \"Weekly\" and derives the pace marker's denominator from it. Naming the\n * window by its measured duration is therefore the honest mapping rather\n * than a borrowed one: codex's primary window is 10080 minutes, which *is*\n * seven days. A duration we have no name for keeps an explicit\n * `window_<n>m` key — clients render it verbatim and simply draw no pace\n * marker, which beats mislabeling it as a week.\n *\n * `status` is 'allowed' by construction (the session is running), matching\n * `rateLimitEventsFromUsage`; codex's `rateLimitReachedType` is the one\n * signal that a limit is actually biting, so it becomes 'rejected'.\n */\n #emitRateLimits(limits: AppServerRateLimits | undefined | null): void {\n if (!limits) return\n const status = limits.rateLimitReachedType ? 'rejected' : 'allowed'\n for (const window of [limits.primary, limits.secondary]) {\n // A window with no percentage is unknown, not zero — dropped rather than\n // reported at 0%, the same rule the Claude mapping follows.\n if (!window || window.usedPercent === null || window.usedPercent === undefined) continue\n this.#emit({\n type: 'rate_limit',\n info: {\n status,\n rateLimitType: rateLimitWindowName(window.windowDurationMins),\n utilization: window.usedPercent,\n ...(typeof window.resetsAt === 'number' ? { resetsAt: window.resetsAt } : {}),\n },\n })\n }\n // Emitted once per change, like the Claude engine's — it names the windows\n // rather than sizing them.\n if (limits.planType && limits.planType !== this.#planType) {\n this.#planType = limits.planType\n this.#emit({ type: 'plan_info', subscriptionType: limits.planType })\n }\n }\n\n /**\n * Context occupancy, after the turn — the same cadence the Claude runner\n * polls `getContextUsage()` on, so clients need nothing new.\n *\n * Emitted only when the binary gave BOTH numbers: the protocol is explicit\n * that a client renders nothing rather than a 0% ring, and a window of\n * `null` (which app-server does send) would otherwise divide into a\n * meaningless percentage. `categories` is empty because codex publishes no\n * breakdown — clients must not render an empty \"Breakdown\" section for it.\n */\n #emitContextUsage(active: ActiveTurn): void {\n const totalTokens = active.contextTokens\n const maxTokens = active.contextWindow\n if (totalTokens === undefined || !maxTokens || maxTokens <= 0) return\n this.#emit({\n type: 'context_usage',\n usage: {\n categories: [],\n totalTokens,\n maxTokens,\n percentage: Math.min(100, (totalTokens / maxTokens) * 100),\n model: this.#model ?? this.#resolvedModel,\n },\n })\n }\n\n #setStatus(status: SessionStatus, detail?: string): void {\n if (this.#status === status) return\n if (this.#status === 'closed' || this.#status === 'failed') return\n this.#status = status\n this.#emit({ type: 'status_changed', status, detail })\n }\n\n #emit(body: SessionEventBody): void {\n // History replay reuses the live item mapping wholesale; the replay flag\n // is stamped here so the mapping itself stays one code path.\n if (this.#replayingHistory && (body.type === 'assistant_message' || body.type === 'user_message')) {\n body = { ...body, replay: true }\n }\n const event: SessionEvent = { ...body, seq: ++this.#seq, ts: Date.now() }\n this.#lastActivityAt = event.ts\n // Rows, not events: what a client diffs to know how much it missed.\n this.#activityCount += transcriptActivity(body)\n // The list's copy of the reading the transcript already has. Folded here\n // rather than at the point it is fetched, so every producer — and any\n // future one — passes through the same rule.\n this.#contextUsage = contextReading(body) ?? this.#contextUsage\n // A reset retires the conversation the window described; the old fill is\n // not this conversation's, exactly as the transcript state clears it.\n if (body.type === 'conversation_reset') this.#contextUsage = undefined\n this.#events.push(event)\n this.#subscribers.emit(event)\n }\n}\n","import type { ModelCatalog } from '../adapter.ts'\n\n/**\n * The Codex engine's model catalog, seeded from the binary's own embedded\n * presets — `@openai/codex@0.146.0` ships its model table inside the\n * executable, and that table (not the SDK's stale `ModelReasoningEffort`\n * union) is the truth about which reasoning efforts each model takes.\n *\n * **Refresh procedure** (release checklist): extract the embedded JSON from\n * the platform binary and diff —\n *\n * node -e 'const d=require(\"fs\").readFileSync(process.argv[1]);\n * const s=d.indexOf(`{\\n \"models\": [`);\n * let i=s,n=0; do{n+=(d[i]===123)-(d[i]===125);i++}while(n);\n * const c=JSON.parse(d.slice(s,i));\n * for(const m of c.models) console.log(m.slug, m.display_name,\n * m.visibility, m.supported_reasoning_levels.map(l=>l.effort).join(\",\"))'\\\n * \"$(node -p 'const{createRequire}=require(\"module\");\n * const w=require.resolve(\"@openai/codex/package.json\");\n * createRequire(w).resolve(\"@openai/codex-darwin-arm64/package.json\")\n * .replace(\"package.json\",\"vendor/aarch64-apple-darwin/bin/codex\")')\"\n *\n * The two-hop resolve is NOT optional: under pnpm's strict layout the platform\n * package is a dependency of `@openai/codex`, so it resolves only from that\n * wrapper's location, never from the repo root. Resolving it directly throws\n * MODULE_NOT_FOUND — the same two hops `resolveBundledCodexExecutable` makes.\n *\n * Mapping decisions:\n * - the internal `codex-auto-review` row is dropped (the codex analogue of\n * dropping the CLI's `default` sentinel);\n * - `primary` mirrors the binary's own `visibility` field ('list' = shown in\n * its picker, 'hide' = its \"older models\"), so both UIs group the way\n * codex's own picker does;\n * - `reasoningEfforts` carries `supported_reasoning_levels` verbatim — note\n * `max`/`ultra` beyond the SDK union; trust the binary, keep strings open.\n */\nexport const CODEX_CATALOG: ModelCatalog = {\n provenance:\n 'embedded model presets of @openai/codex@0.149.0 (darwin-arm64 binary), extracted 2026-08-22',\n models: [\n {\n value: 'gpt-5.6-sol',\n resolvedModel: 'gpt-5.6-sol',\n displayName: 'GPT-5.6 Sol',\n description: 'Latest frontier agentic coding model.',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max', 'ultra'],\n },\n {\n value: 'gpt-5.6-terra',\n resolvedModel: 'gpt-5.6-terra',\n displayName: 'GPT-5.6 Terra',\n description: 'Balanced agentic coding model for everyday work.',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max', 'ultra'],\n },\n {\n value: 'gpt-5.6-luna',\n resolvedModel: 'gpt-5.6-luna',\n displayName: 'GPT-5.6 Luna',\n description: 'Fast and affordable agentic coding model.',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],\n },\n {\n value: 'gpt-5.5',\n resolvedModel: 'gpt-5.5',\n displayName: 'GPT-5.5',\n description: 'Frontier model for complex coding, research, and real-world work.',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],\n },\n {\n value: 'gpt-5.4',\n resolvedModel: 'gpt-5.4',\n displayName: 'GPT-5.4',\n description: 'Strong model for everyday coding.',\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],\n },\n {\n value: 'gpt-5.4-mini',\n resolvedModel: 'gpt-5.4-mini',\n displayName: 'GPT-5.4 Mini',\n description: 'Small, fast, and cost-efficient model for simpler coding tasks.',\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],\n },\n {\n value: 'gpt-5.2',\n resolvedModel: 'gpt-5.2',\n displayName: 'GPT-5.2',\n description: 'Optimized for professional work and long-running agents.',\n primary: true,\n reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],\n },\n ],\n}\n","import { spawn } from 'node:child_process'\nimport { JsonRpcStdioConnection } from './jsonrpc.ts'\nimport type { AppServerConnection } from './types.ts'\n\n/** How much stderr to keep for the exit diagnostic. The binary logs startup\n * noise there; only the tail explains a death. */\nconst STDERR_TAIL_BYTES = 4096\n\n/**\n * Spawn one `codex app-server` child and frame JSON-RPC over its stdio — the\n * real {@link AppServerConnectFn}. The child's env is passed **complete**\n * (a provided spawn env replaces process.env, never merges with it), with the\n * profile's CODEX_HOME pin already applied by the runner.\n *\n * No spawn cwd: the working directory is a thread/turn parameter, and a cwd\n * that doesn't exist should fail the *turn* with codex's own error, not the\n * spawn.\n */\nexport function connectAppServer(options: {\n executable: string\n env: Record<string, string>\n}): AppServerConnection {\n const child = spawn(options.executable, ['app-server'], {\n env: options.env,\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n const rpc = new JsonRpcStdioConnection({ input: child.stdout, output: child.stdin })\n\n let stderrTail = ''\n child.stderr.on('data', (chunk: Buffer) => {\n stderrTail = (stderrTail + String(chunk)).slice(-STDERR_TAIL_BYTES)\n })\n\n let closeHandler: ((message: string) => void) | undefined\n let done = false\n const settle = (message: string) => {\n if (done) return\n done = true\n rpc.fail(message)\n closeHandler?.(message)\n }\n child.on('error', (error) => settle(`codex app-server failed to start: ${error.message}`))\n child.on('exit', (code, signal) => {\n const tail = stderrTail.trim()\n settle(\n `codex app-server exited (${signal ?? `code ${code}`})` +\n (tail ? `: ${tail.slice(-500)}` : ''),\n )\n })\n\n return {\n request: (method, params) => rpc.request(method, params),\n notify: (method, params) => rpc.notify(method, params),\n onNotification: (handler) => rpc.onNotification(handler),\n onRequest: (handler) => rpc.onRequest(handler),\n onClose: (handler) => {\n closeHandler = handler\n },\n close: () => {\n // Deliberate teardown: suppress the exit callback so a session close\n // doesn't read as a crash, then let SIGTERM end the child.\n done = true\n rpc.fail('codex app-server connection closed')\n child.kill()\n },\n }\n}\n","import { execFile } from 'node:child_process'\nimport { existsSync, realpathSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport {\n ENGINE_CAPABILITIES,\n PROTOCOL_VERSION,\n type ProfileInfo,\n type SdkSessionSummary,\n} from '@workerdeck/protocol'\nimport type { EngineAdapter, EngineAvailability } from '../adapter.ts'\nimport { CodexRunner } from './runner.ts'\nimport { CODEX_CATALOG } from './catalog.ts'\nimport { connectAppServer } from './process.ts'\nimport type { AppServerConnectFn, AppServerThreadListResponse, AppServerThreadSummary } from './types.ts'\n\nconst NOT_INSTALLED =\n '@openai/codex is not installed — add it (an optional peer of @workerdeck/core) to run codex profiles'\n\n/**\n * The codex binary sessions will run: the per-platform package installed next\n * to `@openai/codex`, `vendor/<target-triple>/bin/codex` — the same file the\n * npm wrapper's own `bin/codex.js` launcher execs. Probing this binary rather\n * than whatever `codex` is on PATH means the availability answer is about the\n * executable sessions will actually run. Undefined when it can't be found;\n * callers degrade to 'unknown'.\n */\nexport function resolveBundledCodexExecutable(): string | undefined {\n const triple = targetTriple()\n if (!triple) return undefined\n try {\n // Two hops on purpose (the claude-auth pattern): the platform package is a\n // dependency of @openai/codex, so under pnpm's strict layout it only\n // resolves from @openai/codex's own location, never from ours. Plain\n // createRequire throughout — neither package has an exports map.\n const fromHere = createRequire(import.meta.url)\n const wrapper = fromHere.resolve('@openai/codex/package.json')\n const fromWrapper = createRequire(wrapper)\n const platformPackage = fromWrapper.resolve(`@openai/codex-${platformPackageSuffix()}/package.json`)\n const path = platformPackage.replace(/package\\.json$/, `vendor/${triple}/bin/codex`)\n if (existsSync(path)) return path\n } catch {\n // not installed — nothing to probe\n }\n return undefined\n}\n\nfunction targetTriple(): string | undefined {\n const { platform, arch } = process\n if (platform === 'darwin') return arch === 'arm64' ? 'aarch64-apple-darwin' : 'x86_64-apple-darwin'\n if (platform === 'linux') {\n return arch === 'arm64' ? 'aarch64-unknown-linux-musl' : 'x86_64-unknown-linux-musl'\n }\n if (platform === 'win32') return 'x86_64-pc-windows-msvc'\n return undefined\n}\n\nfunction platformPackageSuffix(): string {\n return `${process.platform}-${process.arch}`\n}\n\n/**\n * Availability, mirroring **the app-server surface's actual credential chain**\n * (verified 2026-08-05 against 0.146.0 by driving the raw binary): auth comes\n * solely from the CODEX_HOME auth store (`codex login`, file or keyring). The\n * env-key routes are dead ends here — `CODEX_API_KEY` is read only by\n * `codex exec` (a turn goes out with no credential at all: \"Missing bearer\"),\n * and `OPENAI_API_KEY` was never read by either surface. So, in order:\n *\n * 1. Binary resolvable, else unavailable with the install reason;\n * 2. `codex login status` under the profile's complete session env:\n * exit 0 → available; the \"Not logged in\" verdict → unavailable, with an\n * exact remedy when a stranded env key explains the misconfiguration;\n * anything else (a stray CODEX_ACCESS_TOKEN JWT error, a crashed spawn) →\n * 'unknown' — the checkClaudeAuth never-overclaim discipline.\n *\n * Only the exit code and the fixed verdict line are consulted — never\n * surfaced: `login status` output includes a masked key fragment. The\n * `smoke:codex --canary` run is the drift alarm for all of this.\n */\nasync function checkCodexAvailability(\n profile: ProfileInfo,\n env: Record<string, string | undefined>,\n options: { timeoutMs?: number } = {},\n): Promise<EngineAvailability> {\n const executable = resolveBundledCodexExecutable()\n if (!executable) return { available: false, reason: NOT_INSTALLED }\n const childEnv: Record<string, string> = {}\n for (const [key, value] of Object.entries(env)) {\n if (value !== undefined) childEnv[key] = value\n }\n if (profile.codexHome) childEnv.CODEX_HOME = profile.codexHome\n return new Promise((resolve) => {\n execFile(\n executable,\n ['login', 'status'],\n { env: childEnv, timeout: options.timeoutMs ?? 10_000 },\n (error, stdout, stderr) => {\n if (!error) {\n resolve({ available: true })\n return\n }\n // The verdict line lands on stderr (0.146.0); check both streams so a\n // future move doesn't silently degrade every verdict to 'unknown'.\n if (`${stdout}\\n${stderr}`.includes('Not logged in')) {\n // Presence checks on the NAMES only; values are never read.\n const hint = childEnv.CODEX_API_KEY\n ? ' CODEX_API_KEY is read only by `codex exec`, never by the app-server — run ' +\n '`codex login --with-api-key` under this profile’s CODEX_HOME to persist it.'\n : childEnv.OPENAI_API_KEY\n ? ' OPENAI_API_KEY is not used by codex — run `codex login --with-api-key` ' +\n 'under this profile’s CODEX_HOME.'\n : ''\n resolve({\n available: false,\n reason:\n `codex is not logged in for this profile's environment — run \\`codex login\\`` +\n (profile.codexHome ? ` with CODEX_HOME=${profile.codexHome}` : '') +\n `.${hint}`,\n })\n return\n }\n // An errored probe (not a verdict) is not evidence of a missing login.\n resolve({ available: 'unknown' })\n },\n )\n })\n}\n\n/** `thread/list` page size (its own default is 25) and a hard page bound so a\n * misbehaving cursor can never spin the listing forever. */\nconst LIST_PAGE_SIZE = 100\nconst MAX_LIST_PAGES = 40\n\n/** thread/list's `cwd` filter is an EXACT path match (measured, 0.146.0), so\n * offer both the spelled and canonical forms — macOS listings would otherwise\n * miss `/tmp/...` threads recorded under `/private/tmp/...`. */\nfunction cwdFilter(dir: string): string[] {\n const forms = new Set([dir])\n try {\n forms.add(realpathSync(dir))\n } catch {\n // A directory that no longer exists still names its recorded threads.\n }\n return [...forms]\n}\n\nconst secondsToMs = (value: number | null | undefined): number | undefined =>\n typeof value === 'number' && Number.isFinite(value) ? value * 1000 : undefined\n\n/** One thread row in the protocol's browser-safe summary shape. `id` is what\n * `CreateSessionRequest.resume` feeds `thread/resume` — the row's separate\n * `sessionId` field is not it. */\nfunction summarizeThread(row: AppServerThreadSummary): SdkSessionSummary {\n const name = typeof row.name === 'string' && row.name.length > 0 ? row.name : undefined\n const preview = typeof row.preview === 'string' && row.preview.length > 0 ? row.preview : undefined\n return {\n sessionId: row.id,\n summary: name ?? preview ?? row.id,\n lastModified: secondsToMs(row.updatedAt) ?? secondsToMs(row.createdAt) ?? 0,\n createdAt: secondsToMs(row.createdAt),\n customTitle: name,\n firstPrompt: preview,\n gitBranch:\n typeof row.gitInfo?.branch === 'string' && row.gitInfo.branch.length > 0\n ? row.gitInfo.branch\n : undefined,\n cwd: typeof row.cwd === 'string' ? row.cwd : undefined,\n }\n}\n\n/**\n * CODEX_HOME's threads over ONE short-lived `codex app-server` child: the\n * runner's own handshake (`experimentalApi` and all — one code path, no\n * second vocabulary to drift), `thread/list` pages walked by cursor, child\n * closed before returning. Requires no live session and costs no tokens —\n * it is how \"resume\" is offered before anything is running. The `connectFn`\n * seam exists for the scripted-peer tests; the adapter passes the real\n * spawn.\n */\nexport async function listCodexSessions(options: {\n connectFn: AppServerConnectFn\n profile?: ProfileInfo\n env: Record<string, string | undefined>\n dir?: string\n limit?: number\n offset?: number\n}): Promise<SdkSessionSummary[]> {\n const childEnv: Record<string, string> = {}\n for (const [key, value] of Object.entries(options.env)) {\n if (value !== undefined) childEnv[key] = value\n }\n if (options.profile?.codexHome) childEnv.CODEX_HOME = options.profile.codexHome\n const connection = options.connectFn({ env: childEnv })\n const rows: AppServerThreadSummary[] = []\n try {\n await connection.request('initialize', {\n clientInfo: {\n name: 'workerdeck',\n title: 'WorkerDeck',\n version: `protocol-${PROTOCOL_VERSION}`,\n },\n capabilities: { experimentalApi: true },\n })\n connection.notify('initialized')\n // Newest-first by *update* time — `lastModified` is the field the pickers\n // sort and render, and codex's own default sort is by creation.\n const base: Record<string, unknown> = {\n limit: LIST_PAGE_SIZE,\n sortKey: 'updated_at',\n ...(options.dir ? { cwd: cwdFilter(options.dir) } : {}),\n }\n const want = options.limit === undefined ? undefined : (options.offset ?? 0) + options.limit\n let cursor: string | undefined\n for (let page = 0; page < MAX_LIST_PAGES; page++) {\n const result = (await connection.request('thread/list', {\n ...base,\n ...(cursor ? { cursor } : {}),\n })) as AppServerThreadListResponse\n const data = Array.isArray(result?.data) ? result.data : []\n rows.push(...data)\n if (want !== undefined && rows.length >= want) break\n if (data.length === 0 || typeof result?.nextCursor !== 'string') break\n cursor = result.nextCursor\n }\n } finally {\n connection.close()\n }\n const summaries = rows\n // An ephemeral thread was never materialized on disk — nothing to resume.\n .filter((row) => typeof row.id === 'string' && row.id.length > 0 && !row.ephemeral)\n .map(summarizeThread)\n const start = options.offset ?? 0\n return options.limit === undefined ? summaries.slice(start) : summaries.slice(start, start + options.limit)\n}\n\n/**\n * OpenAI Codex as an engine: the codex CLI binary driven over its `app-server`\n * JSON-RPC surface — structurally the Claude engine's sibling (a local agent\n * binary with sessions, sandboxing and resume, resolving its own credentials\n * from the operator's environment). `@openai/codex` — the npm package that\n * carries the binary — is an **optional peer**: absent, every codex profile\n * reports unavailable and createRunner throws the same message, and no\n * consumer downloads a ~40 MB per-platform binary it never uses.\n */\nexport const codexAdapter: EngineAdapter = {\n engine: 'codex',\n capabilities: ENGINE_CAPABILITIES.codex,\n catalog: CODEX_CATALOG,\n checkAvailability: (profile, env) => checkCodexAvailability(profile, env),\n createRunner({ config, profile, restore, id }) {\n if (restore) throw new Error('the codex engine cannot rebuild a parked session')\n const executable =\n (config as { codexPathOverride?: string }).codexPathOverride ??\n resolveBundledCodexExecutable()\n if (!executable) throw new Error(NOT_INSTALLED)\n return new CodexRunner(\n {\n ...config,\n codexHome: profile?.codexHome,\n connectFn: (options) => connectAppServer({ executable, ...options }),\n },\n id,\n )\n },\n async listSessions(options) {\n const executable = resolveBundledCodexExecutable()\n if (!executable) throw new Error(NOT_INSTALLED)\n return listCodexSessions({\n ...options,\n connectFn: (connect) => connectAppServer({ executable, ...connect }),\n })\n },\n}\n","import { ENGINE_CAPABILITIES } from '@workerdeck/protocol'\nimport type { EngineAdapter } from '../adapter.ts'\n\n/**\n * The model-agnostic provider engine as a pseudo-adapter: capabilities and an\n * env-var probe live here, but its runners are assembled by the host's\n * `createEngineRunner` hook (which is where provider credentials are resolved\n * and model SDKs are imported — neither belongs in this repo's import graph).\n * The server routes provider creates to the hook; `createRunner` here throws\n * so a mis-routed call fails loudly instead of quietly building nothing.\n *\n * The catalog is empty by the same token: provider model ids are operator-\n * declared per profile (`provider.models`), not shipped with releases.\n */\nexport const providerAdapter: EngineAdapter = {\n engine: 'provider',\n capabilities: ENGINE_CAPABILITIES.provider,\n catalog: { models: [], provenance: 'provider model ids are operator-declared (provider.models)' },\n async checkAvailability(profile, env) {\n const keyEnv = profile.provider?.apiKeyEnv\n // No declared key variable = nothing this probe can check (the host hook\n // may resolve credentials some other way) — unknown, not unavailable.\n if (!keyEnv) return { available: 'unknown' }\n const value = env[keyEnv]\n if (value !== undefined && value !== '') return { available: true }\n return {\n available: false,\n reason: `${keyEnv} is not set in the server environment (profile '${profile.name}' names it as apiKeyEnv)`,\n }\n },\n createRunner() {\n throw new Error(\n \"provider-engine runners are built by the host's createEngineRunner hook, not the adapter\",\n )\n },\n}\n","import type {\n EngineCapabilities,\n ModelOption,\n ProfileEngine,\n ProfileInfo,\n SdkSessionSummary,\n} from '@workerdeck/protocol'\nimport type { Runner, RunnerSnapshot } from '../runner-interface.ts'\nimport type { SessionRunnerConfig } from './claude/runner.ts'\n\n/**\n * A probe's verdict on one profile's credentials. 'unknown' means the probe\n * could not run at all — which is NOT evidence of a missing login and must\n * never be surfaced as one (the `checkClaudeAuth` discipline, generalized).\n */\nexport type EngineAvailability =\n | { available: true }\n | { available: false; reason: string }\n | { available: 'unknown' }\n\n/**\n * A model catalog shipped with the release — the answer to \"what can a create\n * form offer\" with no process spawned, correct from a gateway's first request.\n *\n * Never contains a 'default' sentinel row (a choice, not a model — forms add\n * their own \"Profile default\" row mapping to an unset model). Staleness is\n * bounded by the release cadence: the release checklist re-runs each catalog's\n * extraction procedure (documented in its file header) and diffs.\n */\nexport type ModelCatalog = {\n models: ModelOption[]\n /** Source + date, for the release-checklist refresh. Not served. */\n provenance: string\n}\n\nexport type EngineRunnerRequest = {\n config: SessionRunnerConfig\n profile?: ProfileInfo\n /** Rebuild a parked session instead of starting fresh. Engines that cannot\n * rehydrate throw. */\n restore?: RunnerSnapshot\n /**\n * Adopt this session id instead of minting one. For rehydrating a session\n * across a gateway restart: the transcript comes back from the *engine's* own\n * store via `config.resume`, but every client keys its watermarks, unread\n * counts and routes on the WorkerDeck id, so that id has to survive too\n * (`SessionInfo.id` is documented as stable across resumes). A `restore`\n * carries its own id in the snapshot and does not need this.\n */\n id?: string\n}\n\n/**\n * One engine, as the server consumes it: its capability record, its shipped\n * model catalog, a credential probe, and a runner factory. The claude adapter\n * wraps `SessionRunner` without behaviour change; the codex adapter owns the\n * `codex app-server` integration; the provider adapter is a pseudo-adapter —\n * its runners are built by the host's `createEngineRunner` hook, so its\n * `createRunner` throws and the server routes around it.\n */\nexport interface EngineAdapter {\n readonly engine: ProfileEngine\n /** Must deep-equal ENGINE_CAPABILITIES[engine] — asserted by a core test, so\n * the protocol's browser-safe defaults can never drift from the adapter. */\n readonly capabilities: EngineCapabilities\n readonly catalog: ModelCatalog\n /**\n * Probe whether `profile`'s credentials are usable under `env` — the full\n * session environment the real assembly path produces, never a delta (codex\n * replaces the child env wholesale, and a delta would strand HOME/PATH and\n * the auth chain with it). Never rejects.\n */\n checkAvailability(\n profile: ProfileInfo,\n env: Record<string, string | undefined>,\n ): Promise<EngineAvailability>\n /** Build a Runner. Throwing fails the create (session POST 500s, job fails). */\n createRunner(request: EngineRunnerRequest): Runner | Promise<Runner>\n /**\n * List the engine's on-disk resumable sessions (`GET /sdk-sessions`) —\n * present exactly when the capability record's `listSessions` is true. Must\n * not require a live session: the codex adapter answers over a short-lived\n * `thread/list` app-server child it closes before returning; the claude\n * adapter reads the Agent SDK's store directly. `env` follows the\n * checkAvailability contract (the profile's complete session environment,\n * never a delta). `dir` narrows to one project directory; `limit`/`offset`\n * page the newest-first result.\n */\n listSessions?(options: {\n profile?: ProfileInfo\n env: Record<string, string | undefined>\n dir?: string\n limit?: number\n offset?: number\n }): Promise<SdkSessionSummary[]>\n}\n\nimport { claudeAdapter } from './claude/adapter.ts'\nimport { codexAdapter } from './codex/adapter.ts'\nimport { providerAdapter } from './provider/adapter.ts'\n\nconst ADAPTERS: Record<ProfileEngine, EngineAdapter> = {\n claude: claudeAdapter,\n codex: codexAdapter,\n provider: providerAdapter,\n}\n\n/** The in-repo adapter for an engine. An absent `engine` means 'claude'. */\nexport function getEngineAdapter(engine: ProfileEngine | undefined): EngineAdapter {\n return ADAPTERS[engine ?? 'claude']\n}\n"],"mappings":"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"}