@workerdeck/core 0.6.0 → 0.9.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","#config","#permissionMode","#status","#sdkSessionId","#seq","#apiKeySource","#pending","#model","#title","#totalCostUsd","#numTurns","#lastActivityAt","#started","#runPromise","#run","#closed","#input","#emit","#settleApproval","#query","#setStatus","#events","#listeners","sdkQuery","#backfillHistory","#buildOptions","#fetchCapabilities","#fetchContextUsage","#handleMessage","#canUseTool","#capabilitiesEmitted","#resolveQuestionByPolicy","#statusDetail","#config","#model","#permissionMode","#modelAlias","#restore","#seq","#events","#messages","#pendingToolCalls","#dispatched","#numTurns","#totalUsage","#turnAccum","#lastActivityAt","#status","#modelId","#title","#started","#turnChain","#scheduleTurn","#setStatus","#closed","#parked","#abort","#restingOnDeferred","#listeners","#emit","#settlePendingCall","#runTurn","#applyExecutionResult","#announceParked","#dispatchPending","#finishTurn","#options","#execute","#allowsNetwork","#fetchText","#slots","#settle","#options","#early","#applyAnswer","#options"],"sources":["../src/input-queue.ts","../src/normalize.ts","../src/runner.ts","../src/ai-sdk-runner.ts","../src/claude-auth.ts","../src/quickjs-executor.ts","../src/pending-registry.ts","../src/browser-bridge-executor.ts","../src/deferred-executor.ts","../src/tools.ts","../src/web-fetch.ts","../src/engine.ts"],"sourcesContent":["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 { SDKMessage } from '@anthropic-ai/claude-agent-sdk'\nimport type { ApiMessage, ContentBlock, SessionEventBody } from '@workerdeck/protocol'\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/**\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 return {\n type: 'user_message',\n message: toApiMessage(msg.message),\n parentToolUseId: msg.parent_tool_use_id,\n replay: 'isReplay' in msg && msg.isReplay === true ? true : undefined,\n synthetic: msg.isSynthetic === true ? true : undefined,\n uuid: msg.uuid,\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 '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 { randomUUID } from 'node:crypto'\nimport {\n getSessionMessages,\n query as sdkQuery,\n type CanUseTool,\n type Options,\n type PermissionResult,\n type Query,\n type SDKMessage,\n type SDKUserMessage,\n type SessionMessage,\n} from '@anthropic-ai/claude-agent-sdk'\nimport type {\n CreateSessionRequest,\n PermissionMode,\n PermissionRequest,\n SessionEvent,\n SessionEventBody,\n SessionInfo,\n SessionStatus,\n} from '@workerdeck/protocol'\nimport { InputQueue } from './input-queue.ts'\nimport { normalizeSdkMessage, toApiMessage } from './normalize.ts'\nimport type { PermissionDecision, Runner, SessionEventListener } from './runner-interface.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 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}\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 #events: SessionEvent[] = []\n #listeners = new Set<SessionEventListener>()\n #seq = 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 #totalCostUsd: number | undefined\n #numTurns: number | undefined\n #lastActivityAt: number | undefined\n #input = new InputQueue()\n #query: Query | undefined\n #capabilitiesEmitted = false\n #started = false\n #closed = false\n #runPromise: Promise<void> | undefined\n\n constructor(config: SessionRunnerConfig, id: string = randomUUID()) {\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.#config.cwd,\n profile: this.#config.profile,\n engine: 'claude',\n model: this.#model ?? this.#config.model,\n permissionMode: this.#permissionMode,\n apiKeySource: this.#apiKeySource,\n createdAt: this.createdAt,\n lastSeq: this.#seq,\n pendingPermissionCount: this.#pending.size,\n meta: this.#config.meta,\n title: this.#title(),\n totalCostUsd: this.#totalCostUsd,\n numTurns: this.#numTurns,\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 /** 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 sendMessage(text: string): void {\n if (this.#closed) throw new Error('session is closed')\n this.#input.push({\n type: 'user',\n message: { role: 'user', content: text },\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 uuid: randomUUID(),\n })\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 /**\n * Replay buffered events with seq > afterSeq, then deliver live events.\n * Returns an unsubscribe function.\n */\n subscribe(listener: SessionEventListener, afterSeq = 0): () => void {\n for (const event of this.#events) {\n if (event.seq > afterSeq) listener(event)\n }\n this.#listeners.add(listener)\n return () => this.#listeners.delete(listener)\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 and a context baseline\n // now so promptless sessions aren't blank until their first turn.\n if (!this.#config.prompt) {\n this.#setStatus('idle')\n void this.#fetchCapabilities()\n void this.#fetchContextUsage()\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: c.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 this.#emit({\n type: 'user_message',\n message: toApiMessage(m.message),\n parentToolUseId: m.parent_tool_use_id,\n replay: true,\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: c.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 includePartialMessages: c.includePartialMessages ?? 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.#setStatus('running')\n void this.#fetchCapabilities()\n void this.#fetchContextUsage()\n return\n }\n if (msg.type === 'system' && msg.subtype === 'session_state_changed') {\n // Authoritative turn-over signal — but a pending approval outranks it.\n if (this.#pending.size > 0) return\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 === '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.\n if (this.#pending.size === 0) this.#setStatus('idle')\n // Context usage moves every turn; the poll is a cheap control request.\n void this.#fetchContextUsage()\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: models.map((m) => ({\n value: m.value,\n displayName: m.displayName,\n description: m.description,\n })),\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 /** 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 #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 && this.#status === 'awaiting_approval') {\n 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 this.#events.push(event)\n for (const listener of this.#listeners) {\n try {\n listener(event)\n } catch {\n // Listener errors must not break the runner loop.\n }\n }\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 type {\n ContentBlock,\n CreateSessionRequest,\n PermissionMode,\n PermissionRequest,\n SessionEvent,\n SessionEventBody,\n SessionInfo,\n SessionStatus,\n ToolExecutionBackend,\n} from '@workerdeck/protocol'\nimport type { SandboxVfs } from '@workerdeck/sandbox'\nimport type {\n ParkedExecution,\n PermissionDecision,\n Runner,\n RunnerSnapshot,\n SessionEventListener,\n} from './runner-interface.ts'\nimport type { ToolExecutionCall, ToolExecutionResult, ToolExecutor } from './tool-executor.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 /** 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 #listeners = new Set<SessionEventListener>()\n #seq = 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 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 cwd: this.#config.cwd ?? process.cwd(),\n profile: this.#config.profile,\n engine: 'provider',\n model: this.#modelId(),\n permissionMode: this.#permissionMode,\n createdAt: this.createdAt,\n lastSeq: this.#seq,\n pendingPermissionCount: 0,\n meta: this.#config.meta,\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 mid-task: the prompt was consumed by the original run, and the\n // history is already a turn in progress. Waiting on its parked executions is\n // the whole point — the loop re-enters when one is settled.\n if (this.#pendingToolCalls.size === 0) this.#scheduleTurn()\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 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 const snapshot: RunnerSnapshot = {\n engine: 'provider',\n id: this.id,\n createdAt: this.createdAt,\n seq: this.#seq,\n events: [...this.#events],\n vfs: this.#config.vfs?.snapshot(),\n parked,\n state,\n }\n this.#parked = true\n this.#listeners.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 sendMessage(text: string): void {\n if (this.#parked) throw new Error('session is parked')\n if (this.#closed) throw new Error('session is closed')\n this.#messages.push({ role: 'user', content: text })\n this.#emit({\n type: 'user_message',\n message: { role: 'user', content: text },\n parentToolUseId: null,\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 subscribe(listener: SessionEventListener, afterSeq = 0): () => void {\n for (const event of this.#events) {\n if (event.seq > afterSeq) listener(event)\n }\n this.#listeners.add(listener)\n return () => this.#listeners.delete(listener)\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 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 // 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.\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 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 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 #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 this.#events.push(event)\n for (const listener of this.#listeners) {\n try {\n listener(event)\n } catch {\n // Listener errors must not break the runner loop.\n }\n }\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 './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 './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 const definitions = [...context.definitions]\n const tools: ToolSet = { ...context.tools }\n for (const [name, mcpTool] of Object.entries(mcpTools)) {\n if (context.sandboxedToolNames.includes(name)) {\n // A sandboxed name colliding with an MCP name would silently promote\n // untrusted execution to authoritative — refuse rather than guess.\n throw new Error(`MCP tool '${name}' collides with a sandboxed tool of the same name`)\n }\n definitions.push({ name, trust: 'authoritative', tool: mcpTool })\n tools[name] = mcpTool\n }\n return { ...context, tools, definitions }\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 { McpServerConfigWire, ProfileInfo, SessionCapability } from '@workerdeck/protocol'\nimport { createVfs } from '@workerdeck/sandbox'\nimport { AiSdkRunner, type AiSdkRunnerConfig } from './ai-sdk-runner.ts'\nimport { createToolContext, withMcpTools, type ToolContextOptions } from './tools.ts'\nimport type { ToolExecutor } from './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 /** 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 mcpTools?: ToolSet\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\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.\n const vfs = options.config.vfs ?? createVfs(options.config.restore?.vfs)\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 mcpTools = selectMcpTools(options.mcpTools, options.profile?.session?.mcpServers)\n const context = mcpTools ? withMcpTools(base, mcpTools) : base\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 })\n return runner\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 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 */\nexport async function connectMcpTools(\n servers: Record<string, McpServerConfigWire>,\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 options: { onError?: (name: string, error: unknown) => void } = {},\n): Promise<McpConnection> {\n const entries = Object.entries(servers)\n if (entries.length === 0) return { tools: {}, close: async () => {} }\n\n const { createMCPClient } = await import('@ai-sdk/mcp')\n const clients: Array<{ close: () => Promise<void> }> = []\n const tools: ToolSet = {}\n\n for (const [name, server] of entries) {\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 // 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(await client.tools())) {\n tools[`${name}__${toolName}`] = mcpTool as ToolSet[string]\n }\n } catch (error) {\n // One unreachable server must not take down the session; the agent simply\n // does not get those tools.\n options.onError?.(name, error)\n }\n }\n\n return {\n tools,\n close: async () => {\n await Promise.allSettled(clients.map((c) => c.close()))\n },\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"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["#done","#waiter","#buffer","DEFAULT_APPROVAL_TIMEOUT_MS","#config","#permissionMode","#status","#sdkSessionId","#seq","#apiKeySource","#pending","#model","#title","#totalCostUsd","#numTurns","#lastActivityAt","#started","#runPromise","#run","#closed","#input","#emit","#query","#settleApproval","#setStatus","#events","#listeners","sdkQuery","#backfillHistory","#buildOptions","#fetchCapabilities","#fetchContextUsage","#fetchRateLimits","#handleMessage","#canUseTool","#capabilitiesEmitted","#subscriptionType","#resolveQuestionByPolicy","#statusDetail","#config","#model","#permissionMode","#modelAlias","#restore","#seq","#events","#messages","#pendingToolCalls","#dispatched","#numTurns","#totalUsage","#turnAccum","#lastActivityAt","#status","#modelId","#title","#started","#turnChain","#scheduleTurn","#setStatus","#closed","#parked","#abort","#restingOnDeferred","#listeners","#emit","#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","#config","#permissionMode","#model","#reasoningEffort","#sdkSessionId","#status","#seq","#approvals","#resolvedModel","#title","#totalCostUsd","#numTurns","#lastActivityAt","#started","#turnChain","#backfillPending","#backfillHistory","#setStatus","#closed","#buildInput","#emit","#queue","#scheduleTurn","#imageDir","#settleApproval","#interruptTurn","#activeTurn","#connection","#events","#listeners","#runTurn","#ensureThread","#childEnv","#threadLoaded","#handleNotification","#answerServerRequest","#resolvedEffort","#resumedHistory","#replayTurns","#newTurnState","#replayingHistory","#handleItemCompleted","#finishTurn","#handleItemProgress","#emitDelta","#emitRateLimits","#requestApproval","#resolveQuestionByPolicy","#emitToolUse","#emitAssistant","#emitToolResult","#emitContextUsage","#planType"],"sources":["../src/attachments.ts","../src/input-queue.ts","../src/normalize.ts","../src/runner.ts","../src/ai-sdk-runner.ts","../src/claude-auth.ts","../src/quickjs-executor.ts","../src/pending-registry.ts","../src/browser-bridge-executor.ts","../src/deferred-executor.ts","../src/tools.ts","../src/web-fetch.ts","../src/engine.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 { McpServerStatus, SDKMessage } from '@anthropic-ai/claude-agent-sdk'\nimport type {\n ApiMessage,\n ContentBlock,\n McpServerStatusInfo,\n ModelOption,\n SessionEventBody,\n} from '@workerdeck/protocol'\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 return {\n type: 'user_message',\n message: toApiMessage(msg.message),\n parentToolUseId: msg.parent_tool_use_id,\n replay: 'isReplay' in msg && msg.isReplay === true ? true : undefined,\n synthetic: msg.isSynthetic === true ? true : undefined,\n uuid: msg.uuid,\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 '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 { randomUUID } from 'node:crypto'\nimport {\n getSessionMessages,\n query as sdkQuery,\n type CanUseTool,\n type Options,\n type PermissionResult,\n type Query,\n type SDKMessage,\n type SDKUserMessage,\n type SessionMessage,\n} from '@anthropic-ai/claude-agent-sdk'\nimport {\n ENGINE_CAPABILITIES,\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 './attachments.ts'\nimport { InputQueue } from './input-queue.ts'\nimport {\n type UsageRateLimits,\n defaultModelFromSdk,\n mcpStatusInfo,\n modelOptionsFromSdk,\n normalizeSdkMessage,\n rateLimitEventsFromUsage,\n toApiMessage,\n} from './normalize.ts'\nimport type { PermissionDecision, Runner, SessionEventListener } from './runner-interface.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 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}\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 #events: SessionEvent[] = []\n #listeners = new Set<SessionEventListener>()\n #seq = 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 #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 #started = false\n #closed = false\n #runPromise: Promise<void> | undefined\n\n constructor(config: SessionRunnerConfig, id: string = randomUUID()) {\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.#config.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 pendingPermissionCount: this.#pending.size,\n meta: this.#config.meta,\n title: this.#title(),\n totalCostUsd: this.#totalCostUsd,\n numTurns: this.#numTurns,\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 /** 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 /**\n * Replay buffered events with seq > afterSeq, then deliver live events.\n * Returns an unsubscribe function.\n */\n subscribe(listener: SessionEventListener, afterSeq = 0): () => void {\n for (const event of this.#events) {\n if (event.seq > afterSeq) listener(event)\n }\n this.#listeners.add(listener)\n return () => this.#listeners.delete(listener)\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: c.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 this.#emit({\n type: 'user_message',\n message: toApiMessage(m.message),\n parentToolUseId: m.parent_tool_use_id,\n replay: true,\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: c.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 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.#setStatus('running')\n void this.#fetchCapabilities()\n void this.#fetchContextUsage()\n void this.#fetchRateLimits()\n return\n }\n if (msg.type === 'system' && msg.subtype === 'session_state_changed') {\n // Authoritative turn-over signal — but a pending approval outranks it.\n if (this.#pending.size > 0) return\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 === '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.\n if (this.#pending.size === 0) this.#setStatus('idle')\n // Context usage moves every turn; the poll is a cheap control request.\n void this.#fetchContextUsage()\n void this.#fetchRateLimits()\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 /** 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 && this.#status === 'awaiting_approval') {\n 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 this.#events.push(event)\n for (const listener of this.#listeners) {\n try {\n listener(event)\n } catch {\n // Listener errors must not break the runner loop.\n }\n }\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 type ContentBlock,\n type CreateSessionRequest,\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 './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 './tool-executor.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 /** 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 #listeners = new Set<SessionEventListener>()\n #seq = 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 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 cwd: this.#config.cwd ?? process.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 pendingPermissionCount: 0,\n meta: this.#config.meta,\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 mid-task: the prompt was consumed by the original run, and the\n // history is already a turn in progress. Waiting on its parked executions is\n // the whole point — the loop re-enters when one is settled.\n if (this.#pendingToolCalls.size === 0) this.#scheduleTurn()\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 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 const snapshot: RunnerSnapshot = {\n engine: 'provider',\n id: this.id,\n createdAt: this.createdAt,\n seq: this.#seq,\n events: [...this.#events],\n vfs: this.#config.vfs?.snapshot(),\n parked,\n state,\n }\n this.#parked = true\n this.#listeners.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 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 subscribe(listener: SessionEventListener, afterSeq = 0): () => void {\n for (const event of this.#events) {\n if (event.seq > afterSeq) listener(event)\n }\n this.#listeners.add(listener)\n return () => this.#listeners.delete(listener)\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 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 // 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.\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 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 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 #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 this.#events.push(event)\n for (const listener of this.#listeners) {\n try {\n listener(event)\n } catch {\n // Listener errors must not break the runner loop.\n }\n }\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 './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 './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 const definitions = [...context.definitions]\n const tools: ToolSet = { ...context.tools }\n for (const [name, mcpTool] of Object.entries(mcpTools)) {\n if (context.sandboxedToolNames.includes(name)) {\n // A sandboxed name colliding with an MCP name would silently promote\n // untrusted execution to authoritative — refuse rather than guess.\n throw new Error(`MCP tool '${name}' collides with a sandboxed tool of the same name`)\n }\n definitions.push({ name, trust: 'authoritative', tool: mcpTool })\n tools[name] = mcpTool\n }\n return { ...context, tools, definitions }\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 { McpServerConfigWire, ProfileInfo, SessionCapability } from '@workerdeck/protocol'\nimport { createVfs } from '@workerdeck/sandbox'\nimport { AiSdkRunner, type AiSdkRunnerConfig } from './ai-sdk-runner.ts'\nimport { createToolContext, withMcpTools, type ToolContextOptions } from './tools.ts'\nimport type { ToolExecutor } from './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 /** 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 mcpTools?: ToolSet\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\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.\n const vfs = options.config.vfs ?? createVfs(options.config.restore?.vfs)\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 mcpTools = selectMcpTools(options.mcpTools, options.profile?.session?.mcpServers)\n const context = mcpTools ? withMcpTools(base, mcpTools) : base\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 })\n return runner\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 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 */\nexport async function connectMcpTools(\n servers: Record<string, McpServerConfigWire>,\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 options: { onError?: (name: string, error: unknown) => void } = {},\n): Promise<McpConnection> {\n const entries = Object.entries(servers)\n if (entries.length === 0) return { tools: {}, close: async () => {} }\n\n const { createMCPClient } = await import('@ai-sdk/mcp')\n const clients: Array<{ close: () => Promise<void> }> = []\n const tools: ToolSet = {}\n\n for (const [name, server] of entries) {\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 // 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(await client.tools())) {\n tools[`${name}__${toolName}`] = mcpTool as ToolSet[string]\n }\n } catch (error) {\n // One unreachable server must not take down the session; the agent simply\n // does not get those tools.\n options.onError?.(name, error)\n }\n }\n\n return {\n tools,\n close: async () => {\n await Promise.allSettled(clients.map((c) => c.close()))\n },\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 '../../claude-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 }) {\n if (restore) throw new Error('the Claude engine cannot rebuild a parked session')\n return new SessionRunner(config)\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 { randomUUID } from 'node:crypto'\nimport { mkdirSync, rmSync, writeFileSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport {\n ENGINE_CAPABILITIES,\n PROTOCOL_VERSION,\n type ContentBlock,\n type CreateSessionRequest,\n type PermissionDecisionSource,\n type PermissionMode,\n type PermissionRequest,\n type SessionEvent,\n type SessionEventBody,\n type SessionInfo,\n type SessionStatus,\n type UserQuestion,\n} from '@workerdeck/protocol'\nimport {\n attachmentKind,\n attachmentRef,\n normalizeMediaType,\n type AttachmentInput,\n} from '../../attachments.ts'\nimport type { PermissionDecision, Runner, SessionEventListener } from '../../runner-interface.ts'\nimport { JsonRpcError } from './jsonrpc.ts'\nimport type {\n AppServerCommandApprovalParams,\n AppServerConnection,\n AppServerConnectFn,\n AppServerElicitationParams,\n AppServerFileChangeApprovalParams,\n AppServerHistoryTurn,\n AppServerItem,\n AppServerPermissionsApprovalParams,\n AppServerPlanUpdate,\n AppServerRateLimits,\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 * 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/** The text of a history `userMessage` item: its content entries' text parts\n * joined. Image parts have no replayable representation (the bytes went to the\n * model, not into the rollout we can render from) and are skipped. */\nfunction historyUserText(item: AppServerUserMessageItem): string {\n if (!Array.isArray(item.content)) return ''\n return item.content\n .map((part) => {\n const candidate = part as { type?: string; text?: unknown } | null\n return candidate?.type === 'text' && typeof candidate.text === 'string' ? candidate.text : ''\n })\n .filter(Boolean)\n .join('\\n')\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 #events: SessionEvent[] = []\n #listeners = new Set<SessionEventListener>()\n #seq = 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\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 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.#config.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 pendingPermissionCount: this.#approvals.size,\n meta: this.#config.meta,\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 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 return this.#turnChain\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 subscribe(listener: SessionEventListener, afterSeq = 0): () => void {\n for (const event of this.#events) {\n if (event.seq > afterSeq) listener(event)\n }\n this.#listeners.add(listener)\n return () => this.#listeners.delete(listener)\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.#config.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 return connection\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.#config.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 const active = this.#activeTurn\n switch (method) {\n case 'thread/started': {\n const thread = (params as { thread?: { id?: string } })?.thread\n if (typeof thread?.id === 'string') this.#sdkSessionId = thread.id\n return\n }\n case 'turn/started': {\n const turn = (params as { turn?: AppServerTurn })?.turn\n if (active && turn && !active.turnId) active.turnId = turn.id\n return\n }\n case 'turn/completed': {\n const turn = (params as { turn?: AppServerTurn })?.turn\n if (active && turn) active.resolve(turn)\n return\n }\n case 'item/started':\n case 'item/updated': {\n if (!active) return\n const item = (params as { item?: AppServerItem })?.item\n if (item) this.#handleItemProgress(item, active)\n return\n }\n case 'item/completed': {\n if (!active) return\n const item = (params as { item?: AppServerItem })?.item\n if (item) this.#handleItemCompleted(item, active)\n return\n }\n case 'item/agentMessage/delta': {\n if (!active) 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 return\n }\n case 'item/reasoning/textDelta':\n case 'item/reasoning/summaryTextDelta': {\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 // Section boundaries (a new summary/content entry) render as paragraph\n // breaks — the completed item joins sections with '\\n\\n' too.\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 return\n }\n case 'thread/tokenUsage/updated': {\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 return\n }\n case 'account/rateLimits/updated': {\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 return\n }\n case 'turn/plan/updated': {\n // v2's todo list, published as the codex.todo_list sdk_event payload\n // both clients already render.\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 return\n }\n case 'serverRequest/resolved': {\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 return\n }\n case 'error': {\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 error = (params as { error?: { message?: string } })?.error\n if (active && typeof error?.message === 'string') active.lastError = error.message\n return\n }\n default:\n // The app-server surface is wide (mcpServer/*, account/*, thread\n // housekeeping…) — everything unmapped is deliberately dropped.\n return\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 }\n }\n\n #handleItemCompleted(item: AppServerItem, active: ActiveTurn): void {\n const id = `${active.nonce}:${item.id}`\n switch (item.type) {\n case 'userMessage':\n // The echo of our own turn/start input — already in the log.\n return\n case 'agentMessage': {\n const text = typeof item.text === 'string' ? item.text : ''\n this.#emitAssistant(id, [{ type: 'text', text }])\n active.finalText = text\n return\n }\n case 'reasoning': {\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 return\n }\n case 'commandExecution': {\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 return\n }\n case 'fileChange': {\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 this.#emitToolResult(\n id,\n lines.join('\\n') || item.status,\n item.status === 'failed' || item.status === 'declined',\n )\n return\n }\n case 'mcpToolCall': {\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 return\n }\n case 'webSearch':\n this.#emitToolUse(id, 'CodexWebSearch', { query: item.query })\n this.#emitToolResult(id, '', false)\n return\n default: {\n const unknown = item as AppServerUnknownItem\n this.#emit({ type: 'sdk_event', payload: { type: `codex.${unknown.type}`, item: unknown } })\n }\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(toolUseId: string, content: string, isError: boolean): 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 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 this.#events.push(event)\n for (const listener of this.#listeners) {\n try {\n listener(event)\n } catch {\n // Listener errors must not break the runner loop.\n }\n }\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 }) {\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 ...config,\n codexHome: profile?.codexHome,\n connectFn: (options) => connectAppServer({ executable, ...options }),\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 '../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\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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"}