@tanstack/ai-claude-code 0.6.12 → 0.7.2
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.
|
@@ -78,12 +78,15 @@ async function* translateSdkStream(sdkMessages, ctx) {
|
|
|
78
78
|
let partialIsStructuredOutput = false;
|
|
79
79
|
/** Anthropic message ids whose text/thinking already streamed via partials. */
|
|
80
80
|
const streamedMessageIds = /* @__PURE__ */ new Set();
|
|
81
|
+
/** Tool-call ids already streamed via partials, so the complete message dedupes them. */
|
|
82
|
+
const streamedToolCallIds = /* @__PURE__ */ new Set();
|
|
81
83
|
let partialMessageId = null;
|
|
82
|
-
|
|
84
|
+
const partialBlockTypes = /* @__PURE__ */ new Map();
|
|
83
85
|
let partialTextMessageId = null;
|
|
84
86
|
let partialTextContent = "";
|
|
85
87
|
let partialTextStarted = false;
|
|
86
88
|
let partialReasoningId = null;
|
|
89
|
+
const partialToolCalls = /* @__PURE__ */ new Map();
|
|
87
90
|
function* startRun() {
|
|
88
91
|
if (runStarted) return;
|
|
89
92
|
runStarted = true;
|
|
@@ -135,6 +138,32 @@ async function* translateSdkStream(sdkMessages, ctx) {
|
|
|
135
138
|
}
|
|
136
139
|
partialReasoningId = null;
|
|
137
140
|
}
|
|
141
|
+
function* closePartialToolUse(index, interrupted = false) {
|
|
142
|
+
const toolCall = partialToolCalls.get(index);
|
|
143
|
+
if (toolCall) {
|
|
144
|
+
let input = {};
|
|
145
|
+
try {
|
|
146
|
+
const parsed = toolCall.args ? JSON.parse(toolCall.args) : {};
|
|
147
|
+
input = parsed && typeof parsed === "object" ? parsed : {};
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (!interrupted) throw error;
|
|
150
|
+
}
|
|
151
|
+
partialToolCalls.delete(index);
|
|
152
|
+
yield {
|
|
153
|
+
type: EventType.TOOL_CALL_END,
|
|
154
|
+
toolCallId: toolCall.id,
|
|
155
|
+
toolCallName: toolCall.name,
|
|
156
|
+
toolName: toolCall.name,
|
|
157
|
+
model,
|
|
158
|
+
timestamp: now(),
|
|
159
|
+
input
|
|
160
|
+
};
|
|
161
|
+
unresolvedToolCalls.add(toolCall.id);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function* closePartialToolUses(interrupted = false) {
|
|
165
|
+
for (const index of [...partialToolCalls.keys()]) yield* closePartialToolUse(index, interrupted);
|
|
166
|
+
}
|
|
138
167
|
function* emitStructuredOutput(object) {
|
|
139
168
|
const raw = JSON.stringify(object);
|
|
140
169
|
const messageId = genId();
|
|
@@ -252,7 +281,11 @@ async function* translateSdkStream(sdkMessages, ctx) {
|
|
|
252
281
|
model,
|
|
253
282
|
timestamp: now()
|
|
254
283
|
};
|
|
255
|
-
} else if (block.type === "tool_use")
|
|
284
|
+
} else if (block.type === "tool_use") {
|
|
285
|
+
const toolBlock = block;
|
|
286
|
+
if (streamedToolCallIds.has(toolBlock.id)) continue;
|
|
287
|
+
yield* emitToolUse(toolBlock);
|
|
288
|
+
}
|
|
256
289
|
}
|
|
257
290
|
function* handleUser(message) {
|
|
258
291
|
const content = message.message.content;
|
|
@@ -279,6 +312,7 @@ async function* translateSdkStream(sdkMessages, ctx) {
|
|
|
279
312
|
function* handleResult(message) {
|
|
280
313
|
yield* closePartialText();
|
|
281
314
|
yield* closePartialReasoning();
|
|
315
|
+
yield* closePartialToolUses();
|
|
282
316
|
yield* synthesizeUnresolvedResults();
|
|
283
317
|
const usage = buildUsage(message.usage, message.total_cost_usd);
|
|
284
318
|
if (ctx.expectStructuredOutput === true) {
|
|
@@ -335,13 +369,14 @@ async function* translateSdkStream(sdkMessages, ctx) {
|
|
|
335
369
|
partialMessageId = event.message.id ?? genId();
|
|
336
370
|
streamedMessageIds.add(partialMessageId);
|
|
337
371
|
} else if (event.type === "content_block_start") {
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
|
|
372
|
+
const block = event.content_block;
|
|
373
|
+
const partialBlockType = block.type;
|
|
374
|
+
partialBlockTypes.set(event.index, partialBlockType);
|
|
375
|
+
partialIsStructuredOutput = ctx.expectStructuredOutput === true && block.type === "tool_use" && block.name === "StructuredOutput";
|
|
341
376
|
if (partialIsStructuredOutput) {
|
|
342
377
|
partialStructuredJson = "";
|
|
343
|
-
if (
|
|
344
|
-
if ("input" in
|
|
378
|
+
if (typeof block.id === "string") syntheticOutputToolIds.add(block.id);
|
|
379
|
+
if ("input" in block) capturedStructuredOutput = rememberStructuredOutput(capturedStructuredOutput, block.input);
|
|
345
380
|
}
|
|
346
381
|
if (partialBlockType === "text") {
|
|
347
382
|
partialTextMessageId = partialMessageId ?? genId();
|
|
@@ -371,6 +406,23 @@ async function* translateSdkStream(sdkMessages, ctx) {
|
|
|
371
406
|
model,
|
|
372
407
|
timestamp: now()
|
|
373
408
|
};
|
|
409
|
+
} else if (partialBlockType === "tool_use" && !partialIsStructuredOutput && typeof block.id === "string" && typeof block.name === "string") {
|
|
410
|
+
const name = stripMcpPrefix(block.name);
|
|
411
|
+
partialToolCalls.set(event.index, {
|
|
412
|
+
id: block.id,
|
|
413
|
+
name,
|
|
414
|
+
args: ""
|
|
415
|
+
});
|
|
416
|
+
streamedToolCallIds.add(block.id);
|
|
417
|
+
yield {
|
|
418
|
+
type: EventType.TOOL_CALL_START,
|
|
419
|
+
toolCallId: block.id,
|
|
420
|
+
toolCallName: name,
|
|
421
|
+
toolName: name,
|
|
422
|
+
model,
|
|
423
|
+
timestamp: now(),
|
|
424
|
+
...partialMessageId !== null && { parentMessageId: partialMessageId }
|
|
425
|
+
};
|
|
374
426
|
}
|
|
375
427
|
} else if (event.type === "content_block_delta") {
|
|
376
428
|
if (event.delta.type === "text_delta" && partialTextStarted && partialTextMessageId && typeof event.delta.text === "string") {
|
|
@@ -392,13 +444,28 @@ async function* translateSdkStream(sdkMessages, ctx) {
|
|
|
392
444
|
model,
|
|
393
445
|
timestamp: now()
|
|
394
446
|
};
|
|
447
|
+
else if (event.delta.type === "input_json_delta" && typeof event.delta.partial_json === "string") {
|
|
448
|
+
const toolCall = partialToolCalls.get(event.index);
|
|
449
|
+
if (toolCall) {
|
|
450
|
+
toolCall.args += event.delta.partial_json;
|
|
451
|
+
yield {
|
|
452
|
+
type: EventType.TOOL_CALL_ARGS,
|
|
453
|
+
toolCallId: toolCall.id,
|
|
454
|
+
model,
|
|
455
|
+
timestamp: now(),
|
|
456
|
+
delta: event.delta.partial_json
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
}
|
|
395
460
|
} else if (event.type === "content_block_stop") {
|
|
461
|
+
const partialBlockType = partialBlockTypes.get(event.index);
|
|
462
|
+
partialBlockTypes.delete(event.index);
|
|
396
463
|
if (partialIsStructuredOutput && partialStructuredJson !== "") try {
|
|
397
464
|
capturedStructuredOutput = rememberStructuredOutput(capturedStructuredOutput, JSON.parse(partialStructuredJson));
|
|
398
465
|
} catch {}
|
|
399
466
|
if (partialBlockType === "text") yield* closePartialText();
|
|
400
467
|
else if (partialBlockType === "thinking") yield* closePartialReasoning();
|
|
401
|
-
partialBlockType
|
|
468
|
+
else if (partialBlockType === "tool_use") yield* closePartialToolUse(event.index);
|
|
402
469
|
partialIsStructuredOutput = false;
|
|
403
470
|
partialStructuredJson = "";
|
|
404
471
|
}
|
|
@@ -436,6 +503,7 @@ async function* translateSdkStream(sdkMessages, ctx) {
|
|
|
436
503
|
} else if (sdkMessage.type === "result") yield* handleResult(sdkMessage);
|
|
437
504
|
}
|
|
438
505
|
} catch (error) {
|
|
506
|
+
yield* closePartialToolUses(true);
|
|
439
507
|
yield* synthesizeUnresolvedResults();
|
|
440
508
|
throw error;
|
|
441
509
|
}
|
|
@@ -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 { 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 skills: sdkMessage.skills ?? [],\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 /** Tool-call ids already streamed via partials, so the complete message dedupes them. */\n const streamedToolCallIds = new Set<string>()\n\n // Partial-stream state\n let partialMessageId: string | null = null\n const partialBlockTypes = new Map<number, string>()\n let partialTextMessageId: string | null = null\n let partialTextContent = ''\n let partialTextStarted = false\n let partialReasoningId: string | null = null\n const partialToolCalls = new Map<\n number,\n { id: string; name: string; args: string }\n >()\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* closePartialToolUse(\n index: number,\n interrupted = false,\n ): Generator<AdapterYieldChunk> {\n const toolCall = partialToolCalls.get(index)\n if (toolCall) {\n let input: unknown = {}\n try {\n const parsed = toolCall.args ? JSON.parse(toolCall.args) : {}\n input = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (error) {\n if (!interrupted) throw error\n }\n partialToolCalls.delete(index)\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model,\n timestamp: now(),\n input,\n }\n unresolvedToolCalls.add(toolCall.id)\n }\n }\n\n function* closePartialToolUses(\n interrupted = false,\n ): Generator<AdapterYieldChunk> {\n for (const index of [...partialToolCalls.keys()]) {\n yield* closePartialToolUse(index, interrupted)\n }\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 const toolBlock = block as { id: string; name: string; input: unknown }\n // Skip the tool call if the partial stream already emitted it.\n if (streamedToolCallIds.has(toolBlock.id)) continue\n yield* emitToolUse(toolBlock)\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* closePartialToolUses()\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 const block = event.content_block\n const partialBlockType = block.type\n partialBlockTypes.set(event.index, partialBlockType)\n partialIsStructuredOutput =\n ctx.expectStructuredOutput === true &&\n block.type === 'tool_use' &&\n block.name === SYNTHETIC_STRUCTURED_OUTPUT_TOOL\n if (partialIsStructuredOutput) {\n partialStructuredJson = ''\n if (typeof block.id === 'string') {\n syntheticOutputToolIds.add(block.id)\n }\n if ('input' in block) {\n capturedStructuredOutput = rememberStructuredOutput(\n capturedStructuredOutput,\n block.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 } else if (\n partialBlockType === 'tool_use' &&\n !partialIsStructuredOutput &&\n typeof block.id === 'string' &&\n typeof block.name === 'string'\n ) {\n const name = stripMcpPrefix(block.name)\n partialToolCalls.set(event.index, { id: block.id, name, args: '' })\n streamedToolCallIds.add(block.id)\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: block.id,\n toolCallName: name,\n toolName: name,\n model,\n timestamp: now(),\n ...(partialMessageId !== null && {\n parentMessageId: partialMessageId,\n }),\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 } else if (\n event.delta.type === 'input_json_delta' &&\n typeof event.delta.partial_json === 'string'\n ) {\n const toolCall = partialToolCalls.get(event.index)\n if (toolCall) {\n toolCall.args += event.delta.partial_json\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: toolCall.id,\n model,\n timestamp: now(),\n delta: event.delta.partial_json,\n }\n }\n }\n } else if (event.type === 'content_block_stop') {\n const partialBlockType = partialBlockTypes.get(event.index)\n partialBlockTypes.delete(event.index)\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 } else if (partialBlockType === 'tool_use') {\n yield* closePartialToolUse(event.index)\n }\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 skills: sdkMessage.skills ?? [],\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). Close any half-streamed tool\n // calls, then pair every started tool call with a synthetic result so the\n // next request's pending-tool-call scan doesn't try to execute them. Then\n // let the adapter surface the error as RUN_ERROR.\n yield* closePartialToolUses(true)\n yield* synthesizeUnresolvedResults()\n throw error\n }\n}\n"],"mappings":";;;;AAkBA,IAAa,mBAAmB;;AAGhC,IAAa,0BAA0B;AAEvC,IAAM,qBAAqB,QAAQ,wBAAwB;;;;;;AAO3D,IAAa,mCAAmC;;;;;;AA2BhD,SAAgB,eAAe,MAAsB;CACnD,OAAO,KAAK,WAAW,kBAAkB,IACrC,KAAK,MAAM,mBAAmB,MAAM,IACpC;AACN;AAEA,SAAS,yBAAyB,OAAyB;CACzD,OACE,UAAU,QACV,OAAO,UAAU,YACjB,CAAC,MAAM,QAAQ,KAAK,KACpB,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS;AAEhC;AAEA,SAAS,yBAAyB,SAAkB,MAAwB;CAC1E,OAAO,yBAAyB,IAAI,IAAI,OAAO;AACjD;AAEA,SAAS,2BACP,SACQ;CACR,IAAI,YAAY,KAAA,GAAW,OAAO;CAClC,IAAI,OAAO,YAAY,UAAU,OAAO;CACxC,OAAO,QACJ,KAAK,UAAW,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,EAAG,CAAC,CAClE,KAAK,EAAE;AACZ;AAEA,SAAS,WACP,OACA,cACwD;CACxD,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,MAAM,eAAe,MAAM,gBAAgB;CAC3C,MAAM,mBAAmB,MAAM,iBAAiB;CAChD,MAAM,SAAS,eAA+C;EAC5D;EACA;EACA,aAAa,eAAe;CAC9B,CAAC;CACD,MAAM,aAAa,MAAM;CACzB,MAAM,YAAY,MAAM;CACxB,MAAM,sBAAsB;EAC1B,GAAI,aAAa,EAAE,kBAAkB,WAAW,IAAI,CAAC;EACrD,GAAI,YAAY,EAAE,cAAc,UAAU,IAAI,CAAC;CACjD;CACA,IAAI,OAAO,KAAK,mBAAmB,CAAC,CAAC,SAAS,GAC5C,OAAO,sBAAsB;CAE/B,IAAI,iBAAiB,KAAA,GACnB,OAAO,uBAAuB,EAAE,aAAa;CAE/C,OAAO;AACT;;;;;;;;;;;;;;;;AAiBA,gBAAuB,mBACrB,aACA,KACkC;CAClC,MAAM,EAAE,OAAO,OAAO,UAAU,UAAU;CAC1C,MAAM,YAAY,KAAK,IAAI;CAE3B,IAAI,aAAa;;CAEjB,MAAM,sCAAsB,IAAI,IAAY;CAC5C,MAAM,yCAAyB,IAAI,IAAY;CAC/C,IAAI;CACJ,IAAI,0BAA0B;CAC9B,IAAI,wBAAwB;CAC5B,IAAI,4BAA4B;;CAEhC,MAAM,qCAAqB,IAAI,IAAY;;CAE3C,MAAM,sCAAsB,IAAI,IAAY;CAG5C,IAAI,mBAAkC;CACtC,MAAM,oCAAoB,IAAI,IAAoB;CAClD,IAAI,uBAAsC;CAC1C,IAAI,qBAAqB;CACzB,IAAI,qBAAqB;CACzB,IAAI,qBAAoC;CACxC,MAAM,mCAAmB,IAAI,IAG3B;CAEF,UAAU,WAAyC;EACjD,IAAI,YAAY;EAChB,aAAa;EACb,MAAM;GACJ,MAAM,UAAU;GAChB;GACA;GACA;GACA,WAAW,IAAI;GACf,GAAI,IAAI,gBAAgB,KAAA,KAAa,EAAE,aAAa,IAAI,YAAY;EACtE;CACF;CAEA,UAAU,8BAA4D;EACpE,KAAK,MAAM,cAAc,qBACvB,MAAM;GACJ,MAAM,UAAU;GAChB;GACA,WAAW,MAAM;GACjB;GACA,WAAW,IAAI;GACf,SAAS,KAAK,UAAU,EAAE,QAAQ,cAAc,CAAC;EACnD;EAEF,oBAAoB,MAAM;CAC5B;CAEA,UAAU,mBAAiD;EACzD,IAAI,sBAAsB,sBACxB,MAAM;GACJ,MAAM,UAAU;GAChB,WAAW;GACX;GACA,WAAW,IAAI;EACjB;EAEF,qBAAqB;EACrB,uBAAuB;EACvB,qBAAqB;CACvB;CAEA,UAAU,wBAAsD;EAC9D,IAAI,oBAAoB;GACtB,MAAM;IACJ,MAAM,UAAU;IAChB,WAAW;IACX;IACA,WAAW,IAAI;GACjB;GACA,MAAM;IACJ,MAAM,UAAU;IAChB,WAAW;IACX;IACA,WAAW,IAAI;GACjB;EACF;EACA,qBAAqB;CACvB;CAEA,UAAU,oBACR,OACA,cAAc,OACgB;EAC9B,MAAM,WAAW,iBAAiB,IAAI,KAAK;EAC3C,IAAI,UAAU;GACZ,IAAI,QAAiB,CAAC;GACtB,IAAI;IACF,MAAM,SAAS,SAAS,OAAO,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC;IAC5D,QAAQ,UAAU,OAAO,WAAW,WAAW,SAAS,CAAC;GAC3D,SAAS,OAAO;IACd,IAAI,CAAC,aAAa,MAAM;GAC1B;GACA,iBAAiB,OAAO,KAAK;GAC7B,MAAM;IACJ,MAAM,UAAU;IAChB,YAAY,SAAS;IACrB,cAAc,SAAS;IACvB,UAAU,SAAS;IACnB;IACA,WAAW,IAAI;IACf;GACF;GACA,oBAAoB,IAAI,SAAS,EAAE;EACrC;CACF;CAEA,UAAU,qBACR,cAAc,OACgB;EAC9B,KAAK,MAAM,SAAS,CAAC,GAAG,iBAAiB,KAAK,CAAC,GAC7C,OAAO,oBAAoB,OAAO,WAAW;CAEjD;CAEA,UAAU,qBACR,QAC8B;EAC9B,MAAM,MAAM,KAAK,UAAU,MAAM;EACjC,MAAM,YAAY,MAAM;EACxB,MAAM,2BAA2B;GAC/B;GACA;GACA;GACA;EACF,CAAC;EACD,MAAM,8BAA8B;GAClC;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;CACH;CAEA,UAAU,YAAY,OAIW;EAC/B,MAAM,eAAe,eAAe,MAAM,IAAI;EAC9C,IACE,IAAI,2BAA2B,QAC/B,iBAAA,oBACA;GACA,2BAA2B,yBACzB,0BACA,MAAM,KACR;GACA,uBAAuB,IAAI,MAAM,EAAE;GACnC;EACF;EACA,MAAM,OAAO,KAAK,UAAU,MAAM,SAAS,CAAC,CAAC;EAC7C,MAAM;GACJ,MAAM,UAAU;GAChB,YAAY,MAAM;GAClB;GACA,UAAU;GACV;GACA,WAAW,IAAI;EACjB;EACA,MAAM;GACJ,MAAM,UAAU;GAChB,YAAY,MAAM;GAClB;GACA,WAAW,IAAI;GACf,OAAO;GACP;EACF;EACA,MAAM;GACJ,MAAM,UAAU;GAChB,YAAY,MAAM;GAClB;GACA,UAAU;GACV;GACA,WAAW,IAAI;GACf,OAAO,MAAM,SAAS,CAAC;EACzB;EACA,oBAAoB,IAAI,MAAM,EAAE;CAClC;CAEA,UAAU,gBACR,SAC8B;EAC9B,MAAM,kBACJ,QAAQ,QAAQ,OAAO,KAAA,KACvB,mBAAmB,IAAI,QAAQ,QAAQ,EAAE;EAE3C,KAAK,MAAM,SAAS,QAAQ,QAAQ,SAClC,IAAI,MAAM,SAAS,QAAQ;GACzB,IAAI,iBAAiB;GACrB,MAAM,YAAY,QAAQ,QAAQ,MAAM,MAAM;GAC9C,MAAM,OAAQ,MAA2B;GACzC,IAAI,CAAC,iBAAiB,2BAA2B;GACjD,MAAM;IACJ,MAAM,UAAU;IAChB;IACA;IACA,WAAW,IAAI;IACf,MAAM;GACR;GACA,MAAM;IACJ,MAAM,UAAU;IAChB;IACA;IACA,WAAW,IAAI;IACf,OAAO;IACP,SAAS;GACX;GACA,MAAM;IACJ,MAAM,UAAU;IAChB;IACA;IACA,WAAW,IAAI;GACjB;EACF,OAAO,IAAI,MAAM,SAAS,YAAY;GACpC,IAAI,iBAAiB;GACrB,MAAM,cAAc,MAAM;GAC1B,MAAM,WAAY,MAA+B;GACjD,MAAM;IACJ,MAAM,UAAU;IAChB,WAAW;IACX;IACA,WAAW,IAAI;GACjB;GACA,MAAM;IACJ,MAAM,UAAU;IAChB,WAAW;IACX,MAAM;IACN;IACA,WAAW,IAAI;GACjB;GACA,MAAM;IACJ,MAAM,UAAU;IAChB,WAAW;IACX,OAAO;IACP;IACA,WAAW,IAAI;GACjB;GACA,MAAM;IACJ,MAAM,UAAU;IAChB,WAAW;IACX;IACA,WAAW,IAAI;GACjB;GACA,MAAM;IACJ,MAAM,UAAU;IAChB,WAAW;IACX;IACA,WAAW,IAAI;GACjB;EACF,OAAO,IAAI,MAAM,SAAS,YAAY;GACpC,MAAM,YAAY;GAElB,IAAI,oBAAoB,IAAI,UAAU,EAAE,GAAG;GAC3C,OAAO,YAAY,SAAS;EAC9B;CAEJ;CAEA,UAAU,WAAW,SAAuD;EAC1E,MAAM,UAAU,QAAQ,QAAQ;EAChC,IAAI,OAAO,YAAY,UAAU;EACjC,KAAK,MAAM,SAAS,SAAS;GAC3B,IAAI,MAAM,SAAS,eAAe;GAClC,MAAM,aAAa;GAKnB,IAAI,uBAAuB,IAAI,WAAW,WAAW,GAAG;IACtD,uBAAuB,OAAO,WAAW,WAAW;IACpD;GACF;GACA,oBAAoB,OAAO,WAAW,WAAW;GACjD,MAAM;IACJ,MAAM,UAAU;IAChB,YAAY,WAAW;IACvB,WAAW,MAAM;IACjB;IACA,WAAW,IAAI;IACf,SAAS,2BAA2B,WAAW,OAAO;IACtD,GAAI,WAAW,aAAa,QAAQ,EAAE,OAAO,eAAwB;GACvE;EACF;CACF;CAEA,UAAU,aACR,SAC8B;EAC9B,OAAO,iBAAiB;EACxB,OAAO,sBAAsB;EAC7B,OAAO,qBAAqB;EAC5B,OAAO,4BAA4B;EAEnC,MAAM,QAAQ,WAAW,QAAQ,OAAO,QAAQ,cAAc;EAC9D,IAAI,IAAI,2BAA2B,MAAM;GACvC,MAAM,aAAa,yBAAyB,QAAQ,iBAAiB,IACjE,QAAQ,oBACR,KAAA;GACJ,MAAM,WAAW,yBAAyB,wBAAwB,IAC9D,2BACA,KAAA;GACJ,IAAI;GACJ,IAAI,eAAe,KAAA,KAAa,aAAa,KAAA,GAAW;IACtD,MAAM,MAAM,2BAA2B,QAAQ,UAAU;IACzD,IAAI,IAAI,KAAK,MAAM,IACjB,IAAI;KACF,WAAW,2BAA2B,GAAG;IAC3C,QAAQ;KACN,WAAW,KAAA;IACb;GAEJ;GACA,MAAM,SAAS,cAAc,YAAY;GACzC,IAAI,yBAAyB,MAAM,GACjC,OAAO,qBAAqB,MAAM;EAEtC;EACA,IAAI,QAAQ,YAAY,WACtB,MAAM;GACJ,MAAM,UAAU;GAChB;GACA;GACA;GACA,WAAW,IAAI;GACf,cAAc;GACd,GAAI,UAAU,KAAA,KAAa,EAAE,MAAM;EACrC;OACK,IAAI,QAAQ,YAAY,mBAC7B,MAAM;GACJ,MAAM,UAAU;GAChB;GACA;GACA;GACA,WAAW,IAAI;GACf,cAAc;GACd,GAAI,UAAU,KAAA,KAAa,EAAE,MAAM;EACrC;OACK;GACL,MAAM,eACJ,QAAQ,UAAU,QAAQ,OAAO,SAAS,IACtC,QAAQ,OAAO,KAAK,IAAI,IACxB,2BAA2B,QAAQ;GACzC,MAAM;IACJ,MAAM,UAAU;IAChB;IACA,WAAW,IAAI;IACf,SAAS;IACT,MAAM,QAAQ;IACd,OAAO;KAAE,SAAS;KAAc,MAAM,QAAQ;IAAQ;GACxD;EACF;CACF;CAEA,UAAU,kBACR,SAC8B;EAC9B,MAAM,QAAQ,QAAQ;EACtB,IAAI,MAAM,SAAS,iBAAiB;GAClC,mBAAmB,MAAM,QAAQ,MAAM,MAAM;GAC7C,mBAAmB,IAAI,gBAAgB;EACzC,OAAO,IAAI,MAAM,SAAS,uBAAuB;GAC/C,MAAM,QAAQ,MAAM;GACpB,MAAM,mBAAmB,MAAM;GAC/B,kBAAkB,IAAI,MAAM,OAAO,gBAAgB;GACnD,4BACE,IAAI,2BAA2B,QAC/B,MAAM,SAAS,cACf,MAAM,SAAA;GACR,IAAI,2BAA2B;IAC7B,wBAAwB;IACxB,IAAI,OAAO,MAAM,OAAO,UACtB,uBAAuB,IAAI,MAAM,EAAE;IAErC,IAAI,WAAW,OACb,2BAA2B,yBACzB,0BACA,MAAM,KACR;GAEJ;GACA,IAAI,qBAAqB,QAAQ;IAC/B,uBAAuB,oBAAoB,MAAM;IACjD,qBAAqB;IACrB,IAAI,CAAC,oBAAoB;KACvB,qBAAqB;KACrB,MAAM;MACJ,MAAM,UAAU;MAChB,WAAW;MACX;MACA,WAAW,IAAI;MACf,MAAM;KACR;IACF;GACF,OAAO,IAAI,qBAAqB,YAAY;IAC1C,qBAAqB,MAAM;IAC3B,MAAM;KACJ,MAAM,UAAU;KAChB,WAAW;KACX;KACA,WAAW,IAAI;IACjB;IACA,MAAM;KACJ,MAAM,UAAU;KAChB,WAAW;KACX,MAAM;KACN;KACA,WAAW,IAAI;IACjB;GACF,OAAO,IACL,qBAAqB,cACrB,CAAC,6BACD,OAAO,MAAM,OAAO,YACpB,OAAO,MAAM,SAAS,UACtB;IACA,MAAM,OAAO,eAAe,MAAM,IAAI;IACtC,iBAAiB,IAAI,MAAM,OAAO;KAAE,IAAI,MAAM;KAAI;KAAM,MAAM;IAAG,CAAC;IAClE,oBAAoB,IAAI,MAAM,EAAE;IAChC,MAAM;KACJ,MAAM,UAAU;KAChB,YAAY,MAAM;KAClB,cAAc;KACd,UAAU;KACV;KACA,WAAW,IAAI;KACf,GAAI,qBAAqB,QAAQ,EAC/B,iBAAiB,iBACnB;IACF;GACF;EACF,OAAO,IAAI,MAAM,SAAS,uBAAuB;GAC/C,IACE,MAAM,MAAM,SAAS,gBACrB,sBACA,wBACA,OAAO,MAAM,MAAM,SAAS,UAC5B;IACA,sBAAsB,MAAM,MAAM;IAClC,2BAA2B,MAAM,MAAM;IACvC,MAAM;KACJ,MAAM,UAAU;KAChB,WAAW;KACX;KACA,WAAW,IAAI;KACf,OAAO,MAAM,MAAM;KACnB,SAAS;IACX;GACF,OAAO,IACL,MAAM,MAAM,SAAS,sBACrB,6BACA,OAAO,MAAM,MAAM,iBAAiB,UAEpC,yBAAyB,MAAM,MAAM;QAChC,IACL,MAAM,MAAM,SAAS,oBACrB,sBACA,OAAO,MAAM,MAAM,aAAa,UAEhC,MAAM;IACJ,MAAM,UAAU;IAChB,WAAW;IACX,OAAO,MAAM,MAAM;IACnB;IACA,WAAW,IAAI;GACjB;QACK,IACL,MAAM,MAAM,SAAS,sBACrB,OAAO,MAAM,MAAM,iBAAiB,UACpC;IACA,MAAM,WAAW,iBAAiB,IAAI,MAAM,KAAK;IACjD,IAAI,UAAU;KACZ,SAAS,QAAQ,MAAM,MAAM;KAC7B,MAAM;MACJ,MAAM,UAAU;MAChB,YAAY,SAAS;MACrB;MACA,WAAW,IAAI;MACf,OAAO,MAAM,MAAM;KACrB;IACF;GACF;EACF,OAAO,IAAI,MAAM,SAAS,sBAAsB;GAC9C,MAAM,mBAAmB,kBAAkB,IAAI,MAAM,KAAK;GAC1D,kBAAkB,OAAO,MAAM,KAAK;GACpC,IAAI,6BAA6B,0BAA0B,IACzD,IAAI;IACF,2BAA2B,yBACzB,0BACA,KAAK,MAAM,qBAAqB,CAClC;GACF,QAAQ,CAER;GAEF,IAAI,qBAAqB,QACvB,OAAO,iBAAiB;QACnB,IAAI,qBAAqB,YAC9B,OAAO,sBAAsB;QACxB,IAAI,qBAAqB,YAC9B,OAAO,oBAAoB,MAAM,KAAK;GAExC,4BAA4B;GAC5B,wBAAwB;EAC1B;CACF;CAEA,IAAI;EACF,WAAW,MAAM,cAAc,aAAa;GAC1C,IAAI,eAAe,UAAU;GAE7B,IAAI,WAAW,SAAS,YAAY,WAAW,YAAY,QAAQ;IACjE,OAAO,SAAS;IAChB,IAAI,cAAc,WAAW,UAAU;IACvC,MAAM;KACJ,MAAM,UAAU;KAChB;KACA,WAAW,IAAI;KACf,MAAM;KACN,OAAO;MACL,WAAW,WAAW;MACtB,OAAO,WAAW;MAClB,OAAO,WAAW;MAClB,QAAQ,WAAW,UAAU,CAAC;KAChC;IACF;IACA;GACF;GAGA,OAAO,SAAS;GAEhB,IAAI,WAAW,SAAS,gBAAgB;IACtC,IAAI,WAAW,uBAAuB,MAAM;IAC5C,OAAO,kBAAkB,UAAU;GACrC,OAAO,IAAI,WAAW,SAAS,aAAa;IAC1C,IAAI,WAAW,uBAAuB,MAAM;IAC5C,OAAO,gBAAgB,UAAU;GACnC,OAAO,IAAI,WAAW,SAAS,QAAQ;IACrC,IAAI,WAAW,uBAAuB,MAAM;IAC5C,OAAO,WAAW,UAAU;GAC9B,OAAO,IAAI,WAAW,SAAS,UAC7B,OAAO,aAAa,UAAU;EAIlC;CACF,SAAS,OAAO;EAKd,OAAO,qBAAqB,IAAI;EAChC,OAAO,4BAA4B;EACnC,MAAM;CACR;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-claude-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.2",
|
|
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",
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
"src"
|
|
42
42
|
],
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@tanstack/ai": "^0.
|
|
45
|
-
"@tanstack/ai-sandbox": "^0.5.
|
|
44
|
+
"@tanstack/ai": "^0.64.0",
|
|
45
|
+
"@tanstack/ai-sandbox": "^0.5.18"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@vitest/coverage-v8": "4.1.10",
|
|
49
|
-
"@tanstack/ai": "0.
|
|
50
|
-
"@tanstack/ai-sandbox": "0.5.
|
|
51
|
-
"@tanstack/ai-sandbox-local-process": "0.2.
|
|
49
|
+
"@tanstack/ai": "0.64.0",
|
|
50
|
+
"@tanstack/ai-sandbox": "0.5.18",
|
|
51
|
+
"@tanstack/ai-sandbox-local-process": "0.2.6"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"build": "vite build",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"lint:fix": "oxlint src --type-aware --fix",
|
|
57
57
|
"test:build": "publint --strict",
|
|
58
58
|
"test:oxlint": "oxlint src --type-aware",
|
|
59
|
+
"test:coverage": "vitest run --coverage --coverage.include='src/**' --coverage.reporter=text-summary --coverage.reporter=json-summary",
|
|
59
60
|
"test:lib": "vitest",
|
|
60
61
|
"test:lib:dev": "pnpm test:lib --watch",
|
|
61
62
|
"test:types": "tsc"
|
package/src/stream/translate.ts
CHANGED
|
@@ -143,14 +143,20 @@ export async function* translateSdkStream(
|
|
|
143
143
|
let partialIsStructuredOutput = false
|
|
144
144
|
/** Anthropic message ids whose text/thinking already streamed via partials. */
|
|
145
145
|
const streamedMessageIds = new Set<string>()
|
|
146
|
+
/** Tool-call ids already streamed via partials, so the complete message dedupes them. */
|
|
147
|
+
const streamedToolCallIds = new Set<string>()
|
|
146
148
|
|
|
147
149
|
// Partial-stream state
|
|
148
150
|
let partialMessageId: string | null = null
|
|
149
|
-
|
|
151
|
+
const partialBlockTypes = new Map<number, string>()
|
|
150
152
|
let partialTextMessageId: string | null = null
|
|
151
153
|
let partialTextContent = ''
|
|
152
154
|
let partialTextStarted = false
|
|
153
155
|
let partialReasoningId: string | null = null
|
|
156
|
+
const partialToolCalls = new Map<
|
|
157
|
+
number,
|
|
158
|
+
{ id: string; name: string; args: string }
|
|
159
|
+
>()
|
|
154
160
|
|
|
155
161
|
function* startRun(): Generator<AdapterYieldChunk> {
|
|
156
162
|
if (runStarted) return
|
|
@@ -211,6 +217,41 @@ export async function* translateSdkStream(
|
|
|
211
217
|
partialReasoningId = null
|
|
212
218
|
}
|
|
213
219
|
|
|
220
|
+
function* closePartialToolUse(
|
|
221
|
+
index: number,
|
|
222
|
+
interrupted = false,
|
|
223
|
+
): Generator<AdapterYieldChunk> {
|
|
224
|
+
const toolCall = partialToolCalls.get(index)
|
|
225
|
+
if (toolCall) {
|
|
226
|
+
let input: unknown = {}
|
|
227
|
+
try {
|
|
228
|
+
const parsed = toolCall.args ? JSON.parse(toolCall.args) : {}
|
|
229
|
+
input = parsed && typeof parsed === 'object' ? parsed : {}
|
|
230
|
+
} catch (error) {
|
|
231
|
+
if (!interrupted) throw error
|
|
232
|
+
}
|
|
233
|
+
partialToolCalls.delete(index)
|
|
234
|
+
yield {
|
|
235
|
+
type: EventType.TOOL_CALL_END,
|
|
236
|
+
toolCallId: toolCall.id,
|
|
237
|
+
toolCallName: toolCall.name,
|
|
238
|
+
toolName: toolCall.name,
|
|
239
|
+
model,
|
|
240
|
+
timestamp: now(),
|
|
241
|
+
input,
|
|
242
|
+
}
|
|
243
|
+
unresolvedToolCalls.add(toolCall.id)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function* closePartialToolUses(
|
|
248
|
+
interrupted = false,
|
|
249
|
+
): Generator<AdapterYieldChunk> {
|
|
250
|
+
for (const index of [...partialToolCalls.keys()]) {
|
|
251
|
+
yield* closePartialToolUse(index, interrupted)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
214
255
|
function* emitStructuredOutput(
|
|
215
256
|
object: unknown,
|
|
216
257
|
): Generator<AdapterYieldChunk> {
|
|
@@ -349,9 +390,10 @@ export async function* translateSdkStream(
|
|
|
349
390
|
timestamp: now(),
|
|
350
391
|
}
|
|
351
392
|
} else if (block.type === 'tool_use') {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
)
|
|
393
|
+
const toolBlock = block as { id: string; name: string; input: unknown }
|
|
394
|
+
// Skip the tool call if the partial stream already emitted it.
|
|
395
|
+
if (streamedToolCallIds.has(toolBlock.id)) continue
|
|
396
|
+
yield* emitToolUse(toolBlock)
|
|
355
397
|
}
|
|
356
398
|
}
|
|
357
399
|
}
|
|
@@ -388,6 +430,7 @@ export async function* translateSdkStream(
|
|
|
388
430
|
): Generator<AdapterYieldChunk> {
|
|
389
431
|
yield* closePartialText()
|
|
390
432
|
yield* closePartialReasoning()
|
|
433
|
+
yield* closePartialToolUses()
|
|
391
434
|
yield* synthesizeUnresolvedResults()
|
|
392
435
|
|
|
393
436
|
const usage = buildUsage(message.usage, message.total_cost_usd)
|
|
@@ -458,22 +501,22 @@ export async function* translateSdkStream(
|
|
|
458
501
|
partialMessageId = event.message.id ?? genId()
|
|
459
502
|
streamedMessageIds.add(partialMessageId)
|
|
460
503
|
} else if (event.type === 'content_block_start') {
|
|
461
|
-
|
|
462
|
-
const
|
|
504
|
+
const block = event.content_block
|
|
505
|
+
const partialBlockType = block.type
|
|
506
|
+
partialBlockTypes.set(event.index, partialBlockType)
|
|
463
507
|
partialIsStructuredOutput =
|
|
464
508
|
ctx.expectStructuredOutput === true &&
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
startedBlock.name === SYNTHETIC_STRUCTURED_OUTPUT_TOOL
|
|
509
|
+
block.type === 'tool_use' &&
|
|
510
|
+
block.name === SYNTHETIC_STRUCTURED_OUTPUT_TOOL
|
|
468
511
|
if (partialIsStructuredOutput) {
|
|
469
512
|
partialStructuredJson = ''
|
|
470
|
-
if (
|
|
471
|
-
syntheticOutputToolIds.add(
|
|
513
|
+
if (typeof block.id === 'string') {
|
|
514
|
+
syntheticOutputToolIds.add(block.id)
|
|
472
515
|
}
|
|
473
|
-
if ('input' in
|
|
516
|
+
if ('input' in block) {
|
|
474
517
|
capturedStructuredOutput = rememberStructuredOutput(
|
|
475
518
|
capturedStructuredOutput,
|
|
476
|
-
|
|
519
|
+
block.input,
|
|
477
520
|
)
|
|
478
521
|
}
|
|
479
522
|
}
|
|
@@ -505,6 +548,26 @@ export async function* translateSdkStream(
|
|
|
505
548
|
model,
|
|
506
549
|
timestamp: now(),
|
|
507
550
|
}
|
|
551
|
+
} else if (
|
|
552
|
+
partialBlockType === 'tool_use' &&
|
|
553
|
+
!partialIsStructuredOutput &&
|
|
554
|
+
typeof block.id === 'string' &&
|
|
555
|
+
typeof block.name === 'string'
|
|
556
|
+
) {
|
|
557
|
+
const name = stripMcpPrefix(block.name)
|
|
558
|
+
partialToolCalls.set(event.index, { id: block.id, name, args: '' })
|
|
559
|
+
streamedToolCallIds.add(block.id)
|
|
560
|
+
yield {
|
|
561
|
+
type: EventType.TOOL_CALL_START,
|
|
562
|
+
toolCallId: block.id,
|
|
563
|
+
toolCallName: name,
|
|
564
|
+
toolName: name,
|
|
565
|
+
model,
|
|
566
|
+
timestamp: now(),
|
|
567
|
+
...(partialMessageId !== null && {
|
|
568
|
+
parentMessageId: partialMessageId,
|
|
569
|
+
}),
|
|
570
|
+
}
|
|
508
571
|
}
|
|
509
572
|
} else if (event.type === 'content_block_delta') {
|
|
510
573
|
if (
|
|
@@ -541,8 +604,25 @@ export async function* translateSdkStream(
|
|
|
541
604
|
model,
|
|
542
605
|
timestamp: now(),
|
|
543
606
|
}
|
|
607
|
+
} else if (
|
|
608
|
+
event.delta.type === 'input_json_delta' &&
|
|
609
|
+
typeof event.delta.partial_json === 'string'
|
|
610
|
+
) {
|
|
611
|
+
const toolCall = partialToolCalls.get(event.index)
|
|
612
|
+
if (toolCall) {
|
|
613
|
+
toolCall.args += event.delta.partial_json
|
|
614
|
+
yield {
|
|
615
|
+
type: EventType.TOOL_CALL_ARGS,
|
|
616
|
+
toolCallId: toolCall.id,
|
|
617
|
+
model,
|
|
618
|
+
timestamp: now(),
|
|
619
|
+
delta: event.delta.partial_json,
|
|
620
|
+
}
|
|
621
|
+
}
|
|
544
622
|
}
|
|
545
623
|
} else if (event.type === 'content_block_stop') {
|
|
624
|
+
const partialBlockType = partialBlockTypes.get(event.index)
|
|
625
|
+
partialBlockTypes.delete(event.index)
|
|
546
626
|
if (partialIsStructuredOutput && partialStructuredJson !== '') {
|
|
547
627
|
try {
|
|
548
628
|
capturedStructuredOutput = rememberStructuredOutput(
|
|
@@ -557,8 +637,9 @@ export async function* translateSdkStream(
|
|
|
557
637
|
yield* closePartialText()
|
|
558
638
|
} else if (partialBlockType === 'thinking') {
|
|
559
639
|
yield* closePartialReasoning()
|
|
640
|
+
} else if (partialBlockType === 'tool_use') {
|
|
641
|
+
yield* closePartialToolUse(event.index)
|
|
560
642
|
}
|
|
561
|
-
partialBlockType = null
|
|
562
643
|
partialIsStructuredOutput = false
|
|
563
644
|
partialStructuredJson = ''
|
|
564
645
|
}
|
|
@@ -605,10 +686,11 @@ export async function* translateSdkStream(
|
|
|
605
686
|
// harness-internal and intentionally ignored.
|
|
606
687
|
}
|
|
607
688
|
} catch (error) {
|
|
608
|
-
// The run is dying (abort or SDK failure).
|
|
609
|
-
// with a synthetic result
|
|
610
|
-
// scan doesn't try to execute them
|
|
611
|
-
// error as RUN_ERROR.
|
|
689
|
+
// The run is dying (abort or SDK failure). Close any half-streamed tool
|
|
690
|
+
// calls, then pair every started tool call with a synthetic result so the
|
|
691
|
+
// next request's pending-tool-call scan doesn't try to execute them. Then
|
|
692
|
+
// let the adapter surface the error as RUN_ERROR.
|
|
693
|
+
yield* closePartialToolUses(true)
|
|
612
694
|
yield* synthesizeUnresolvedResults()
|
|
613
695
|
throw error
|
|
614
696
|
}
|