@tanstack/ai-claude-code 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { BaseTextAdapter, StructuredOutputOptions, StructuredOutputResult } from '@tanstack/ai/adapters';
2
2
  import { SandboxHandle } from '@tanstack/ai-sandbox';
3
- import { DefaultMessageMetadataByModality, Modality, StreamChunk, TextOptions } from '@tanstack/ai';
3
+ import { DefaultMessageMetadataByModality, Modality, AdapterYieldChunk, TextOptions } from '@tanstack/ai';
4
4
  import { ClaudeCodeModel } from '../model-meta.js';
5
5
  import { ClaudeCodeTextProviderOptions } from '../provider-options.js';
6
6
  export type ClaudeCodePermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan';
@@ -64,7 +64,7 @@ export declare class ClaudeCodeTextAdapter<TModel extends ClaudeCodeModel> exten
64
64
  * records an approval-requested event and denies (the client re-runs to grant).
65
65
  */
66
66
  private buildPermissionResolver;
67
- chatStream(options: TextOptions<ClaudeCodeTextProviderOptions>): AsyncIterable<StreamChunk>;
67
+ chatStream(options: TextOptions<ClaudeCodeTextProviderOptions>): AsyncIterable<AdapterYieldChunk>;
68
68
  structuredOutput(_options: StructuredOutputOptions<ClaudeCodeTextProviderOptions>): Promise<StructuredOutputResult<unknown>>;
69
69
  }
70
70
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":[],"sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { EventType, normalizeSystemPrompts } from '@tanstack/ai'\nimport {\n appendOutputSchemaInstruction,\n toRunErrorRawEvent,\n} from '@tanstack/ai/adapter-internals'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport {\n SandboxCapability,\n alignedIfAttaching,\n approvalId,\n buildApprovalRequestedEvent,\n createBridgeEventChannel,\n createRunScopedIdGen,\n encodeRunId,\n getSandbox,\n getSandboxDurability,\n getSandboxPolicy,\n getToolBridgeProvisioner,\n getWorkspaceProjection,\n journalOptionsFor,\n mergeChunkStreams,\n nodeHttpBridgeProvisioner,\n resolveApproval,\n resolveDurableRunId,\n resolveDurableThreadId,\n spawnNdjson,\n} from '@tanstack/ai-sandbox'\nimport { buildPrompt } from '../messages/prompt'\nimport { translateSdkStream } from '../stream/translate'\nimport { mapPolicyToClaudeFlags } from './policy-map'\nimport { projectClaudeWorkspace } from './projection'\nimport {\n CLAUDE_JSON_SCHEMA_PLACEHOLDER,\n CLAUDE_RUNNER_SOURCE,\n} from './claude-run-source'\n\nimport type { ClaudePolicyFlags } from './policy-map'\nimport type {\n BridgeEventChannel,\n HostToolBridge,\n PermissionToolResult,\n SandboxHandle,\n SandboxPolicy,\n} from '@tanstack/ai-sandbox'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type {\n DefaultMessageMetadataByModality,\n Modality,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type { ClaudeCodeModel } from '../model-meta'\nimport type { ClaudeCodeTextProviderOptions } from '../provider-options'\nimport type { AgentSdkMessage } from '../stream/sdk-types'\n\nexport type ClaudeCodePermissionMode =\n | 'default'\n | 'acceptEdits'\n | 'bypassPermissions'\n | 'plan'\n\nconst DEFAULT_WORKDIR = '/workspace'\n\nexport interface ClaudeCodeTextConfig {\n /**\n * Working directory inside the sandbox where `claude` runs. Defaults to\n * `/workspace` (the conventional sandbox workspace root).\n */\n cwd?: string\n /**\n * Claude Code permission mode passed via `--permission-mode`. Defaults to\n * `'bypassPermissions'` — a sandbox is isolated, so the agent is allowed to\n * edit files and run commands without prompting. Tighten via `defineSandboxPolicy`\n * / this option for less autonomy.\n */\n permissionMode?: ClaudeCodePermissionMode\n /** Built-in tools the harness may use (`--allowedTools`). */\n allowedTools?: Array<string>\n /** Built-in tools removed from the harness (`--disallowedTools`). */\n disallowedTools?: Array<string>\n /** Extra directories the agent may access (`--add-dir`). */\n addDirs?: Array<string>\n /** Maximum harness-internal turns (`--max-turns`). */\n maxTurns?: number\n /**\n * How `systemPrompts` from `chat()` are applied:\n * - `'append'` (default): `--append-system-prompt` on top of the preset.\n * - `'replace'`: `--system-prompt` as the entire system prompt.\n */\n systemPromptMode?: 'append' | 'replace'\n /** Path/name of the claude executable inside the sandbox. Defaults to `claude`. */\n claudeExecutable?: string\n /** Emit token-level deltas via `--include-partial-messages` (default true). */\n streamPartials?: boolean\n /** Extra environment variables for the claude process inside the sandbox. */\n env?: Record<string, string>\n /**\n * `'api-key'` (default) injects `ANTHROPIC_API_KEY`.\n * `'host'` uses `claude login` and does not inject the key.\n * Not inferred from the sandbox.\n */\n authMode?: 'host' | 'api-key'\n /** Emit a `file.changed` CUSTOM event with the git diff after the run (default true). */\n emitDiff?: boolean\n}\n\n/** POSIX single-quote escape for embedding values in the `claude …` command. */\nfunction q(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`\n}\n\n/** Copy host Anthropic auth into the sandbox process. Docker `exec` Env replaces the container env, so a key set only at create time can vanish. */\nfunction hostClaudeAuthEnv(): Record<string, string> {\n const env: Record<string, string> = {}\n const apiKey = process.env.ANTHROPIC_API_KEY\n if (apiKey) env.ANTHROPIC_API_KEY = apiKey\n const authToken = process.env.ANTHROPIC_AUTH_TOKEN\n if (authToken) env.ANTHROPIC_AUTH_TOKEN = authToken\n return env\n}\n\n/**\n * Windows Node often has USERPROFILE but no HOME. Claude then cannot find\n * `~/.claude.json` (the `claude login` file) and prints \"Not logged in\".\n */\nfunction localProcessHomeEnv(provider: string): Record<string, string> {\n if (provider !== 'local-process') return {}\n if (process.env.HOME) return {}\n const home = process.env.USERPROFILE\n return home ? { HOME: home } : {}\n}\n\n/** Format a host tool-bridge as claude's `--mcp-config` JSON. */\nfunction bridgeToMcpConfig(bridge: HostToolBridge): string {\n return JSON.stringify({\n mcpServers: {\n [bridge.name]: {\n type: 'http',\n url: bridge.url,\n headers: { Authorization: `Bearer ${bridge.token}` },\n },\n },\n })\n}\n\nexport class ClaudeCodeTextAdapter<\n TModel extends ClaudeCodeModel,\n> extends BaseTextAdapter<\n TModel,\n ClaudeCodeTextProviderOptions,\n ReadonlyArray<Modality> & readonly ['text'],\n DefaultMessageMetadataByModality,\n ReadonlyArray<string>,\n unknown,\n never\n> {\n readonly name = 'claude-code' as const\n\n // Harness adapter: requires a sandbox to run the agent CLI inside.\n override readonly requires = [SandboxCapability] as const\n\n private readonly adapterConfig: ClaudeCodeTextConfig\n\n constructor(config: ClaudeCodeTextConfig, model: TModel) {\n super({}, model)\n this.adapterConfig = config\n }\n\n private sandboxFrom(\n options: TextOptions<ClaudeCodeTextProviderOptions>,\n ): SandboxHandle {\n const ctx = options.capabilities\n if (!ctx) {\n throw new Error(\n 'Adapter \"claude-code\" requires a sandbox. Add withSandbox(defineSandbox({ ... })) ' +\n 'to chat() middleware (e.g. with the local-process or docker provider).',\n )\n }\n return getSandbox(ctx)\n }\n\n private workdir(options: TextOptions<ClaudeCodeTextProviderOptions>): string {\n return (\n options.modelOptions?.cwd ?? this.adapterConfig.cwd ?? DEFAULT_WORKDIR\n )\n }\n\n supportsCombinedToolsAndSchema(): boolean {\n return true\n }\n\n combinedStructuredOutputSource(): 'event' {\n return 'event'\n }\n\n /** Build the `claude` command line (prompt goes via stdin, not argv). */\n private buildArgv(\n options: TextOptions<ClaudeCodeTextProviderOptions>,\n resume: string | undefined,\n policyFlags: ClaudePolicyFlags,\n mcpConfigPath: string | undefined,\n permissionPromptTool: string | undefined,\n hasJsonSchema: boolean,\n ): Array<string> {\n const config = this.adapterConfig\n const modelOptions = options.modelOptions\n const exeParts = (config.claudeExecutable ?? 'claude').split(' ')\n\n // `--setting-sources user` before `-p`. Do not pass `--bare`: that flag\n // skips stored `claude login` credentials and prints\n // \"Not logged in · Please run /login\" (claude-code#51047).\n // `-p` can take the next token as the prompt, so these flags stay first.\n const args: Array<string> = [\n ...exeParts,\n '--setting-sources',\n 'user',\n '-p',\n '--output-format',\n 'stream-json',\n '--verbose',\n '--model',\n this.model,\n ]\n\n if (config.streamPartials !== false) args.push('--include-partial-messages')\n if (resume !== undefined) args.push('--resume', resume)\n\n const permissionMode =\n modelOptions?.permissionMode ??\n config.permissionMode ??\n policyFlags.permissionMode ??\n 'bypassPermissions'\n args.push('--permission-mode', permissionMode)\n\n const maxTurns = modelOptions?.maxTurns ?? config.maxTurns\n if (maxTurns !== undefined) args.push('--max-turns', String(maxTurns))\n\n for (const dir of config.addDirs ?? []) args.push('--add-dir', dir)\n\n const allowedTools = [\n ...(modelOptions?.allowedTools ?? config.allowedTools ?? []),\n ...policyFlags.allowedTools,\n ]\n if (allowedTools.length > 0) {\n args.push('--allowedTools', [...new Set(allowedTools)].join(','))\n }\n const disallowedTools = [\n ...(modelOptions?.disallowedTools ?? config.disallowedTools ?? []),\n ...policyFlags.disallowedTools,\n ]\n if (disallowedTools.length > 0) {\n args.push('--disallowedTools', [...new Set(disallowedTools)].join(','))\n }\n\n const systemPrompts = normalizeSystemPrompts(options.systemPrompts)\n .map((prompt) => prompt.content)\n .filter((content) => content.trim() !== '')\n if (systemPrompts.length > 0) {\n const joined = systemPrompts.join('\\n\\n')\n const flag =\n config.systemPromptMode === 'replace'\n ? '--system-prompt'\n : '--append-system-prompt'\n args.push(flag, joined)\n }\n\n if (mcpConfigPath !== undefined) args.push('--mcp-config', mcpConfigPath)\n if (hasJsonSchema) {\n args.push('--json-schema', CLAUDE_JSON_SCHEMA_PLACEHOLDER)\n }\n if (permissionPromptTool !== undefined) {\n args.push('--permission-prompt-tool', permissionPromptTool)\n }\n\n return args\n }\n\n /**\n * Build the permission-prompt resolver the host MCP bridge exposes to claude\n * (`--permission-prompt-tool`). Maps claude's permission request onto the\n * sandbox policy + client approvals; on an `ask` action with no decision yet,\n * records an approval-requested event and denies (the client re-runs to grant).\n */\n private buildPermissionResolver(\n policy: SandboxPolicy | undefined,\n approvals: ReadonlyMap<string, boolean> | undefined,\n scripts: Record<string, string> | undefined,\n sink: Array<StreamChunk>,\n threadId: string,\n runId: string,\n ): (input: { tool_name?: string; input?: unknown }) => PermissionToolResult {\n const writeTools = new Set(['Write', 'Edit', 'MultiEdit', 'NotebookEdit'])\n const networkTools = new Set(['WebFetch', 'WebSearch'])\n return (request) => {\n const toolName = request.tool_name ?? 'tool'\n const cmdInput = request.input\n const command =\n toolName === 'Bash' &&\n cmdInput !== null &&\n typeof cmdInput === 'object' &&\n 'command' in cmdInput &&\n typeof (cmdInput as { command?: unknown }).command === 'string'\n ? (cmdInput as { command: string }).command\n : undefined\n const capability = writeTools.has(toolName)\n ? 'fileWrite'\n : networkTools.has(toolName)\n ? 'network'\n : undefined\n const id = approvalId({\n provider: 'claude-code',\n kind: command !== undefined ? 'command' : (capability ?? 'tool'),\n target: command ?? toolName,\n })\n const outcome = resolveApproval({\n policy,\n approvals,\n id,\n scripts,\n ...(command !== undefined ? { command } : {}),\n ...(capability !== undefined ? { capability } : {}),\n })\n if (outcome.needsApproval) {\n sink.push(\n buildApprovalRequestedEvent({\n approvalId: id,\n title: `Approve ${toolName}${command !== undefined ? `: ${command}` : ''}`,\n threadId,\n runId,\n detail: { provider: 'claude-code', toolName },\n }),\n )\n return {\n behavior: 'deny',\n message:\n 'Awaiting client approval. Approve in the UI and re-run to continue.',\n }\n }\n return outcome.decision === 'allow'\n ? { behavior: 'allow' }\n : { behavior: 'deny', message: 'Denied by sandbox policy.' }\n }\n }\n\n async *chatStream(\n options: TextOptions<ClaudeCodeTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const { logger } = options\n let bridge: HostToolBridge | undefined\n let channel: BridgeEventChannel | undefined\n const approvalRequests: Array<StreamChunk> = []\n // Temp files written for the run (bridge MCP config, redirected prompt) that\n // carry the bearer token / prompt; removed in `finally` so they don't linger\n // in the sandbox after the run.\n let cleanupSandbox: SandboxHandle | undefined\n const tempFiles: Array<string> = []\n try {\n const sandbox = this.sandboxFrom(options)\n cleanupSandbox = sandbox\n const cwd = this.workdir(options)\n\n // Durability comes from `withSandbox(sandbox, { runs, durability })`, read\n // back off the capability bus. Absent it, everything below resolves to\n // exactly today's behavior (no journal option, no alignment, and a\n // generated `runId` when the caller didn't supply one).\n const durability = options.capabilities\n ? getSandboxDurability(options.capabilities, { optional: true })\n : undefined\n // The journaled path below derives its journal path and its\n // message-id generator from `runId`. A resuming host must recompute\n // the same `runId` to find the same journal file and reproduce the\n // same translated ids — that's only possible when the caller supplies\n // `runId` explicitly. `resolveDurableRunId` enforces that loudly (via\n // `DurableRunIdRequiredError`) whenever durability is wired, and falls\n // back to a fresh `this.generateId()` otherwise, preserving today's\n // behavior for non-durable runs.\n const runId = resolveDurableRunId(options.runId, {\n durable: durability !== undefined,\n adapter: 'claude-code',\n fallback: () => this.generateId(),\n })\n // `threadId` is stamped on every chunk `translateSdkStream` emits, so an\n // ATTACHING run that mints a fresh one replays a stream the stored log\n // cannot match at index 0. `resolveDurableThreadId` refuses that up front\n // instead of letting alignment discover it mid-stream; a durable FRESH run\n // and a non-durable run both keep the generated fallback untouched.\n const threadId = resolveDurableThreadId(options.threadId, {\n durable: durability !== undefined,\n attaching: durability?.attach === true,\n adapter: 'claude-code',\n fallback: () => this.generateId(),\n })\n // Surfaces custom events from bridged tools (e.g. code mode console logs)\n // on this run's live output stream.\n channel = createBridgeEventChannel({ model: this.model, threadId, runId })\n\n // Idempotently project workspace skills/plugins/MCP into the sandbox in\n // claude's native format (guarded by the projection marker file).\n const projection = options.capabilities\n ? getWorkspaceProjection(options.capabilities, { optional: true })\n : undefined\n if (projection) await projectClaudeWorkspace(sandbox, projection)\n\n const policy = options.capabilities\n ? getSandboxPolicy(options.capabilities, { optional: true })\n : undefined\n\n // A permission-prompt tool gates the agent's native tools when a policy\n // can `ask`/`deny` (interactive approvals).\n const permission =\n policy !== undefined\n ? {\n toolName: 'approval_prompt',\n resolve: this.buildPermissionResolver(\n policy,\n options.approvals,\n projection?.scripts,\n approvalRequests,\n threadId,\n runId,\n ),\n }\n : undefined\n\n // Bridge chat()-provided server tools (and/or the permission tool) into\n // the sandbox over MCP.\n const hasTools = options.tools !== undefined && options.tools.length > 0\n if (hasTools || permission !== undefined) {\n const provisioner =\n (options.capabilities\n ? getToolBridgeProvisioner(options.capabilities, { optional: true })\n : undefined) ?? nodeHttpBridgeProvisioner\n bridge = await provisioner.provision(options.tools ?? [], {\n provider: sandbox.provider,\n context: options.context,\n emitCustomEvent: channel.emitCustomEvent,\n ...(permission !== undefined ? { permission } : {}),\n ...(options.abortController?.signal\n ? { signal: options.abortController.signal }\n : {}),\n })\n }\n\n const built = buildPrompt(\n options.messages,\n options.modelOptions?.sessionId,\n )\n const resume = built.resume\n const prompt =\n options.outputSchema !== undefined\n ? appendOutputSchemaInstruction(built.prompt, options.outputSchema)\n : built.prompt\n // Both files below name themselves after `runId`, and durability makes\n // `runId` CALLER-chosen. Raw, a `/` in it would silently turn each basename\n // into a nested path (writing outside the intended directory, or failing on\n // one that does not exist), `..` would climb out of that directory, and a\n // long id would fail the spawn with `ENAMETOOLONG`. `encodeRunId` collapses\n // any id to one bounded, injective path segment — the same encoder\n // `journalPaths` uses, so these files and the journal agree on how a given\n // id spells. Computed once so the two filenames cannot drift apart.\n const runIdSegment = encodeRunId(runId)\n\n // The bridge MCP config carries the per-run bearer token. Write it to a\n // file and pass claude the PATH, so the token never appears in argv (where\n // any process in the sandbox could read it via `ps` / `/proc/<pid>/cmdline`).\n let mcpConfigArg: string | undefined\n if (bridge) {\n // Pass claude a path RELATIVE to its cwd (the real workdir the handle\n // runs the process in). An absolute VIRTUAL path like `/workspace/…` is\n // wrong wherever claude runs outside a sandbox that literally uses\n // `/workspace` — e.g. local-process on Windows, where git-bash resolves\n // `/workspace` to `C:\\Program Files\\Git\\workspace` and the file is \"not\n // found\". The bare filename resolves correctly on every provider.\n const mcpConfigFile = `.tanstack-mcp-bridge-${runIdSegment}.json`\n const mcpConfigPath = `${cwd}/${mcpConfigFile}`\n await sandbox.fs.write(mcpConfigPath, bridgeToMcpConfig(bridge))\n tempFiles.push(mcpConfigPath)\n mcpConfigArg = mcpConfigFile\n }\n let jsonSchemaFile: string | undefined\n if (options.outputSchema !== undefined) {\n jsonSchemaFile = `tanstack-output-schema-${runIdSegment}.json`\n const schemaPath = `${cwd}/${jsonSchemaFile}`\n await sandbox.fs.write(schemaPath, JSON.stringify(options.outputSchema))\n tempFiles.push(schemaPath)\n }\n const runnerFile = `tanstack-claude-run-${runIdSegment}.mjs`\n await sandbox.fs.write(`${cwd}/${runnerFile}`, CLAUDE_RUNNER_SOURCE)\n tempFiles.push(`${cwd}/${runnerFile}`)\n const argv = this.buildArgv(\n options,\n resume,\n mapPolicyToClaudeFlags(policy),\n mcpConfigArg,\n bridge && permission\n ? `mcp__${bridge.name}__${permission.toolName}`\n : undefined,\n jsonSchemaFile !== undefined,\n )\n // Filenames only on the shell line. JSON (schema, system prompt, MCP\n // config path) lives in the argv file so git-bash cannot retokenize it.\n const argvFile = `tanstack-claude-argv-${runIdSegment}.json`\n await sandbox.fs.write(`${cwd}/${argvFile}`, JSON.stringify(argv))\n tempFiles.push(`${cwd}/${argvFile}`)\n const command =\n jsonSchemaFile === undefined\n ? `node ${q(runnerFile)} ${q(argvFile)}`\n : `node ${q(runnerFile)} ${q(argvFile)} ${q(jsonSchemaFile)}`\n\n // Deliver the prompt. The default feeds it over stdin (keeps it out of\n // argv). Providers without a writable host→process stdin (e.g. Cloudflare)\n // can't accept that write, so write the prompt to a file and redirect the\n // CLI's stdin from it in-shell (`claude -p … < file`) — still out of argv.\n let runCommand = command\n let stdinInput: string | undefined = prompt\n if (sandbox.capabilities.writableStdin === false) {\n const promptPath = `/tmp/tanstack-claude-prompt-${runIdSegment}`\n await sandbox.fs.write(promptPath, prompt)\n tempFiles.push(promptPath)\n runCommand = `${command} < ${q(promptPath)}`\n stdinInput = undefined\n }\n\n logger.request(\n `activity=chat provider=claude-code model=${this.model} sandbox=${sandbox.provider} messages=${options.messages.length} resume=${resume ?? 'none'}`,\n { provider: 'claude-code', model: this.model },\n )\n\n const authMode =\n options.modelOptions?.authMode ??\n this.adapterConfig.authMode ??\n 'api-key'\n const injectApiKey = authMode === 'api-key'\n\n const journalOptions = journalOptionsFor(durability, runId)\n const rawEvents = spawnNdjson(sandbox, runCommand, {\n cwd,\n ...(stdinInput !== undefined ? { input: stdinInput } : {}),\n // Isolated sandboxes often run as root. Claude refuses\n // `--dangerously-skip-permissions` as root unless IS_SANDBOX=1.\n // CLAUDE_CODE_SANDBOXED marks a real isolation boundary. Do not set\n // either on local-process: that provider runs on the host.\n env: {\n ...(sandbox.provider === 'local-process'\n ? {}\n : {\n IS_SANDBOX: '1',\n CLAUDE_CODE_SANDBOXED: '1',\n }),\n ...(injectApiKey ? hostClaudeAuthEnv() : {}),\n ...localProcessHomeEnv(sandbox.provider),\n ...this.adapterConfig.env,\n },\n ...(options.abortController?.signal\n ? { signal: options.abortController.signal }\n : options.request?.signal\n ? { signal: options.request.signal }\n : {}),\n onNonJsonLine: (line) =>\n logger.provider(`provider=claude-code non-json line: ${line}`, {\n chunk: line,\n }),\n // Journal + attach both come from the sandbox durability capability, so\n // the attach route configures takeover by passing `attach: true` to\n // `withSandbox` — `chat()` stays free of sandbox vocabulary. Omitted\n // entirely (not passed as `undefined`) when the run isn't durable, so\n // `spawnNdjson` takes its original, unjournaled path byte-for-byte.\n ...(journalOptions === undefined ? {} : { journal: journalOptions }),\n })\n\n async function* asMessages(): AsyncIterable<AgentSdkMessage> {\n for await (const event of rawEvents) yield event as AgentSdkMessage\n }\n\n // `mergeChunkStreams` splices `channel.stream` (host-tool-bridge CUSTOM\n // events from LIVE tool execution) into the deterministic translator\n // output. Those events do not occur on a replay, so a takeover's replay is\n // NOT chunk-for-chunk identical to what the log holds. `alignedIfAttaching`\n // handles it: alignment skips stored out-of-band CUSTOM entries within a\n // bounded window (see `align.ts`), so a bridged-tool run can be taken over\n // without a spurious `JournalReplayDivergedError` — while a genuine\n // determinism regression still throws. The wrap goes OUTSIDE the merge:\n // the stored log holds the previous host's merged output, so aligning the\n // pre-merge translated stream alone would compare against a log it never\n // produced. On a non-attaching run this is a pure passthrough (see\n // `alignedIfAttaching`'s `attach` guard).\n yield* alignedIfAttaching(\n mergeChunkStreams(\n translateSdkStream(asMessages(), {\n model: this.model,\n runId,\n threadId,\n ...(options.parentRunId !== undefined && {\n parentRunId: options.parentRunId,\n }),\n // Deterministic on the journaled path: two translations of the same\n // journal prefix from a fresh generator seeded with the same\n // `runId` mint the same message ids (unlike `this.generateId()`,\n // which mixes in `Date.now()` / `Math.random()`). See\n // `createRunScopedIdGen` in `@tanstack/ai-sandbox`.\n genId: createRunScopedIdGen(runId),\n ...(options.outputSchema ? { expectStructuredOutput: true } : {}),\n onSdkMessage: (message) =>\n logger.provider(`provider=claude-code type=${message.type}`, {\n chunk: message,\n }),\n }),\n channel.stream,\n ),\n durability,\n logger,\n )\n\n // Surface the working-tree diff so UIs can render what the agent changed.\n if (this.adapterConfig.emitDiff !== false) {\n try {\n const diff = await sandbox.process.exec(`git -C ${q(cwd)} diff`, {\n cwd,\n })\n if (diff.exitCode === 0 && diff.stdout.trim() !== '') {\n yield {\n type: EventType.CUSTOM,\n name: 'file.changed',\n value: { path: '.', diff: diff.stdout },\n timestamp: Date.now(),\n threadId,\n runId,\n }\n }\n } catch {\n // not a git repo / git unavailable — skip the diff event\n }\n }\n\n // Surface any pending approval requests (policy `ask` actions awaiting a\n // client decision); the client approves and re-runs to continue.\n for (const event of approvalRequests) yield event\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n const rawEvent = toRunErrorRawEvent(error)\n logger.errors('claude-code.chatStream fatal', {\n error,\n source: 'claude-code.chatStream',\n })\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: err.message || 'Unknown error occurred',\n ...(err.code !== undefined && { code: err.code }),\n ...(rawEvent !== undefined && { rawEvent }),\n error: {\n message: err.message || 'Unknown error occurred',\n ...(err.code !== undefined && { code: err.code }),\n },\n }\n } finally {\n channel?.close()\n if (bridge) await bridge.close()\n // Remove the per-run token/prompt files. Best-effort: a cleanup failure\n // must not mask the run's own outcome.\n if (cleanupSandbox) {\n for (const path of tempFiles) {\n try {\n await cleanupSandbox.fs.remove(path)\n } catch {\n // file already gone / sandbox torn down — nothing to clean up\n }\n }\n }\n }\n }\n\n structuredOutput(\n _options: StructuredOutputOptions<ClaudeCodeTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n return Promise.reject(\n new Error(\n 'This harness honors outputSchema on chat() in the same turn. ' +\n 'Pass outputSchema to chat(), or use a model adapter for a one-shot extract.',\n ),\n )\n }\n}\n\n/**\n * Creates a Claude Code harness adapter that runs **inside a sandbox**.\n *\n * Unlike HTTP provider adapters, this is a *harness* adapter: it spawns the\n * `claude` CLI inside the sandbox provided by `withSandbox(...)` (the adapter\n * declares `requires: [SandboxCapability]`), streams its `stream-json` stdout\n * back as AG-UI events, and lets Claude Code run its own agent loop and native\n * tools (Bash, file edits, search, …) against the sandbox workspace. The\n * sandbox image must provide the `claude` executable and `ANTHROPIC_API_KEY`\n * in its environment (e.g. via `workspace.secrets`). The session id is\n * surfaced via a CUSTOM `claude-code.session-id` event so follow-up calls can\n * resume through `modelOptions.sessionId`.\n */\nexport function claudeCodeText<TModel extends ClaudeCodeModel>(\n model: TModel,\n config: ClaudeCodeTextConfig = {},\n): ClaudeCodeTextAdapter<TModel> {\n return new ClaudeCodeTextAdapter(config, model)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"text.js","names":[],"sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { EventType, normalizeSystemPrompts } from '@tanstack/ai'\nimport {\n appendOutputSchemaInstruction,\n toRunErrorRawEvent,\n} from '@tanstack/ai/adapter-internals'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport {\n SandboxCapability,\n alignedIfAttaching,\n approvalId,\n buildApprovalRequestedEvent,\n createBridgeEventChannel,\n createRunScopedIdGen,\n encodeRunId,\n getSandbox,\n getSandboxDurability,\n getSandboxPolicy,\n getToolBridgeProvisioner,\n getWorkspaceProjection,\n journalOptionsFor,\n mergeChunkStreams,\n nodeHttpBridgeProvisioner,\n resolveApproval,\n resolveDurableRunId,\n resolveDurableThreadId,\n spawnNdjson,\n} from '@tanstack/ai-sandbox'\nimport { buildPrompt } from '../messages/prompt'\nimport { translateSdkStream } from '../stream/translate'\nimport { mapPolicyToClaudeFlags } from './policy-map'\nimport { projectClaudeWorkspace } from './projection'\nimport {\n CLAUDE_JSON_SCHEMA_PLACEHOLDER,\n CLAUDE_RUNNER_SOURCE,\n} from './claude-run-source'\n\nimport type { ClaudePolicyFlags } from './policy-map'\nimport type {\n BridgeEventChannel,\n HostToolBridge,\n PermissionToolResult,\n SandboxHandle,\n SandboxPolicy,\n} from '@tanstack/ai-sandbox'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type {\n DefaultMessageMetadataByModality,\n Modality,\n AdapterYieldChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type { ClaudeCodeModel } from '../model-meta'\nimport type { ClaudeCodeTextProviderOptions } from '../provider-options'\nimport type { AgentSdkMessage } from '../stream/sdk-types'\n\nexport type ClaudeCodePermissionMode =\n | 'default'\n | 'acceptEdits'\n | 'bypassPermissions'\n | 'plan'\n\nconst DEFAULT_WORKDIR = '/workspace'\n\nexport interface ClaudeCodeTextConfig {\n /**\n * Working directory inside the sandbox where `claude` runs. Defaults to\n * `/workspace` (the conventional sandbox workspace root).\n */\n cwd?: string\n /**\n * Claude Code permission mode passed via `--permission-mode`. Defaults to\n * `'bypassPermissions'` — a sandbox is isolated, so the agent is allowed to\n * edit files and run commands without prompting. Tighten via `defineSandboxPolicy`\n * / this option for less autonomy.\n */\n permissionMode?: ClaudeCodePermissionMode\n /** Built-in tools the harness may use (`--allowedTools`). */\n allowedTools?: Array<string>\n /** Built-in tools removed from the harness (`--disallowedTools`). */\n disallowedTools?: Array<string>\n /** Extra directories the agent may access (`--add-dir`). */\n addDirs?: Array<string>\n /** Maximum harness-internal turns (`--max-turns`). */\n maxTurns?: number\n /**\n * How `systemPrompts` from `chat()` are applied:\n * - `'append'` (default): `--append-system-prompt` on top of the preset.\n * - `'replace'`: `--system-prompt` as the entire system prompt.\n */\n systemPromptMode?: 'append' | 'replace'\n /** Path/name of the claude executable inside the sandbox. Defaults to `claude`. */\n claudeExecutable?: string\n /** Emit token-level deltas via `--include-partial-messages` (default true). */\n streamPartials?: boolean\n /** Extra environment variables for the claude process inside the sandbox. */\n env?: Record<string, string>\n /**\n * `'api-key'` (default) injects `ANTHROPIC_API_KEY`.\n * `'host'` uses `claude login` and does not inject the key.\n * Not inferred from the sandbox.\n */\n authMode?: 'host' | 'api-key'\n /** Emit a `file.changed` CUSTOM event with the git diff after the run (default true). */\n emitDiff?: boolean\n}\n\n/** POSIX single-quote escape for embedding values in the `claude …` command. */\nfunction q(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`\n}\n\n/** Copy host Anthropic auth into the sandbox process. Docker `exec` Env replaces the container env, so a key set only at create time can vanish. */\nfunction hostClaudeAuthEnv(): Record<string, string> {\n const env: Record<string, string> = {}\n const apiKey = process.env.ANTHROPIC_API_KEY\n if (apiKey) env.ANTHROPIC_API_KEY = apiKey\n const authToken = process.env.ANTHROPIC_AUTH_TOKEN\n if (authToken) env.ANTHROPIC_AUTH_TOKEN = authToken\n return env\n}\n\n/**\n * Windows Node often has USERPROFILE but no HOME. Claude then cannot find\n * `~/.claude.json` (the `claude login` file) and prints \"Not logged in\".\n */\nfunction localProcessHomeEnv(provider: string): Record<string, string> {\n if (provider !== 'local-process') return {}\n if (process.env.HOME) return {}\n const home = process.env.USERPROFILE\n return home ? { HOME: home } : {}\n}\n\n/** Format a host tool-bridge as claude's `--mcp-config` JSON. */\nfunction bridgeToMcpConfig(bridge: HostToolBridge): string {\n return JSON.stringify({\n mcpServers: {\n [bridge.name]: {\n type: 'http',\n url: bridge.url,\n headers: { Authorization: `Bearer ${bridge.token}` },\n },\n },\n })\n}\n\nexport class ClaudeCodeTextAdapter<\n TModel extends ClaudeCodeModel,\n> extends BaseTextAdapter<\n TModel,\n ClaudeCodeTextProviderOptions,\n ReadonlyArray<Modality> & readonly ['text'],\n DefaultMessageMetadataByModality,\n ReadonlyArray<string>,\n unknown,\n never\n> {\n readonly name = 'claude-code' as const\n\n // Harness adapter: requires a sandbox to run the agent CLI inside.\n override readonly requires = [SandboxCapability] as const\n\n private readonly adapterConfig: ClaudeCodeTextConfig\n\n constructor(config: ClaudeCodeTextConfig, model: TModel) {\n super({}, model)\n this.adapterConfig = config\n }\n\n private sandboxFrom(\n options: TextOptions<ClaudeCodeTextProviderOptions>,\n ): SandboxHandle {\n const ctx = options.capabilities\n if (!ctx) {\n throw new Error(\n 'Adapter \"claude-code\" requires a sandbox. Add withSandbox(defineSandbox({ ... })) ' +\n 'to chat() middleware (e.g. with the local-process or docker provider).',\n )\n }\n return getSandbox(ctx)\n }\n\n private workdir(options: TextOptions<ClaudeCodeTextProviderOptions>): string {\n return (\n options.modelOptions?.cwd ?? this.adapterConfig.cwd ?? DEFAULT_WORKDIR\n )\n }\n\n supportsCombinedToolsAndSchema(): boolean {\n return true\n }\n\n combinedStructuredOutputSource(): 'event' {\n return 'event'\n }\n\n /** Build the `claude` command line (prompt goes via stdin, not argv). */\n private buildArgv(\n options: TextOptions<ClaudeCodeTextProviderOptions>,\n resume: string | undefined,\n policyFlags: ClaudePolicyFlags,\n mcpConfigPath: string | undefined,\n permissionPromptTool: string | undefined,\n hasJsonSchema: boolean,\n ): Array<string> {\n const config = this.adapterConfig\n const modelOptions = options.modelOptions\n const exeParts = (config.claudeExecutable ?? 'claude').split(' ')\n\n // `--setting-sources user` before `-p`. Do not pass `--bare`: that flag\n // skips stored `claude login` credentials and prints\n // \"Not logged in · Please run /login\" (claude-code#51047).\n // `-p` can take the next token as the prompt, so these flags stay first.\n const args: Array<string> = [\n ...exeParts,\n '--setting-sources',\n 'user',\n '-p',\n '--output-format',\n 'stream-json',\n '--verbose',\n '--model',\n this.model,\n ]\n\n if (config.streamPartials !== false) args.push('--include-partial-messages')\n if (resume !== undefined) args.push('--resume', resume)\n\n const permissionMode =\n modelOptions?.permissionMode ??\n config.permissionMode ??\n policyFlags.permissionMode ??\n 'bypassPermissions'\n args.push('--permission-mode', permissionMode)\n\n const maxTurns = modelOptions?.maxTurns ?? config.maxTurns\n if (maxTurns !== undefined) args.push('--max-turns', String(maxTurns))\n\n for (const dir of config.addDirs ?? []) args.push('--add-dir', dir)\n\n const allowedTools = [\n ...(modelOptions?.allowedTools ?? config.allowedTools ?? []),\n ...policyFlags.allowedTools,\n ]\n if (allowedTools.length > 0) {\n args.push('--allowedTools', [...new Set(allowedTools)].join(','))\n }\n const disallowedTools = [\n ...(modelOptions?.disallowedTools ?? config.disallowedTools ?? []),\n ...policyFlags.disallowedTools,\n ]\n if (disallowedTools.length > 0) {\n args.push('--disallowedTools', [...new Set(disallowedTools)].join(','))\n }\n\n const systemPrompts = normalizeSystemPrompts(options.systemPrompts)\n .map((prompt) => prompt.content)\n .filter((content) => content.trim() !== '')\n if (systemPrompts.length > 0) {\n const joined = systemPrompts.join('\\n\\n')\n const flag =\n config.systemPromptMode === 'replace'\n ? '--system-prompt'\n : '--append-system-prompt'\n args.push(flag, joined)\n }\n\n if (mcpConfigPath !== undefined) args.push('--mcp-config', mcpConfigPath)\n if (hasJsonSchema) {\n args.push('--json-schema', CLAUDE_JSON_SCHEMA_PLACEHOLDER)\n }\n if (permissionPromptTool !== undefined) {\n args.push('--permission-prompt-tool', permissionPromptTool)\n }\n\n return args\n }\n\n /**\n * Build the permission-prompt resolver the host MCP bridge exposes to claude\n * (`--permission-prompt-tool`). Maps claude's permission request onto the\n * sandbox policy + client approvals; on an `ask` action with no decision yet,\n * records an approval-requested event and denies (the client re-runs to grant).\n */\n private buildPermissionResolver(\n policy: SandboxPolicy | undefined,\n approvals: ReadonlyMap<string, boolean> | undefined,\n scripts: Record<string, string> | undefined,\n sink: Array<AdapterYieldChunk>,\n threadId: string,\n runId: string,\n ): (input: { tool_name?: string; input?: unknown }) => PermissionToolResult {\n const writeTools = new Set(['Write', 'Edit', 'MultiEdit', 'NotebookEdit'])\n const networkTools = new Set(['WebFetch', 'WebSearch'])\n return (request) => {\n const toolName = request.tool_name ?? 'tool'\n const cmdInput = request.input\n const command =\n toolName === 'Bash' &&\n cmdInput !== null &&\n typeof cmdInput === 'object' &&\n 'command' in cmdInput &&\n typeof (cmdInput as { command?: unknown }).command === 'string'\n ? (cmdInput as { command: string }).command\n : undefined\n const capability = writeTools.has(toolName)\n ? 'fileWrite'\n : networkTools.has(toolName)\n ? 'network'\n : undefined\n const id = approvalId({\n provider: 'claude-code',\n kind: command !== undefined ? 'command' : (capability ?? 'tool'),\n target: command ?? toolName,\n })\n const outcome = resolveApproval({\n policy,\n approvals,\n id,\n scripts,\n ...(command !== undefined ? { command } : {}),\n ...(capability !== undefined ? { capability } : {}),\n })\n if (outcome.needsApproval) {\n sink.push(\n buildApprovalRequestedEvent({\n approvalId: id,\n title: `Approve ${toolName}${command !== undefined ? `: ${command}` : ''}`,\n threadId,\n runId,\n detail: { provider: 'claude-code', toolName },\n }),\n )\n return {\n behavior: 'deny',\n message:\n 'Awaiting client approval. Approve in the UI and re-run to continue.',\n }\n }\n return outcome.decision === 'allow'\n ? { behavior: 'allow' }\n : { behavior: 'deny', message: 'Denied by sandbox policy.' }\n }\n }\n\n async *chatStream(\n options: TextOptions<ClaudeCodeTextProviderOptions>,\n ): AsyncIterable<AdapterYieldChunk> {\n const { logger } = options\n let bridge: HostToolBridge | undefined\n let channel: BridgeEventChannel | undefined\n const approvalRequests: Array<AdapterYieldChunk> = []\n // Temp files written for the run (bridge MCP config, redirected prompt) that\n // carry the bearer token / prompt; removed in `finally` so they don't linger\n // in the sandbox after the run.\n let cleanupSandbox: SandboxHandle | undefined\n const tempFiles: Array<string> = []\n try {\n const sandbox = this.sandboxFrom(options)\n cleanupSandbox = sandbox\n const cwd = this.workdir(options)\n\n // Durability comes from `withSandbox(sandbox, { runs, durability })`, read\n // back off the capability bus. Absent it, everything below resolves to\n // exactly today's behavior (no journal option, no alignment, and a\n // generated `runId` when the caller didn't supply one).\n const durability = options.capabilities\n ? getSandboxDurability(options.capabilities, { optional: true })\n : undefined\n // The journaled path below derives its journal path and its\n // message-id generator from `runId`. A resuming host must recompute\n // the same `runId` to find the same journal file and reproduce the\n // same translated ids — that's only possible when the caller supplies\n // `runId` explicitly. `resolveDurableRunId` enforces that loudly (via\n // `DurableRunIdRequiredError`) whenever durability is wired, and falls\n // back to a fresh `this.generateId()` otherwise, preserving today's\n // behavior for non-durable runs.\n const runId = resolveDurableRunId(options.runId, {\n durable: durability !== undefined,\n adapter: 'claude-code',\n fallback: () => this.generateId(),\n })\n // `threadId` is stamped on every chunk `translateSdkStream` emits, so an\n // ATTACHING run that mints a fresh one replays a stream the stored log\n // cannot match at index 0. `resolveDurableThreadId` refuses that up front\n // instead of letting alignment discover it mid-stream; a durable FRESH run\n // and a non-durable run both keep the generated fallback untouched.\n const threadId = resolveDurableThreadId(options.threadId, {\n durable: durability !== undefined,\n attaching: durability?.attach === true,\n adapter: 'claude-code',\n fallback: () => this.generateId(),\n })\n // Surfaces custom events from bridged tools (e.g. code mode console logs)\n // on this run's live output stream.\n channel = createBridgeEventChannel({ model: this.model, threadId, runId })\n\n // Idempotently project workspace skills/plugins/MCP into the sandbox in\n // claude's native format (guarded by the projection marker file).\n const projection = options.capabilities\n ? getWorkspaceProjection(options.capabilities, { optional: true })\n : undefined\n if (projection) await projectClaudeWorkspace(sandbox, projection)\n\n const policy = options.capabilities\n ? getSandboxPolicy(options.capabilities, { optional: true })\n : undefined\n\n // A permission-prompt tool gates the agent's native tools when a policy\n // can `ask`/`deny` (interactive approvals).\n const permission =\n policy !== undefined\n ? {\n toolName: 'approval_prompt',\n resolve: this.buildPermissionResolver(\n policy,\n options.approvals,\n projection?.scripts,\n approvalRequests,\n threadId,\n runId,\n ),\n }\n : undefined\n\n // Bridge chat()-provided server tools (and/or the permission tool) into\n // the sandbox over MCP.\n const hasTools = options.tools !== undefined && options.tools.length > 0\n if (hasTools || permission !== undefined) {\n const provisioner =\n (options.capabilities\n ? getToolBridgeProvisioner(options.capabilities, { optional: true })\n : undefined) ?? nodeHttpBridgeProvisioner\n bridge = await provisioner.provision(options.tools ?? [], {\n provider: sandbox.provider,\n context: options.context,\n emitCustomEvent: channel.emitCustomEvent,\n ...(permission !== undefined ? { permission } : {}),\n ...(options.abortController?.signal\n ? { signal: options.abortController.signal }\n : {}),\n })\n }\n\n const built = buildPrompt(\n options.messages,\n options.modelOptions?.sessionId,\n )\n const resume = built.resume\n const prompt =\n options.outputSchema !== undefined\n ? appendOutputSchemaInstruction(built.prompt, options.outputSchema)\n : built.prompt\n // Both files below name themselves after `runId`, and durability makes\n // `runId` CALLER-chosen. Raw, a `/` in it would silently turn each basename\n // into a nested path (writing outside the intended directory, or failing on\n // one that does not exist), `..` would climb out of that directory, and a\n // long id would fail the spawn with `ENAMETOOLONG`. `encodeRunId` collapses\n // any id to one bounded, injective path segment — the same encoder\n // `journalPaths` uses, so these files and the journal agree on how a given\n // id spells. Computed once so the two filenames cannot drift apart.\n const runIdSegment = encodeRunId(runId)\n\n // The bridge MCP config carries the per-run bearer token. Write it to a\n // file and pass claude the PATH, so the token never appears in argv (where\n // any process in the sandbox could read it via `ps` / `/proc/<pid>/cmdline`).\n let mcpConfigArg: string | undefined\n if (bridge) {\n // Pass claude a path RELATIVE to its cwd (the real workdir the handle\n // runs the process in). An absolute VIRTUAL path like `/workspace/…` is\n // wrong wherever claude runs outside a sandbox that literally uses\n // `/workspace` — e.g. local-process on Windows, where git-bash resolves\n // `/workspace` to `C:\\Program Files\\Git\\workspace` and the file is \"not\n // found\". The bare filename resolves correctly on every provider.\n const mcpConfigFile = `.tanstack-mcp-bridge-${runIdSegment}.json`\n const mcpConfigPath = `${cwd}/${mcpConfigFile}`\n await sandbox.fs.write(mcpConfigPath, bridgeToMcpConfig(bridge))\n tempFiles.push(mcpConfigPath)\n mcpConfigArg = mcpConfigFile\n }\n let jsonSchemaFile: string | undefined\n if (options.outputSchema !== undefined) {\n jsonSchemaFile = `tanstack-output-schema-${runIdSegment}.json`\n const schemaPath = `${cwd}/${jsonSchemaFile}`\n await sandbox.fs.write(schemaPath, JSON.stringify(options.outputSchema))\n tempFiles.push(schemaPath)\n }\n const runnerFile = `tanstack-claude-run-${runIdSegment}.mjs`\n await sandbox.fs.write(`${cwd}/${runnerFile}`, CLAUDE_RUNNER_SOURCE)\n tempFiles.push(`${cwd}/${runnerFile}`)\n const argv = this.buildArgv(\n options,\n resume,\n mapPolicyToClaudeFlags(policy),\n mcpConfigArg,\n bridge && permission\n ? `mcp__${bridge.name}__${permission.toolName}`\n : undefined,\n jsonSchemaFile !== undefined,\n )\n // Filenames only on the shell line. JSON (schema, system prompt, MCP\n // config path) lives in the argv file so git-bash cannot retokenize it.\n const argvFile = `tanstack-claude-argv-${runIdSegment}.json`\n await sandbox.fs.write(`${cwd}/${argvFile}`, JSON.stringify(argv))\n tempFiles.push(`${cwd}/${argvFile}`)\n const command =\n jsonSchemaFile === undefined\n ? `node ${q(runnerFile)} ${q(argvFile)}`\n : `node ${q(runnerFile)} ${q(argvFile)} ${q(jsonSchemaFile)}`\n\n // Deliver the prompt. The default feeds it over stdin (keeps it out of\n // argv). Providers without a writable host→process stdin (e.g. Cloudflare)\n // can't accept that write, so write the prompt to a file and redirect the\n // CLI's stdin from it in-shell (`claude -p … < file`) — still out of argv.\n let runCommand = command\n let stdinInput: string | undefined = prompt\n if (sandbox.capabilities.writableStdin === false) {\n const promptPath = `/tmp/tanstack-claude-prompt-${runIdSegment}`\n await sandbox.fs.write(promptPath, prompt)\n tempFiles.push(promptPath)\n runCommand = `${command} < ${q(promptPath)}`\n stdinInput = undefined\n }\n\n logger.request(\n `activity=chat provider=claude-code model=${this.model} sandbox=${sandbox.provider} messages=${options.messages.length} resume=${resume ?? 'none'}`,\n { provider: 'claude-code', model: this.model },\n )\n\n const authMode =\n options.modelOptions?.authMode ??\n this.adapterConfig.authMode ??\n 'api-key'\n const injectApiKey = authMode === 'api-key'\n\n const journalOptions = journalOptionsFor(durability, runId)\n const rawEvents = spawnNdjson(sandbox, runCommand, {\n cwd,\n ...(stdinInput !== undefined ? { input: stdinInput } : {}),\n // Isolated sandboxes often run as root. Claude refuses\n // `--dangerously-skip-permissions` as root unless IS_SANDBOX=1.\n // CLAUDE_CODE_SANDBOXED marks a real isolation boundary. Do not set\n // either on local-process: that provider runs on the host.\n env: {\n ...(sandbox.provider === 'local-process'\n ? {}\n : {\n IS_SANDBOX: '1',\n CLAUDE_CODE_SANDBOXED: '1',\n }),\n ...(injectApiKey ? hostClaudeAuthEnv() : {}),\n ...localProcessHomeEnv(sandbox.provider),\n ...this.adapterConfig.env,\n },\n ...(options.abortController?.signal\n ? { signal: options.abortController.signal }\n : options.request?.signal\n ? { signal: options.request.signal }\n : {}),\n onNonJsonLine: (line) =>\n logger.provider(`provider=claude-code non-json line: ${line}`, {\n chunk: line,\n }),\n // Journal + attach both come from the sandbox durability capability, so\n // the attach route configures takeover by passing `attach: true` to\n // `withSandbox` — `chat()` stays free of sandbox vocabulary. Omitted\n // entirely (not passed as `undefined`) when the run isn't durable, so\n // `spawnNdjson` takes its original, unjournaled path byte-for-byte.\n ...(journalOptions === undefined ? {} : { journal: journalOptions }),\n })\n\n async function* asMessages(): AsyncIterable<AgentSdkMessage> {\n for await (const event of rawEvents) yield event as AgentSdkMessage\n }\n\n // `mergeChunkStreams` splices `channel.stream` (host-tool-bridge CUSTOM\n // events from LIVE tool execution) into the deterministic translator\n // output. Those events do not occur on a replay, so a takeover's replay is\n // NOT chunk-for-chunk identical to what the log holds. `alignedIfAttaching`\n // handles it: alignment skips stored out-of-band CUSTOM entries within a\n // bounded window (see `align.ts`), so a bridged-tool run can be taken over\n // without a spurious `JournalReplayDivergedError` — while a genuine\n // determinism regression still throws. The wrap goes OUTSIDE the merge:\n // the stored log holds the previous host's merged output, so aligning the\n // pre-merge translated stream alone would compare against a log it never\n // produced. On a non-attaching run this is a pure passthrough (see\n // `alignedIfAttaching`'s `attach` guard).\n yield* alignedIfAttaching(\n mergeChunkStreams(\n translateSdkStream(asMessages(), {\n model: this.model,\n runId,\n threadId,\n ...(options.parentRunId !== undefined && {\n parentRunId: options.parentRunId,\n }),\n // Deterministic on the journaled path: two translations of the same\n // journal prefix from a fresh generator seeded with the same\n // `runId` mint the same message ids (unlike `this.generateId()`,\n // which mixes in `Date.now()` / `Math.random()`). See\n // `createRunScopedIdGen` in `@tanstack/ai-sandbox`.\n genId: createRunScopedIdGen(runId),\n ...(options.outputSchema ? { expectStructuredOutput: true } : {}),\n onSdkMessage: (message) =>\n logger.provider(`provider=claude-code type=${message.type}`, {\n chunk: message,\n }),\n }),\n channel.stream,\n ),\n durability,\n logger,\n )\n\n // Surface the working-tree diff so UIs can render what the agent changed.\n if (this.adapterConfig.emitDiff !== false) {\n try {\n const diff = await sandbox.process.exec(`git -C ${q(cwd)} diff`, {\n cwd,\n })\n if (diff.exitCode === 0 && diff.stdout.trim() !== '') {\n yield {\n type: EventType.CUSTOM,\n name: 'file.changed',\n value: { path: '.', diff: diff.stdout },\n timestamp: Date.now(),\n threadId,\n runId,\n }\n }\n } catch {\n // not a git repo / git unavailable — skip the diff event\n }\n }\n\n // Surface any pending approval requests (policy `ask` actions awaiting a\n // client decision); the client approves and re-runs to continue.\n for (const event of approvalRequests) yield event\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n const rawEvent = toRunErrorRawEvent(error)\n logger.errors('claude-code.chatStream fatal', {\n error,\n source: 'claude-code.chatStream',\n })\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: err.message || 'Unknown error occurred',\n ...(err.code !== undefined && { code: err.code }),\n ...(rawEvent !== undefined && { rawEvent }),\n error: {\n message: err.message || 'Unknown error occurred',\n ...(err.code !== undefined && { code: err.code }),\n },\n }\n } finally {\n channel?.close()\n if (bridge) await bridge.close()\n // Remove the per-run token/prompt files. Best-effort: a cleanup failure\n // must not mask the run's own outcome.\n if (cleanupSandbox) {\n for (const path of tempFiles) {\n try {\n await cleanupSandbox.fs.remove(path)\n } catch {\n // file already gone / sandbox torn down — nothing to clean up\n }\n }\n }\n }\n }\n\n structuredOutput(\n _options: StructuredOutputOptions<ClaudeCodeTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n return Promise.reject(\n new Error(\n 'This harness honors outputSchema on chat() in the same turn. ' +\n 'Pass outputSchema to chat(), or use a model adapter for a one-shot extract.',\n ),\n )\n }\n}\n\n/**\n * Creates a Claude Code harness adapter that runs **inside a sandbox**.\n *\n * Unlike HTTP provider adapters, this is a *harness* adapter: it spawns the\n * `claude` CLI inside the sandbox provided by `withSandbox(...)` (the adapter\n * declares `requires: [SandboxCapability]`), streams its `stream-json` stdout\n * back as AG-UI events, and lets Claude Code run its own agent loop and native\n * tools (Bash, file edits, search, …) against the sandbox workspace. The\n * sandbox image must provide the `claude` executable and `ANTHROPIC_API_KEY`\n * in its environment (e.g. via `workspace.secrets`). The session id is\n * surfaced via a CUSTOM `claude-code.session-id` event so follow-up calls can\n * resume through `modelOptions.sessionId`.\n */\nexport function claudeCodeText<TModel extends ClaudeCodeModel>(\n model: TModel,\n config: ClaudeCodeTextConfig = {},\n): ClaudeCodeTextAdapter<TModel> {\n return new ClaudeCodeTextAdapter(config, model)\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { StreamChunk } from '@tanstack/ai';
1
+ import { AdapterYieldChunk } from '@tanstack/ai';
2
2
  import { AgentSdkMessage } from './sdk-types.js';
3
3
  /** Name of the CUSTOM event carrying the Claude Code session id. */
4
4
  export declare const SESSION_ID_EVENT = "claude-code.session-id";
@@ -49,4 +49,4 @@ export declare function stripMcpPrefix(name: string): string;
49
49
  * ends or aborts before the harness reported one) so the engine's
50
50
  * pending-tool-call scan on the next request never force-executes them.
51
51
  */
52
- export declare function translateSdkStream(sdkMessages: AsyncIterable<AgentSdkMessage>, ctx: TranslateContext): AsyncIterable<StreamChunk>;
52
+ export declare function translateSdkStream(sdkMessages: AsyncIterable<AgentSdkMessage>, ctx: TranslateContext): AsyncIterable<AdapterYieldChunk>;
@@ -1 +1 @@
1
- {"version":3,"file":"translate.js","names":[],"sources":["../../../src/stream/translate.ts"],"sourcesContent":["import { EventType, buildBaseUsage } from '@tanstack/ai'\nimport {\n parseJsonFromAssistantText,\n structuredOutputCompleteChunk,\n structuredOutputStartChunk,\n} from '@tanstack/ai/adapter-internals'\nimport type { StreamChunk, TokenUsage } from '@tanstack/ai'\nimport type {\n AgentSdkMessage,\n SdkAssistantMessage,\n SdkPartialAssistantMessage,\n SdkResultMessage,\n SdkToolResultContent,\n SdkUsage,\n SdkUserMessage,\n} from './sdk-types'\n\n/** Name of the CUSTOM event carrying the Claude Code session id. */\nexport const SESSION_ID_EVENT = 'claude-code.session-id'\n\n/** Server name used for bridged TanStack tools (model sees `mcp__tanstack__<name>`). */\nexport const BRIDGED_MCP_SERVER_NAME = 'tanstack'\n\nconst BRIDGED_MCP_PREFIX = `mcp__${BRIDGED_MCP_SERVER_NAME}__`\n\n/**\n * Claude Code `--json-schema` injects a fake tool named StructuredOutput.\n * The model \"calls\" it with the schema JSON. The result message may omit\n * `structured_output`; harvest the tool input in that case.\n */\nexport const SYNTHETIC_STRUCTURED_OUTPUT_TOOL = 'StructuredOutput'\n\n/** Claude Code-specific usage details attached to RUN_FINISHED usage. */\nexport type ClaudeCodeProviderUsageDetails = {\n /** Total cost of the harness run in USD, as reported by Claude Code. */\n totalCostUsd?: number\n}\n\nexport interface TranslateContext {\n model: string\n runId: string\n threadId: string\n parentRunId?: string\n genId: () => string\n /** Called as soon as the harness reports its session id. */\n onSessionId?: (sessionId: string) => void\n /** Called for each raw SDK message, for logging. */\n onSdkMessage?: (message: AgentSdkMessage) => void\n /** Emit structured-output events from `result.structured_output`. */\n expectStructuredOutput?: boolean\n}\n\n/**\n * Strip the bridged MCP server prefix so tool-call events match the TanStack\n * tool names the application registered. Built-in harness tools (Bash, Read,\n * Edit, ...) and foreign MCP tools pass through verbatim.\n */\nexport function stripMcpPrefix(name: string): string {\n return name.startsWith(BRIDGED_MCP_PREFIX)\n ? name.slice(BRIDGED_MCP_PREFIX.length)\n : name\n}\n\nfunction isUsefulStructuredObject(value: unknown): boolean {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n Object.keys(value).length > 0\n )\n}\n\nfunction rememberStructuredOutput(current: unknown, next: unknown): unknown {\n return isUsefulStructuredObject(next) ? next : current\n}\n\nfunction stringifyToolResultContent(\n content: SdkToolResultContent | undefined,\n): string {\n if (content === undefined) return ''\n if (typeof content === 'string') return content\n return content\n .map((block) => (typeof block.text === 'string' ? block.text : ''))\n .join('')\n}\n\nfunction buildUsage(\n usage: SdkUsage | undefined,\n totalCostUsd: number | undefined,\n): TokenUsage<ClaudeCodeProviderUsageDetails> | undefined {\n if (!usage) return undefined\n const promptTokens = usage.input_tokens ?? 0\n const completionTokens = usage.output_tokens ?? 0\n const result = buildBaseUsage<ClaudeCodeProviderUsageDetails>({\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n })\n const cacheWrite = usage.cache_creation_input_tokens\n const cacheRead = usage.cache_read_input_tokens\n const promptTokensDetails = {\n ...(cacheWrite ? { cacheWriteTokens: cacheWrite } : {}),\n ...(cacheRead ? { cachedTokens: cacheRead } : {}),\n }\n if (Object.keys(promptTokensDetails).length > 0) {\n result.promptTokensDetails = promptTokensDetails\n }\n if (totalCostUsd !== undefined) {\n result.providerUsageDetails = { totalCostUsd }\n }\n return result\n}\n\n/**\n * Translate a Claude Code Agent SDK message stream into AG-UI StreamChunk\n * events.\n *\n * The harness runs its own agent loop and executes its own tools, so the\n * translation always ends with `finishReason: 'stop'` (or `'length'` /\n * RUN_ERROR) — never `'tool_calls'`. Harness tool activity is emitted as\n * already-resolved TOOL_CALL_START/ARGS/END + TOOL_CALL_RESULT sequences so\n * UIs can render it, while the TanStack engine never tries to execute them.\n *\n * Invariant: every TOOL_CALL_START is eventually paired with a\n * TOOL_CALL_RESULT (synthesized as `{\"status\":\"interrupted\"}` when the run\n * ends or aborts before the harness reported one) so the engine's\n * pending-tool-call scan on the next request never force-executes them.\n */\nexport async function* translateSdkStream(\n sdkMessages: AsyncIterable<AgentSdkMessage>,\n ctx: TranslateContext,\n): AsyncIterable<StreamChunk> {\n const { model, runId, threadId, genId } = ctx\n const now = () => Date.now()\n\n let runStarted = false\n /** Tool calls started but with no result yet. */\n const unresolvedToolCalls = new Set<string>()\n const syntheticOutputToolIds = new Set<string>()\n let capturedStructuredOutput: unknown\n let assistantTextForHarvest = ''\n let partialStructuredJson = ''\n let partialIsStructuredOutput = false\n /** Anthropic message ids whose text/thinking already streamed via partials. */\n const streamedMessageIds = new Set<string>()\n\n // Partial-stream state\n let partialMessageId: string | null = null\n let partialBlockType: string | null = null\n let partialTextMessageId: string | null = null\n let partialTextContent = ''\n let partialTextStarted = false\n let partialReasoningId: string | null = null\n\n function* startRun(): Generator<StreamChunk> {\n if (runStarted) return\n runStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: now(),\n ...(ctx.parentRunId !== undefined && { parentRunId: ctx.parentRunId }),\n }\n }\n\n function* synthesizeUnresolvedResults(): Generator<StreamChunk> {\n for (const toolCallId of unresolvedToolCalls) {\n yield {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId,\n messageId: genId(),\n model,\n timestamp: now(),\n content: JSON.stringify({ status: 'interrupted' }),\n }\n }\n unresolvedToolCalls.clear()\n }\n\n function* closePartialText(): Generator<StreamChunk> {\n if (partialTextStarted && partialTextMessageId) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: partialTextMessageId,\n model,\n timestamp: now(),\n }\n }\n partialTextStarted = false\n partialTextMessageId = null\n partialTextContent = ''\n }\n\n function* closePartialReasoning(): Generator<StreamChunk> {\n if (partialReasoningId) {\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: partialReasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: partialReasoningId,\n model,\n timestamp: now(),\n }\n }\n partialReasoningId = null\n }\n\n function* emitStructuredOutput(object: unknown): Generator<StreamChunk> {\n const raw = JSON.stringify(object)\n const messageId = genId()\n yield structuredOutputStartChunk({\n messageId,\n model,\n threadId,\n runId,\n })\n yield structuredOutputCompleteChunk({\n messageId,\n model,\n threadId,\n runId,\n object,\n raw,\n })\n }\n\n function* emitToolUse(block: {\n id: string\n name: string\n input: unknown\n }): Generator<StreamChunk> {\n const toolCallName = stripMcpPrefix(block.name)\n if (\n ctx.expectStructuredOutput === true &&\n toolCallName === SYNTHETIC_STRUCTURED_OUTPUT_TOOL\n ) {\n capturedStructuredOutput = rememberStructuredOutput(\n capturedStructuredOutput,\n block.input,\n )\n syntheticOutputToolIds.add(block.id)\n return\n }\n const args = JSON.stringify(block.input ?? {})\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: block.id,\n toolCallName,\n toolName: toolCallName,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: block.id,\n model,\n timestamp: now(),\n delta: args,\n args,\n }\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: block.id,\n toolCallName,\n toolName: toolCallName,\n model,\n timestamp: now(),\n input: block.input ?? {},\n }\n unresolvedToolCalls.add(block.id)\n }\n\n function* handleAssistant(\n message: SdkAssistantMessage,\n ): Generator<StreamChunk> {\n const alreadyStreamed =\n message.message.id !== undefined &&\n streamedMessageIds.has(message.message.id)\n\n for (const block of message.message.content) {\n if (block.type === 'text') {\n if (alreadyStreamed) continue\n const messageId = message.message.id ?? genId()\n const text = (block as { text: string }).text\n if (!alreadyStreamed) assistantTextForHarvest += text\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: now(),\n role: 'assistant',\n }\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: now(),\n delta: text,\n content: text,\n }\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp: now(),\n }\n } else if (block.type === 'thinking') {\n if (alreadyStreamed) continue\n const reasoningId = genId()\n const thinking = (block as { thinking: string }).thinking\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningId,\n role: 'reasoning' as const,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningId,\n delta: thinking,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n } else if (block.type === 'tool_use') {\n yield* emitToolUse(\n block as { id: string; name: string; input: unknown },\n )\n }\n }\n }\n\n function* handleUser(message: SdkUserMessage): Generator<StreamChunk> {\n const content = message.message.content\n if (typeof content === 'string') return\n for (const block of content) {\n if (block.type !== 'tool_result') continue\n const toolResult = block as {\n tool_use_id: string\n content?: SdkToolResultContent\n is_error?: boolean\n }\n if (syntheticOutputToolIds.has(toolResult.tool_use_id)) {\n syntheticOutputToolIds.delete(toolResult.tool_use_id)\n continue\n }\n unresolvedToolCalls.delete(toolResult.tool_use_id)\n yield {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId: toolResult.tool_use_id,\n messageId: genId(),\n model,\n timestamp: now(),\n content: stringifyToolResultContent(toolResult.content),\n ...(toolResult.is_error === true && { state: 'output-error' as const }),\n }\n }\n }\n\n function* handleResult(message: SdkResultMessage): Generator<StreamChunk> {\n yield* closePartialText()\n yield* closePartialReasoning()\n yield* synthesizeUnresolvedResults()\n\n const usage = buildUsage(message.usage, message.total_cost_usd)\n if (ctx.expectStructuredOutput === true) {\n const fromResult = isUsefulStructuredObject(message.structured_output)\n ? message.structured_output\n : undefined\n const fromTool = isUsefulStructuredObject(capturedStructuredOutput)\n ? capturedStructuredOutput\n : undefined\n let fromText: unknown\n if (fromResult === undefined && fromTool === undefined) {\n const raw = assistantTextForHarvest || message.result || ''\n if (raw.trim() !== '') {\n try {\n fromText = parseJsonFromAssistantText(raw)\n } catch {\n fromText = undefined\n }\n }\n }\n const object = fromResult ?? fromTool ?? fromText\n if (isUsefulStructuredObject(object)) {\n yield* emitStructuredOutput(object)\n }\n }\n if (message.subtype === 'success') {\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: now(),\n finishReason: 'stop',\n ...(usage !== undefined && { usage }),\n }\n } else if (message.subtype === 'error_max_turns') {\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: now(),\n finishReason: 'length',\n ...(usage !== undefined && { usage }),\n }\n } else {\n const errorMessage =\n message.errors && message.errors.length > 0\n ? message.errors.join('; ')\n : `Claude Code run failed: ${message.subtype}`\n yield {\n type: EventType.RUN_ERROR,\n model,\n timestamp: now(),\n message: errorMessage,\n code: message.subtype,\n error: { message: errorMessage, code: message.subtype },\n }\n }\n }\n\n function* handleStreamEvent(\n message: SdkPartialAssistantMessage,\n ): Generator<StreamChunk> {\n const event = message.event\n if (event.type === 'message_start') {\n partialMessageId = event.message.id ?? genId()\n streamedMessageIds.add(partialMessageId)\n } else if (event.type === 'content_block_start') {\n partialBlockType = event.content_block.type\n const startedBlock = event.content_block\n partialIsStructuredOutput =\n ctx.expectStructuredOutput === true &&\n startedBlock.type === 'tool_use' &&\n 'name' in startedBlock &&\n startedBlock.name === SYNTHETIC_STRUCTURED_OUTPUT_TOOL\n if (partialIsStructuredOutput) {\n partialStructuredJson = ''\n if ('id' in startedBlock && typeof startedBlock.id === 'string') {\n syntheticOutputToolIds.add(startedBlock.id)\n }\n if ('input' in startedBlock) {\n capturedStructuredOutput = rememberStructuredOutput(\n capturedStructuredOutput,\n startedBlock.input,\n )\n }\n }\n if (partialBlockType === 'text') {\n partialTextMessageId = partialMessageId ?? genId()\n partialTextContent = ''\n if (!partialTextStarted) {\n partialTextStarted = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: partialTextMessageId,\n model,\n timestamp: now(),\n role: 'assistant',\n }\n }\n } else if (partialBlockType === 'thinking') {\n partialReasoningId = genId()\n yield {\n type: EventType.REASONING_START,\n messageId: partialReasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: partialReasoningId,\n role: 'reasoning' as const,\n model,\n timestamp: now(),\n }\n }\n } else if (event.type === 'content_block_delta') {\n if (\n event.delta.type === 'text_delta' &&\n partialTextStarted &&\n partialTextMessageId &&\n typeof event.delta.text === 'string'\n ) {\n partialTextContent += event.delta.text\n assistantTextForHarvest += event.delta.text\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: partialTextMessageId,\n model,\n timestamp: now(),\n delta: event.delta.text,\n content: partialTextContent,\n }\n } else if (\n event.delta.type === 'input_json_delta' &&\n partialIsStructuredOutput &&\n typeof event.delta.partial_json === 'string'\n ) {\n partialStructuredJson += event.delta.partial_json\n } else if (\n event.delta.type === 'thinking_delta' &&\n partialReasoningId &&\n typeof event.delta.thinking === 'string'\n ) {\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: partialReasoningId,\n delta: event.delta.thinking,\n model,\n timestamp: now(),\n }\n }\n } else if (event.type === 'content_block_stop') {\n if (partialIsStructuredOutput && partialStructuredJson !== '') {\n try {\n capturedStructuredOutput = rememberStructuredOutput(\n capturedStructuredOutput,\n JSON.parse(partialStructuredJson),\n )\n } catch {\n // Incomplete JSON; the complete assistant tool_use may still arrive.\n }\n }\n if (partialBlockType === 'text') {\n yield* closePartialText()\n } else if (partialBlockType === 'thinking') {\n yield* closePartialReasoning()\n }\n partialBlockType = null\n partialIsStructuredOutput = false\n partialStructuredJson = ''\n }\n }\n\n try {\n for await (const sdkMessage of sdkMessages) {\n ctx.onSdkMessage?.(sdkMessage)\n\n if (sdkMessage.type === 'system' && sdkMessage.subtype === 'init') {\n yield* startRun()\n ctx.onSessionId?.(sdkMessage.session_id)\n yield {\n type: EventType.CUSTOM,\n model,\n timestamp: now(),\n name: SESSION_ID_EVENT,\n value: {\n sessionId: sdkMessage.session_id,\n model: sdkMessage.model,\n tools: sdkMessage.tools,\n },\n }\n continue\n }\n\n // Anything before init still needs RUN_STARTED first.\n yield* startRun()\n\n if (sdkMessage.type === 'stream_event') {\n if (sdkMessage.parent_tool_use_id !== null) continue\n yield* handleStreamEvent(sdkMessage)\n } else if (sdkMessage.type === 'assistant') {\n if (sdkMessage.parent_tool_use_id !== null) continue\n yield* handleAssistant(sdkMessage)\n } else if (sdkMessage.type === 'user') {\n if (sdkMessage.parent_tool_use_id !== null) continue\n yield* handleUser(sdkMessage)\n } else if (sdkMessage.type === 'result') {\n yield* handleResult(sdkMessage)\n }\n // All other SDK message types (status, hooks, notifications, ...) are\n // harness-internal and intentionally ignored.\n }\n } catch (error) {\n // The run is dying (abort or SDK failure). Pair any started tool calls\n // with a synthetic result first so the next request's pending-tool-call\n // scan doesn't try to execute them, then let the adapter surface the\n // error as RUN_ERROR.\n yield* synthesizeUnresolvedResults()\n throw error\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"translate.js","names":[],"sources":["../../../src/stream/translate.ts"],"sourcesContent":["import { EventType, buildBaseUsage } from '@tanstack/ai'\nimport {\n parseJsonFromAssistantText,\n structuredOutputCompleteChunk,\n structuredOutputStartChunk,\n} from '@tanstack/ai/adapter-internals'\nimport type { AdapterYieldChunk, TokenUsage } from '@tanstack/ai'\nimport type {\n AgentSdkMessage,\n SdkAssistantMessage,\n SdkPartialAssistantMessage,\n SdkResultMessage,\n SdkToolResultContent,\n SdkUsage,\n SdkUserMessage,\n} from './sdk-types'\n\n/** Name of the CUSTOM event carrying the Claude Code session id. */\nexport const SESSION_ID_EVENT = 'claude-code.session-id'\n\n/** Server name used for bridged TanStack tools (model sees `mcp__tanstack__<name>`). */\nexport const BRIDGED_MCP_SERVER_NAME = 'tanstack'\n\nconst BRIDGED_MCP_PREFIX = `mcp__${BRIDGED_MCP_SERVER_NAME}__`\n\n/**\n * Claude Code `--json-schema` injects a fake tool named StructuredOutput.\n * The model \"calls\" it with the schema JSON. The result message may omit\n * `structured_output`; harvest the tool input in that case.\n */\nexport const SYNTHETIC_STRUCTURED_OUTPUT_TOOL = 'StructuredOutput'\n\n/** Claude Code-specific usage details attached to RUN_FINISHED usage. */\nexport type ClaudeCodeProviderUsageDetails = {\n /** Total cost of the harness run in USD, as reported by Claude Code. */\n totalCostUsd?: number\n}\n\nexport interface TranslateContext {\n model: string\n runId: string\n threadId: string\n parentRunId?: string\n genId: () => string\n /** Called as soon as the harness reports its session id. */\n onSessionId?: (sessionId: string) => void\n /** Called for each raw SDK message, for logging. */\n onSdkMessage?: (message: AgentSdkMessage) => void\n /** Emit structured-output events from `result.structured_output`. */\n expectStructuredOutput?: boolean\n}\n\n/**\n * Strip the bridged MCP server prefix so tool-call events match the TanStack\n * tool names the application registered. Built-in harness tools (Bash, Read,\n * Edit, ...) and foreign MCP tools pass through verbatim.\n */\nexport function stripMcpPrefix(name: string): string {\n return name.startsWith(BRIDGED_MCP_PREFIX)\n ? name.slice(BRIDGED_MCP_PREFIX.length)\n : name\n}\n\nfunction isUsefulStructuredObject(value: unknown): boolean {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n Object.keys(value).length > 0\n )\n}\n\nfunction rememberStructuredOutput(current: unknown, next: unknown): unknown {\n return isUsefulStructuredObject(next) ? next : current\n}\n\nfunction stringifyToolResultContent(\n content: SdkToolResultContent | undefined,\n): string {\n if (content === undefined) return ''\n if (typeof content === 'string') return content\n return content\n .map((block) => (typeof block.text === 'string' ? block.text : ''))\n .join('')\n}\n\nfunction buildUsage(\n usage: SdkUsage | undefined,\n totalCostUsd: number | undefined,\n): TokenUsage<ClaudeCodeProviderUsageDetails> | undefined {\n if (!usage) return undefined\n const promptTokens = usage.input_tokens ?? 0\n const completionTokens = usage.output_tokens ?? 0\n const result = buildBaseUsage<ClaudeCodeProviderUsageDetails>({\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n })\n const cacheWrite = usage.cache_creation_input_tokens\n const cacheRead = usage.cache_read_input_tokens\n const promptTokensDetails = {\n ...(cacheWrite ? { cacheWriteTokens: cacheWrite } : {}),\n ...(cacheRead ? { cachedTokens: cacheRead } : {}),\n }\n if (Object.keys(promptTokensDetails).length > 0) {\n result.promptTokensDetails = promptTokensDetails\n }\n if (totalCostUsd !== undefined) {\n result.providerUsageDetails = { totalCostUsd }\n }\n return result\n}\n\n/**\n * Translate a Claude Code Agent SDK message stream into AG-UI StreamChunk\n * events.\n *\n * The harness runs its own agent loop and executes its own tools, so the\n * translation always ends with `finishReason: 'stop'` (or `'length'` /\n * RUN_ERROR) — never `'tool_calls'`. Harness tool activity is emitted as\n * already-resolved TOOL_CALL_START/ARGS/END + TOOL_CALL_RESULT sequences so\n * UIs can render it, while the TanStack engine never tries to execute them.\n *\n * Invariant: every TOOL_CALL_START is eventually paired with a\n * TOOL_CALL_RESULT (synthesized as `{\"status\":\"interrupted\"}` when the run\n * ends or aborts before the harness reported one) so the engine's\n * pending-tool-call scan on the next request never force-executes them.\n */\nexport async function* translateSdkStream(\n sdkMessages: AsyncIterable<AgentSdkMessage>,\n ctx: TranslateContext,\n): AsyncIterable<AdapterYieldChunk> {\n const { model, runId, threadId, genId } = ctx\n const now = () => Date.now()\n\n let runStarted = false\n /** Tool calls started but with no result yet. */\n const unresolvedToolCalls = new Set<string>()\n const syntheticOutputToolIds = new Set<string>()\n let capturedStructuredOutput: unknown\n let assistantTextForHarvest = ''\n let partialStructuredJson = ''\n let partialIsStructuredOutput = false\n /** Anthropic message ids whose text/thinking already streamed via partials. */\n const streamedMessageIds = new Set<string>()\n\n // Partial-stream state\n let partialMessageId: string | null = null\n let partialBlockType: string | null = null\n let partialTextMessageId: string | null = null\n let partialTextContent = ''\n let partialTextStarted = false\n let partialReasoningId: string | null = null\n\n function* startRun(): Generator<AdapterYieldChunk> {\n if (runStarted) return\n runStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: now(),\n ...(ctx.parentRunId !== undefined && { parentRunId: ctx.parentRunId }),\n }\n }\n\n function* synthesizeUnresolvedResults(): Generator<AdapterYieldChunk> {\n for (const toolCallId of unresolvedToolCalls) {\n yield {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId,\n messageId: genId(),\n model,\n timestamp: now(),\n content: JSON.stringify({ status: 'interrupted' }),\n }\n }\n unresolvedToolCalls.clear()\n }\n\n function* closePartialText(): Generator<AdapterYieldChunk> {\n if (partialTextStarted && partialTextMessageId) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: partialTextMessageId,\n model,\n timestamp: now(),\n }\n }\n partialTextStarted = false\n partialTextMessageId = null\n partialTextContent = ''\n }\n\n function* closePartialReasoning(): Generator<AdapterYieldChunk> {\n if (partialReasoningId) {\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: partialReasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: partialReasoningId,\n model,\n timestamp: now(),\n }\n }\n partialReasoningId = null\n }\n\n function* emitStructuredOutput(\n object: unknown,\n ): Generator<AdapterYieldChunk> {\n const raw = JSON.stringify(object)\n const messageId = genId()\n yield structuredOutputStartChunk({\n messageId,\n model,\n threadId,\n runId,\n })\n yield structuredOutputCompleteChunk({\n messageId,\n model,\n threadId,\n runId,\n object,\n raw,\n })\n }\n\n function* emitToolUse(block: {\n id: string\n name: string\n input: unknown\n }): Generator<AdapterYieldChunk> {\n const toolCallName = stripMcpPrefix(block.name)\n if (\n ctx.expectStructuredOutput === true &&\n toolCallName === SYNTHETIC_STRUCTURED_OUTPUT_TOOL\n ) {\n capturedStructuredOutput = rememberStructuredOutput(\n capturedStructuredOutput,\n block.input,\n )\n syntheticOutputToolIds.add(block.id)\n return\n }\n const args = JSON.stringify(block.input ?? {})\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: block.id,\n toolCallName,\n toolName: toolCallName,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: block.id,\n model,\n timestamp: now(),\n delta: args,\n args,\n }\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: block.id,\n toolCallName,\n toolName: toolCallName,\n model,\n timestamp: now(),\n input: block.input ?? {},\n }\n unresolvedToolCalls.add(block.id)\n }\n\n function* handleAssistant(\n message: SdkAssistantMessage,\n ): Generator<AdapterYieldChunk> {\n const alreadyStreamed =\n message.message.id !== undefined &&\n streamedMessageIds.has(message.message.id)\n\n for (const block of message.message.content) {\n if (block.type === 'text') {\n if (alreadyStreamed) continue\n const messageId = message.message.id ?? genId()\n const text = (block as { text: string }).text\n if (!alreadyStreamed) assistantTextForHarvest += text\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: now(),\n role: 'assistant',\n }\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: now(),\n delta: text,\n content: text,\n }\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp: now(),\n }\n } else if (block.type === 'thinking') {\n if (alreadyStreamed) continue\n const reasoningId = genId()\n const thinking = (block as { thinking: string }).thinking\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningId,\n role: 'reasoning' as const,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningId,\n delta: thinking,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n } else if (block.type === 'tool_use') {\n yield* emitToolUse(\n block as { id: string; name: string; input: unknown },\n )\n }\n }\n }\n\n function* handleUser(message: SdkUserMessage): Generator<AdapterYieldChunk> {\n const content = message.message.content\n if (typeof content === 'string') return\n for (const block of content) {\n if (block.type !== 'tool_result') continue\n const toolResult = block as {\n tool_use_id: string\n content?: SdkToolResultContent\n is_error?: boolean\n }\n if (syntheticOutputToolIds.has(toolResult.tool_use_id)) {\n syntheticOutputToolIds.delete(toolResult.tool_use_id)\n continue\n }\n unresolvedToolCalls.delete(toolResult.tool_use_id)\n yield {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId: toolResult.tool_use_id,\n messageId: genId(),\n model,\n timestamp: now(),\n content: stringifyToolResultContent(toolResult.content),\n ...(toolResult.is_error === true && { state: 'output-error' as const }),\n }\n }\n }\n\n function* handleResult(\n message: SdkResultMessage,\n ): Generator<AdapterYieldChunk> {\n yield* closePartialText()\n yield* closePartialReasoning()\n yield* synthesizeUnresolvedResults()\n\n const usage = buildUsage(message.usage, message.total_cost_usd)\n if (ctx.expectStructuredOutput === true) {\n const fromResult = isUsefulStructuredObject(message.structured_output)\n ? message.structured_output\n : undefined\n const fromTool = isUsefulStructuredObject(capturedStructuredOutput)\n ? capturedStructuredOutput\n : undefined\n let fromText: unknown\n if (fromResult === undefined && fromTool === undefined) {\n const raw = assistantTextForHarvest || message.result || ''\n if (raw.trim() !== '') {\n try {\n fromText = parseJsonFromAssistantText(raw)\n } catch {\n fromText = undefined\n }\n }\n }\n const object = fromResult ?? fromTool ?? fromText\n if (isUsefulStructuredObject(object)) {\n yield* emitStructuredOutput(object)\n }\n }\n if (message.subtype === 'success') {\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: now(),\n finishReason: 'stop',\n ...(usage !== undefined && { usage }),\n }\n } else if (message.subtype === 'error_max_turns') {\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: now(),\n finishReason: 'length',\n ...(usage !== undefined && { usage }),\n }\n } else {\n const errorMessage =\n message.errors && message.errors.length > 0\n ? message.errors.join('; ')\n : `Claude Code run failed: ${message.subtype}`\n yield {\n type: EventType.RUN_ERROR,\n model,\n timestamp: now(),\n message: errorMessage,\n code: message.subtype,\n error: { message: errorMessage, code: message.subtype },\n }\n }\n }\n\n function* handleStreamEvent(\n message: SdkPartialAssistantMessage,\n ): Generator<AdapterYieldChunk> {\n const event = message.event\n if (event.type === 'message_start') {\n partialMessageId = event.message.id ?? genId()\n streamedMessageIds.add(partialMessageId)\n } else if (event.type === 'content_block_start') {\n partialBlockType = event.content_block.type\n const startedBlock = event.content_block\n partialIsStructuredOutput =\n ctx.expectStructuredOutput === true &&\n startedBlock.type === 'tool_use' &&\n 'name' in startedBlock &&\n startedBlock.name === SYNTHETIC_STRUCTURED_OUTPUT_TOOL\n if (partialIsStructuredOutput) {\n partialStructuredJson = ''\n if ('id' in startedBlock && typeof startedBlock.id === 'string') {\n syntheticOutputToolIds.add(startedBlock.id)\n }\n if ('input' in startedBlock) {\n capturedStructuredOutput = rememberStructuredOutput(\n capturedStructuredOutput,\n startedBlock.input,\n )\n }\n }\n if (partialBlockType === 'text') {\n partialTextMessageId = partialMessageId ?? genId()\n partialTextContent = ''\n if (!partialTextStarted) {\n partialTextStarted = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: partialTextMessageId,\n model,\n timestamp: now(),\n role: 'assistant',\n }\n }\n } else if (partialBlockType === 'thinking') {\n partialReasoningId = genId()\n yield {\n type: EventType.REASONING_START,\n messageId: partialReasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: partialReasoningId,\n role: 'reasoning' as const,\n model,\n timestamp: now(),\n }\n }\n } else if (event.type === 'content_block_delta') {\n if (\n event.delta.type === 'text_delta' &&\n partialTextStarted &&\n partialTextMessageId &&\n typeof event.delta.text === 'string'\n ) {\n partialTextContent += event.delta.text\n assistantTextForHarvest += event.delta.text\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: partialTextMessageId,\n model,\n timestamp: now(),\n delta: event.delta.text,\n content: partialTextContent,\n }\n } else if (\n event.delta.type === 'input_json_delta' &&\n partialIsStructuredOutput &&\n typeof event.delta.partial_json === 'string'\n ) {\n partialStructuredJson += event.delta.partial_json\n } else if (\n event.delta.type === 'thinking_delta' &&\n partialReasoningId &&\n typeof event.delta.thinking === 'string'\n ) {\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: partialReasoningId,\n delta: event.delta.thinking,\n model,\n timestamp: now(),\n }\n }\n } else if (event.type === 'content_block_stop') {\n if (partialIsStructuredOutput && partialStructuredJson !== '') {\n try {\n capturedStructuredOutput = rememberStructuredOutput(\n capturedStructuredOutput,\n JSON.parse(partialStructuredJson),\n )\n } catch {\n // Incomplete JSON; the complete assistant tool_use may still arrive.\n }\n }\n if (partialBlockType === 'text') {\n yield* closePartialText()\n } else if (partialBlockType === 'thinking') {\n yield* closePartialReasoning()\n }\n partialBlockType = null\n partialIsStructuredOutput = false\n partialStructuredJson = ''\n }\n }\n\n try {\n for await (const sdkMessage of sdkMessages) {\n ctx.onSdkMessage?.(sdkMessage)\n\n if (sdkMessage.type === 'system' && sdkMessage.subtype === 'init') {\n yield* startRun()\n ctx.onSessionId?.(sdkMessage.session_id)\n yield {\n type: EventType.CUSTOM,\n model,\n timestamp: now(),\n name: SESSION_ID_EVENT,\n value: {\n sessionId: sdkMessage.session_id,\n model: sdkMessage.model,\n tools: sdkMessage.tools,\n },\n }\n continue\n }\n\n // Anything before init still needs RUN_STARTED first.\n yield* startRun()\n\n if (sdkMessage.type === 'stream_event') {\n if (sdkMessage.parent_tool_use_id !== null) continue\n yield* handleStreamEvent(sdkMessage)\n } else if (sdkMessage.type === 'assistant') {\n if (sdkMessage.parent_tool_use_id !== null) continue\n yield* handleAssistant(sdkMessage)\n } else if (sdkMessage.type === 'user') {\n if (sdkMessage.parent_tool_use_id !== null) continue\n yield* handleUser(sdkMessage)\n } else if (sdkMessage.type === 'result') {\n yield* handleResult(sdkMessage)\n }\n // All other SDK message types (status, hooks, notifications, ...) are\n // harness-internal and intentionally ignored.\n }\n } catch (error) {\n // The run is dying (abort or SDK failure). Pair any started tool calls\n // with a synthetic result first so the next request's pending-tool-call\n // scan doesn't try to execute them, then let the adapter surface the\n // error as RUN_ERROR.\n yield* synthesizeUnresolvedResults()\n throw error\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-claude-code",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Claude Code harness adapter for TanStack AI — run Claude Code as a chat backend with local tool execution and stateful sessions.",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -37,14 +37,14 @@
37
37
  "src"
38
38
  ],
39
39
  "peerDependencies": {
40
- "@tanstack/ai": "^0.47.1",
41
- "@tanstack/ai-sandbox": "^0.4.0"
40
+ "@tanstack/ai": "^0.48.0",
41
+ "@tanstack/ai-sandbox": "^0.5.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@vitest/coverage-v8": "4.1.10",
45
- "@tanstack/ai": "0.47.1",
46
- "@tanstack/ai-sandbox": "0.4.0",
47
- "@tanstack/ai-sandbox-local-process": "0.2.3"
45
+ "@tanstack/ai": "0.48.0",
46
+ "@tanstack/ai-sandbox": "0.5.0",
47
+ "@tanstack/ai-sandbox-local-process": "0.2.4"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "vite build",
@@ -49,7 +49,7 @@ import type {
49
49
  import type {
50
50
  DefaultMessageMetadataByModality,
51
51
  Modality,
52
- StreamChunk,
52
+ AdapterYieldChunk,
53
53
  TextOptions,
54
54
  } from '@tanstack/ai'
55
55
  import type { ClaudeCodeModel } from '../model-meta'
@@ -288,7 +288,7 @@ export class ClaudeCodeTextAdapter<
288
288
  policy: SandboxPolicy | undefined,
289
289
  approvals: ReadonlyMap<string, boolean> | undefined,
290
290
  scripts: Record<string, string> | undefined,
291
- sink: Array<StreamChunk>,
291
+ sink: Array<AdapterYieldChunk>,
292
292
  threadId: string,
293
293
  runId: string,
294
294
  ): (input: { tool_name?: string; input?: unknown }) => PermissionToolResult {
@@ -347,11 +347,11 @@ export class ClaudeCodeTextAdapter<
347
347
 
348
348
  async *chatStream(
349
349
  options: TextOptions<ClaudeCodeTextProviderOptions>,
350
- ): AsyncIterable<StreamChunk> {
350
+ ): AsyncIterable<AdapterYieldChunk> {
351
351
  const { logger } = options
352
352
  let bridge: HostToolBridge | undefined
353
353
  let channel: BridgeEventChannel | undefined
354
- const approvalRequests: Array<StreamChunk> = []
354
+ const approvalRequests: Array<AdapterYieldChunk> = []
355
355
  // Temp files written for the run (bridge MCP config, redirected prompt) that
356
356
  // carry the bearer token / prompt; removed in `finally` so they don't linger
357
357
  // in the sandbox after the run.
@@ -4,7 +4,7 @@ import {
4
4
  structuredOutputCompleteChunk,
5
5
  structuredOutputStartChunk,
6
6
  } from '@tanstack/ai/adapter-internals'
7
- import type { StreamChunk, TokenUsage } from '@tanstack/ai'
7
+ import type { AdapterYieldChunk, TokenUsage } from '@tanstack/ai'
8
8
  import type {
9
9
  AgentSdkMessage,
10
10
  SdkAssistantMessage,
@@ -129,7 +129,7 @@ function buildUsage(
129
129
  export async function* translateSdkStream(
130
130
  sdkMessages: AsyncIterable<AgentSdkMessage>,
131
131
  ctx: TranslateContext,
132
- ): AsyncIterable<StreamChunk> {
132
+ ): AsyncIterable<AdapterYieldChunk> {
133
133
  const { model, runId, threadId, genId } = ctx
134
134
  const now = () => Date.now()
135
135
 
@@ -152,7 +152,7 @@ export async function* translateSdkStream(
152
152
  let partialTextStarted = false
153
153
  let partialReasoningId: string | null = null
154
154
 
155
- function* startRun(): Generator<StreamChunk> {
155
+ function* startRun(): Generator<AdapterYieldChunk> {
156
156
  if (runStarted) return
157
157
  runStarted = true
158
158
  yield {
@@ -165,7 +165,7 @@ export async function* translateSdkStream(
165
165
  }
166
166
  }
167
167
 
168
- function* synthesizeUnresolvedResults(): Generator<StreamChunk> {
168
+ function* synthesizeUnresolvedResults(): Generator<AdapterYieldChunk> {
169
169
  for (const toolCallId of unresolvedToolCalls) {
170
170
  yield {
171
171
  type: EventType.TOOL_CALL_RESULT,
@@ -179,7 +179,7 @@ export async function* translateSdkStream(
179
179
  unresolvedToolCalls.clear()
180
180
  }
181
181
 
182
- function* closePartialText(): Generator<StreamChunk> {
182
+ function* closePartialText(): Generator<AdapterYieldChunk> {
183
183
  if (partialTextStarted && partialTextMessageId) {
184
184
  yield {
185
185
  type: EventType.TEXT_MESSAGE_END,
@@ -193,7 +193,7 @@ export async function* translateSdkStream(
193
193
  partialTextContent = ''
194
194
  }
195
195
 
196
- function* closePartialReasoning(): Generator<StreamChunk> {
196
+ function* closePartialReasoning(): Generator<AdapterYieldChunk> {
197
197
  if (partialReasoningId) {
198
198
  yield {
199
199
  type: EventType.REASONING_MESSAGE_END,
@@ -211,7 +211,9 @@ export async function* translateSdkStream(
211
211
  partialReasoningId = null
212
212
  }
213
213
 
214
- function* emitStructuredOutput(object: unknown): Generator<StreamChunk> {
214
+ function* emitStructuredOutput(
215
+ object: unknown,
216
+ ): Generator<AdapterYieldChunk> {
215
217
  const raw = JSON.stringify(object)
216
218
  const messageId = genId()
217
219
  yield structuredOutputStartChunk({
@@ -234,7 +236,7 @@ export async function* translateSdkStream(
234
236
  id: string
235
237
  name: string
236
238
  input: unknown
237
- }): Generator<StreamChunk> {
239
+ }): Generator<AdapterYieldChunk> {
238
240
  const toolCallName = stripMcpPrefix(block.name)
239
241
  if (
240
242
  ctx.expectStructuredOutput === true &&
@@ -278,7 +280,7 @@ export async function* translateSdkStream(
278
280
 
279
281
  function* handleAssistant(
280
282
  message: SdkAssistantMessage,
281
- ): Generator<StreamChunk> {
283
+ ): Generator<AdapterYieldChunk> {
282
284
  const alreadyStreamed =
283
285
  message.message.id !== undefined &&
284
286
  streamedMessageIds.has(message.message.id)
@@ -354,7 +356,7 @@ export async function* translateSdkStream(
354
356
  }
355
357
  }
356
358
 
357
- function* handleUser(message: SdkUserMessage): Generator<StreamChunk> {
359
+ function* handleUser(message: SdkUserMessage): Generator<AdapterYieldChunk> {
358
360
  const content = message.message.content
359
361
  if (typeof content === 'string') return
360
362
  for (const block of content) {
@@ -381,7 +383,9 @@ export async function* translateSdkStream(
381
383
  }
382
384
  }
383
385
 
384
- function* handleResult(message: SdkResultMessage): Generator<StreamChunk> {
386
+ function* handleResult(
387
+ message: SdkResultMessage,
388
+ ): Generator<AdapterYieldChunk> {
385
389
  yield* closePartialText()
386
390
  yield* closePartialReasoning()
387
391
  yield* synthesizeUnresolvedResults()
@@ -448,7 +452,7 @@ export async function* translateSdkStream(
448
452
 
449
453
  function* handleStreamEvent(
450
454
  message: SdkPartialAssistantMessage,
451
- ): Generator<StreamChunk> {
455
+ ): Generator<AdapterYieldChunk> {
452
456
  const event = message.event
453
457
  if (event.type === 'message_start') {
454
458
  partialMessageId = event.message.id ?? genId()