@workerdeck/core 0.6.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.
- package/LICENSE +21 -0
- package/README.md +140 -0
- package/build/index.d.mts +848 -0
- package/build/index.mjs +2324 -0
- package/build/index.mjs.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +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(/ /g, ' ')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'|'/g, \"'\")\n .replace(/&/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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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@workerdeck/core",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "The WorkerDeck session runner: wraps the Agent SDK's query() with a push-based input queue, promotes canUseTool calls into pending approvals, normalizes SDKMessages into wire-protocol events, and keeps a seq-numbered event log for attach/replay. Pure library, no transport.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./build/index.mjs",
|
|
8
|
+
"types": "./build/index.d.mts",
|
|
9
|
+
"files": [
|
|
10
|
+
"build"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"@workerdeck/source": "./src/index.ts",
|
|
15
|
+
"types": "./build/index.d.mts",
|
|
16
|
+
"default": "./build/index.mjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@ai-sdk/anthropic": "^4.0.22",
|
|
21
|
+
"@ai-sdk/mcp": "^2.0.17",
|
|
22
|
+
"@ai-sdk/moonshotai": "^3.0.18",
|
|
23
|
+
"@ai-sdk/openai": "^4.0.21",
|
|
24
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.217",
|
|
25
|
+
"ai": "^7.0.38",
|
|
26
|
+
"@workerdeck/protocol": "0.6.0",
|
|
27
|
+
"@workerdeck/sandbox": "0.6.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@jitl/quickjs-ng-wasmfile-release-asyncify": "^0.31.0",
|
|
31
|
+
"@types/node": "^22.10.0",
|
|
32
|
+
"rimraf": "^6.1.3",
|
|
33
|
+
"tsdown": "^0.21.10",
|
|
34
|
+
"vitest": "^3.2.0",
|
|
35
|
+
"zod": "^4.4.3"
|
|
36
|
+
},
|
|
37
|
+
"author": "Tobias Strebitzer",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/workerdeck/workerdeck.git",
|
|
41
|
+
"directory": "packages/core"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://workerdeck.github.io/workerdeck/",
|
|
44
|
+
"bugs": "https://github.com/workerdeck/workerdeck/issues",
|
|
45
|
+
"keywords": [
|
|
46
|
+
"claude",
|
|
47
|
+
"claude-code",
|
|
48
|
+
"anthropic",
|
|
49
|
+
"agent",
|
|
50
|
+
"agent-sdk",
|
|
51
|
+
"session",
|
|
52
|
+
"runner"
|
|
53
|
+
],
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"clean": "rimraf build",
|
|
59
|
+
"build": "tsdown",
|
|
60
|
+
"typecheck": "tsgo -p tsconfig.json",
|
|
61
|
+
"test": "vitest run"
|
|
62
|
+
}
|
|
63
|
+
}
|