@tanstack/ai 0.59.0 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/esm/activities/chat/adapter.d.ts +9 -0
- package/dist/esm/activities/chat/adapter.js +1 -0
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/agents/define-agent.d.ts +17 -5
- package/dist/esm/activities/chat/agents/define-agent.js.map +1 -1
- package/dist/esm/activities/chat/agents/spawn.d.ts +2 -0
- package/dist/esm/activities/chat/agents/spawn.js +8 -5
- package/dist/esm/activities/chat/agents/spawn.js.map +1 -1
- package/dist/esm/activities/chat/index.js +289 -78
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +35 -19
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +1 -0
- package/dist/esm/activities/chat/middleware/types.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.d.ts +2 -2
- package/dist/esm/activities/chat/stream/message-updaters.js +11 -3
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +11 -10
- package/dist/esm/activities/chat/stream/processor.js +51 -28
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +16 -2
- package/dist/esm/activities/chat/tools/tool-calls.js +57 -16
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +4 -0
- package/dist/esm/activities/chat/tools/tool-definition.js +4 -0
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/embed/adapter.d.ts +7 -0
- package/dist/esm/activities/embed/adapter.js +1 -0
- package/dist/esm/activities/embed/adapter.js.map +1 -1
- package/dist/esm/activities/embed/index.js +2 -0
- package/dist/esm/activities/embed/index.js.map +1 -1
- package/dist/esm/activities/evaluate/adapter.d.ts +4 -0
- package/dist/esm/activities/evaluate/adapter.js.map +1 -1
- package/dist/esm/activities/evaluate/index.d.ts +4 -0
- package/dist/esm/activities/evaluate/index.js +3 -1
- package/dist/esm/activities/evaluate/index.js.map +1 -1
- package/dist/esm/activities/files/adapter.d.ts +97 -0
- package/dist/esm/activities/files/adapter.js +45 -0
- package/dist/esm/activities/files/adapter.js.map +1 -0
- package/dist/esm/activities/files/index.d.ts +66 -0
- package/dist/esm/activities/files/index.js +78 -0
- package/dist/esm/activities/files/index.js.map +1 -0
- package/dist/esm/activities/generateImage/adapter.d.ts +8 -0
- package/dist/esm/activities/generateImage/adapter.js +1 -0
- package/dist/esm/activities/generateImage/adapter.js.map +1 -1
- package/dist/esm/activities/generateImage/index.js +2 -0
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.d.ts +8 -0
- package/dist/esm/activities/generateVideo/adapter.js +1 -0
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.js +3 -0
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/generateWorld/adapter.d.ts +4 -2
- package/dist/esm/activities/generateWorld/adapter.js.map +1 -1
- package/dist/esm/activities/generateWorld/index.d.ts +4 -3
- package/dist/esm/activities/generateWorld/index.js +5 -4
- package/dist/esm/activities/generateWorld/index.js.map +1 -1
- package/dist/esm/activities/index.d.ts +6 -3
- package/dist/esm/activities/index.js +13 -11
- package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +2 -0
- package/dist/esm/activities/summarize/chat-stream-summarize.js +8 -8
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/client.d.ts +3 -1
- package/dist/esm/client.js +2 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +4 -3
- package/dist/esm/index.js +5 -3
- package/dist/esm/interrupt-resume.js +29 -4
- package/dist/esm/interrupt-resume.js.map +1 -1
- package/dist/esm/middlewares/otel.d.ts +5 -2
- package/dist/esm/middlewares/otel.js +114 -0
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/types.d.ts +114 -14
- package/dist/esm/utilities/ag-ui-wire.js +39 -16
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/content-source.d.ts +60 -0
- package/dist/esm/utilities/content-source.js +85 -0
- package/dist/esm/utilities/content-source.js.map +1 -0
- package/dist/esm/utilities/provider-executed.d.ts +7 -0
- package/dist/esm/utilities/provider-executed.js +10 -1
- package/dist/esm/utilities/provider-executed.js.map +1 -1
- package/dist/esm/utilities/tool-result.d.ts +14 -3
- package/dist/esm/utilities/tool-result.js +26 -3
- package/dist/esm/utilities/tool-result.js.map +1 -1
- package/package.json +4 -4
- package/skills/ai-core/adapter-configuration/SKILL.md +62 -0
- package/skills/ai-core/chat-experience/SKILL.md +134 -0
- package/skills/ai-core/media-generation/SKILL.md +8 -0
- package/skills/ai-core/tool-calling/SKILL.md +103 -0
- package/src/activities/chat/adapter.ts +10 -0
- package/src/activities/chat/agents/define-agent.ts +20 -3
- package/src/activities/chat/agents/spawn.ts +20 -12
- package/src/activities/chat/index.ts +458 -99
- package/src/activities/chat/messages.ts +52 -6
- package/src/activities/chat/middleware/types.ts +1 -0
- package/src/activities/chat/stream/message-updaters.ts +27 -2
- package/src/activities/chat/stream/processor.ts +81 -49
- package/src/activities/chat/tools/tool-calls.ts +104 -9
- package/src/activities/chat/tools/tool-definition.ts +8 -0
- package/src/activities/embed/adapter.ts +7 -0
- package/src/activities/embed/index.ts +5 -0
- package/src/activities/evaluate/adapter.ts +4 -0
- package/src/activities/evaluate/index.ts +6 -0
- package/src/activities/files/adapter.ts +120 -0
- package/src/activities/files/index.ts +113 -0
- package/src/activities/generateImage/adapter.ts +8 -0
- package/src/activities/generateImage/index.ts +4 -0
- package/src/activities/generateVideo/adapter.ts +8 -0
- package/src/activities/generateVideo/index.ts +7 -0
- package/src/activities/generateWorld/adapter.ts +4 -2
- package/src/activities/generateWorld/index.ts +7 -6
- package/src/activities/index.ts +25 -1
- package/src/activities/summarize/chat-stream-summarize.ts +22 -12
- package/src/client.ts +8 -0
- package/src/index.ts +17 -0
- package/src/interrupt-resume.ts +55 -4
- package/src/middlewares/otel.ts +161 -3
- package/src/types.ts +114 -14
- package/src/utilities/ag-ui-wire.ts +72 -17
- package/src/utilities/content-source.ts +138 -0
- package/src/utilities/provider-executed.ts +13 -0
- package/src/utilities/tool-result.ts +45 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ContentPart, UIMessage } from '../../../types.js';
|
|
1
|
+
import { ContentPart, ToolResultOutcome, UIMessage } from '../../../types.js';
|
|
2
2
|
import { ToolCallState, ToolResultState } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Update or add a text part to a message.
|
|
@@ -22,7 +22,7 @@ export declare function updateToolCallPart(messages: Array<UIMessage>, messageId
|
|
|
22
22
|
/**
|
|
23
23
|
* Update or add a tool result part to a message.
|
|
24
24
|
*/
|
|
25
|
-
export declare function updateToolResultPart(messages: Array<UIMessage>, messageId: string, toolCallId: string, content: string | Array<ContentPart>, state: ToolResultState, error?: string): Array<UIMessage>;
|
|
25
|
+
export declare function updateToolResultPart(messages: Array<UIMessage>, messageId: string, toolCallId: string, content: string | Array<ContentPart>, state: ToolResultState, error?: string, outcome?: ToolResultOutcome): Array<UIMessage>;
|
|
26
26
|
/**
|
|
27
27
|
* Update a tool call part with approval request metadata.
|
|
28
28
|
*/
|
|
@@ -63,7 +63,7 @@ function updateToolCallPart(messages, messageId, toolCall) {
|
|
|
63
63
|
/**
|
|
64
64
|
* Update or add a tool result part to a message.
|
|
65
65
|
*/
|
|
66
|
-
function updateToolResultPart(messages, messageId, toolCallId, content, state, error) {
|
|
66
|
+
function updateToolResultPart(messages, messageId, toolCallId, content, state, error, outcome) {
|
|
67
67
|
return messages.map((msg) => {
|
|
68
68
|
if (msg.id !== messageId) return msg;
|
|
69
69
|
const parts = [...msg.parts];
|
|
@@ -73,6 +73,7 @@ function updateToolResultPart(messages, messageId, toolCallId, content, state, e
|
|
|
73
73
|
toolCallId,
|
|
74
74
|
content,
|
|
75
75
|
state,
|
|
76
|
+
...outcome !== void 0 && { outcome },
|
|
76
77
|
...error && { error }
|
|
77
78
|
};
|
|
78
79
|
if (resultPartIndex >= 0) parts[resultPartIndex] = toolResultPart;
|
|
@@ -275,12 +276,19 @@ function updateThinkingPart(messages, messageId, stepId, content, signature) {
|
|
|
275
276
|
return messages.map((msg) => {
|
|
276
277
|
if (msg.id !== messageId) return msg;
|
|
277
278
|
const parts = [...msg.parts];
|
|
278
|
-
|
|
279
|
+
let thinkingPartIndex = parts.findIndex((p) => p.type === "thinking" && p.stepId === stepId);
|
|
280
|
+
let adopted;
|
|
281
|
+
if (thinkingPartIndex < 0) {
|
|
282
|
+
thinkingPartIndex = parts.findIndex((p) => p.type === "thinking" && p.stepId === void 0);
|
|
283
|
+
const candidate = parts[thinkingPartIndex];
|
|
284
|
+
if (candidate?.type === "thinking") adopted = candidate;
|
|
285
|
+
}
|
|
286
|
+
const nextSignature = signature ?? adopted?.signature;
|
|
279
287
|
const thinkingPart = {
|
|
280
288
|
type: "thinking",
|
|
281
289
|
content,
|
|
282
290
|
stepId,
|
|
283
|
-
...
|
|
291
|
+
...nextSignature && { signature: nextSignature }
|
|
284
292
|
};
|
|
285
293
|
if (thinkingPartIndex >= 0) parts[thinkingPartIndex] = thinkingPart;
|
|
286
294
|
else parts.push(thinkingPart);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-updaters.js","names":[],"sources":["../../../../../src/activities/chat/stream/message-updaters.ts"],"sourcesContent":["/**\n * Message Updaters (Internal)\n *\n * Internal helper functions for updating UIMessage parts.\n * These are used by StreamProcessor to manage the message array.\n */\n\nimport { parsePartialJSON } from './json-parser'\nimport type {\n ContentPart,\n StructuredOutputPart,\n ThinkingPart,\n ToolCallPart,\n ToolResultPart,\n UIMessage,\n} from '../../../types'\nimport type { ToolCallState, ToolResultState } from './types'\n\n/**\n * Update or add a text part to a message.\n *\n * If the last part is a text part, update it (continuing the same text segment).\n * Otherwise, create a new text part (starting a new text segment after tool calls).\n */\nexport function updateTextPart(\n messages: Array<UIMessage>,\n messageId: string,\n content: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const lastPart = parts.length > 0 ? parts[parts.length - 1] : null\n\n if (lastPart && lastPart.type === 'text') {\n // Update the last text part (continuing same text segment)\n parts[parts.length - 1] = { type: 'text', content }\n } else {\n // Create new text part (starting new text segment after tool calls/results)\n parts.push({ type: 'text', content })\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a tool call part to a message.\n */\nexport function updateToolCallPart(\n messages: Array<UIMessage>,\n messageId: string,\n toolCall: {\n id: string\n name: string\n arguments: string\n state: ToolCallState\n /** Parsed input — set when the arguments are complete. */\n input?: unknown\n metadata?: Record<string, unknown>\n },\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existing = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCall.id,\n )\n\n // Carry forward metadata from either the new toolCall or the existing\n // part. Once the adapter has emitted metadata for a tool call (e.g.\n // Gemini's thoughtSignature on TOOL_CALL_START) we must not lose it on\n // subsequent updates that don't re-supply it.\n const metadata = toolCall.metadata ?? existing?.metadata\n // Same for the parsed input: it's supplied once at completion, so\n // subsequent arg-less updates (approval, etc.) must not drop it.\n const input = toolCall.input ?? existing?.input\n\n const toolCallPart: ToolCallPart = {\n type: 'tool-call',\n id: toolCall.id,\n name: toolCall.name,\n arguments: toolCall.arguments,\n state: toolCall.state,\n // Carry forward approval, output and parsed input from the existing part\n ...(existing?.approval && { approval: { ...existing.approval } }),\n ...(existing?.output !== undefined && { output: existing.output }),\n ...(input !== undefined && { input }),\n ...(metadata !== undefined && { metadata }),\n }\n\n if (existing) {\n // Update existing tool call\n parts[parts.indexOf(existing)] = toolCallPart\n } else {\n // Add new tool call at the end (preserve natural streaming order)\n parts.push(toolCallPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a tool result part to a message.\n */\nexport function updateToolResultPart(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n content: string | Array<ContentPart>,\n state: ToolResultState,\n error?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const resultPartIndex = parts.findIndex(\n (p): p is ToolResultPart =>\n p.type === 'tool-result' && p.toolCallId === toolCallId,\n )\n\n const toolResultPart: ToolResultPart = {\n type: 'tool-result',\n toolCallId,\n content,\n state,\n ...(error && { error }),\n }\n\n if (resultPartIndex >= 0) {\n parts[resultPartIndex] = toolResultPart\n } else {\n parts.push(toolResultPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with approval request metadata.\n */\nexport function updateToolCallApproval(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n approvalId: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = {\n ...toolCallPart,\n state: 'approval-requested',\n approval: {\n id: approvalId,\n needsApproval: true,\n },\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part's state (e.g., to \"input-complete\").\n */\nexport function updateToolCallState(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n state: ToolCallState,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = { ...toolCallPart, state }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with output.\n * Searches all messages to find the tool call by ID.\n */\nexport function updateToolCallWithOutput(\n messages: Array<UIMessage>,\n toolCallId: string,\n output: any,\n state?: ToolCallState,\n errorText?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = {\n ...toolCallPart,\n output: errorText ? { error: errorText } : output,\n state: state ?? (errorText ? 'error' : 'complete'),\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with approval response.\n * Searches all messages to find the tool call by approval ID.\n */\nexport function updateToolCallApprovalResponse(\n messages: Array<UIMessage>,\n approvalId: string,\n approved: boolean,\n): Array<UIMessage> {\n return messages.map((msg) => {\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart =>\n p.type === 'tool-call' && p.approval?.id === approvalId,\n )\n\n if (toolCallPart && toolCallPart.approval) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = {\n ...toolCallPart,\n approval: { ...toolCallPart.approval, approved },\n state: 'approval-responded',\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Append a delta to the structured-output part on `messageId`, or create one\n * if absent. Progressive parse of the accumulated buffer fills `partial`.\n *\n * Callers must only invoke this while the part is still in flight — the\n * helper unconditionally writes `status: 'streaming'`, so feeding it a delta\n * after a `complete`/`error` terminal would regress the part. In practice the\n * processor gates calls via `structuredMessageIds`, which is dropped on\n * terminal events.\n *\n * If the progressive parse returns null/undefined (the buffer is not yet a\n * parseable JSON prefix), the previously-good `partial` is preserved so the\n * UI doesn't flicker back to empty for a single render.\n */\nexport function appendStructuredOutputDelta(\n messages: Array<UIMessage>,\n messageId: string,\n delta: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n const existing =\n existingIndex >= 0 ? (parts[existingIndex] as StructuredOutputPart) : null\n\n const nextRaw = (existing?.raw ?? '') + delta\n const progressive = parsePartialJSON(nextRaw)\n const nextPartial =\n progressive !== undefined && progressive !== null\n ? progressive\n : existing?.partial\n\n const nextPart: StructuredOutputPart = {\n type: 'structured-output',\n status: 'streaming',\n raw: nextRaw,\n ...(nextPartial !== undefined ? { partial: nextPartial } : {}),\n ...(existing?.reasoning !== undefined\n ? { reasoning: existing.reasoning }\n : {}),\n }\n\n if (existingIndex >= 0) {\n parts[existingIndex] = nextPart\n } else {\n parts.push(nextPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Snap the structured-output part on `messageId` to `complete` with the\n * validated `data`. Picks the freshest available `raw` so the wire\n * round-trip stays internally consistent:\n *\n * 1. Caller-supplied `raw` (the original streamed bytes from the model).\n * 2. The existing part's `raw` (deltas accumulated before this terminal).\n * 3. `JSON.stringify(data)` as a defensive fallback for terminal-only\n * completes that never shipped raw — keeps the part self-consistent\n * so downstream consumers never see a complete part with empty raw.\n */\nexport function completeStructuredOutputPart(\n messages: Array<UIMessage>,\n messageId: string,\n data: unknown,\n raw: string,\n reasoning?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n\n const existingRaw =\n existingIndex >= 0\n ? (parts[existingIndex] as StructuredOutputPart).raw\n : ''\n let resolvedRaw = raw || existingRaw\n if (resolvedRaw === '' && data !== undefined) {\n try {\n resolvedRaw = JSON.stringify(data)\n } catch {\n // Unserializable (circular, BigInt, throwing toJSON). Leave raw\n // empty. Both downstream paths handle this: `ag-ui-wire.ts`\n // `collectText` skips complete parts with empty raw entirely, and\n // `uiMessageToModelMessages` falls back to a defensive\n // `safeJsonStringify(data)` which itself returns `''` for the same\n // unserializable inputs — so the turn is silently dropped from the\n // next request rather than shipping garbage or crashing the stream.\n }\n }\n\n const nextPart: StructuredOutputPart = {\n type: 'structured-output',\n status: 'complete',\n data,\n partial: data,\n raw: resolvedRaw,\n ...(reasoning !== undefined ? { reasoning } : {}),\n }\n\n if (existingIndex >= 0) {\n parts[existingIndex] = nextPart\n } else {\n parts.push(nextPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Mark the structured-output part on `messageId` as errored. If no part\n * exists yet — RUN_ERROR fired after `structured-output.start` but before\n * any delta — create an empty errored placeholder so consumers have\n * something renderable. Existing complete parts are left alone (an error\n * after a successful complete should not retroactively un-complete it).\n */\nexport function errorStructuredOutputPart(\n messages: Array<UIMessage>,\n messageId: string,\n errorMessage: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n\n if (existingIndex < 0) {\n parts.push({\n type: 'structured-output',\n status: 'error',\n raw: '',\n errorMessage,\n })\n return { ...msg, parts }\n }\n\n const existing = parts[existingIndex] as StructuredOutputPart\n if (existing.status === 'complete') {\n return msg\n }\n parts[existingIndex] = {\n ...existing,\n status: 'error',\n errorMessage,\n }\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a thinking part to a message, keyed by stepId.\n * Each distinct stepId produces its own ThinkingPart.\n */\nexport function updateThinkingPart(\n messages: Array<UIMessage>,\n messageId: string,\n stepId: string,\n content: string,\n signature?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const thinkingPartIndex = parts.findIndex(\n (p) => p.type === 'thinking' && p.stepId === stepId,\n )\n\n const thinkingPart: ThinkingPart = {\n type: 'thinking',\n content,\n stepId,\n ...(signature && { signature }),\n }\n\n if (thinkingPartIndex >= 0) {\n // Update existing thinking part for this step\n parts[thinkingPartIndex] = thinkingPart\n } else {\n // Add new thinking part at the end (preserve natural streaming order)\n parts.push(thinkingPart)\n }\n\n return { ...msg, parts }\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"message-updaters.js","names":[],"sources":["../../../../../src/activities/chat/stream/message-updaters.ts"],"sourcesContent":["/**\n * Message Updaters (Internal)\n *\n * Internal helper functions for updating UIMessage parts.\n * These are used by StreamProcessor to manage the message array.\n */\n\nimport { parsePartialJSON } from './json-parser'\nimport type {\n ContentPart,\n StructuredOutputPart,\n ThinkingPart,\n ToolCallPart,\n ToolResultOutcome,\n ToolResultPart,\n UIMessage,\n} from '../../../types'\nimport type { ToolCallState, ToolResultState } from './types'\n\n/**\n * Update or add a text part to a message.\n *\n * If the last part is a text part, update it (continuing the same text segment).\n * Otherwise, create a new text part (starting a new text segment after tool calls).\n */\nexport function updateTextPart(\n messages: Array<UIMessage>,\n messageId: string,\n content: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const lastPart = parts.length > 0 ? parts[parts.length - 1] : null\n\n if (lastPart && lastPart.type === 'text') {\n // Update the last text part (continuing same text segment)\n parts[parts.length - 1] = { type: 'text', content }\n } else {\n // Create new text part (starting new text segment after tool calls/results)\n parts.push({ type: 'text', content })\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a tool call part to a message.\n */\nexport function updateToolCallPart(\n messages: Array<UIMessage>,\n messageId: string,\n toolCall: {\n id: string\n name: string\n arguments: string\n state: ToolCallState\n /** Parsed input — set when the arguments are complete. */\n input?: unknown\n metadata?: Record<string, unknown>\n },\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existing = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCall.id,\n )\n\n // Carry forward metadata from either the new toolCall or the existing\n // part. Once the adapter has emitted metadata for a tool call (e.g.\n // Gemini's thoughtSignature on TOOL_CALL_START) we must not lose it on\n // subsequent updates that don't re-supply it.\n const metadata = toolCall.metadata ?? existing?.metadata\n // Same for the parsed input: it's supplied once at completion, so\n // subsequent arg-less updates (approval, etc.) must not drop it.\n const input = toolCall.input ?? existing?.input\n\n const toolCallPart: ToolCallPart = {\n type: 'tool-call',\n id: toolCall.id,\n name: toolCall.name,\n arguments: toolCall.arguments,\n state: toolCall.state,\n // Carry forward approval, output and parsed input from the existing part\n ...(existing?.approval && { approval: { ...existing.approval } }),\n ...(existing?.output !== undefined && { output: existing.output }),\n ...(input !== undefined && { input }),\n ...(metadata !== undefined && { metadata }),\n }\n\n if (existing) {\n // Update existing tool call\n parts[parts.indexOf(existing)] = toolCallPart\n } else {\n // Add new tool call at the end (preserve natural streaming order)\n parts.push(toolCallPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a tool result part to a message.\n */\nexport function updateToolResultPart(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n content: string | Array<ContentPart>,\n state: ToolResultState,\n error?: string,\n outcome?: ToolResultOutcome,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const resultPartIndex = parts.findIndex(\n (p): p is ToolResultPart =>\n p.type === 'tool-result' && p.toolCallId === toolCallId,\n )\n\n const toolResultPart: ToolResultPart = {\n type: 'tool-result',\n toolCallId,\n content,\n state,\n ...(outcome !== undefined && { outcome }),\n ...(error && { error }),\n }\n\n if (resultPartIndex >= 0) {\n parts[resultPartIndex] = toolResultPart\n } else {\n parts.push(toolResultPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with approval request metadata.\n */\nexport function updateToolCallApproval(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n approvalId: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = {\n ...toolCallPart,\n state: 'approval-requested',\n approval: {\n id: approvalId,\n needsApproval: true,\n },\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part's state (e.g., to \"input-complete\").\n */\nexport function updateToolCallState(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n state: ToolCallState,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = { ...toolCallPart, state }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with output.\n * Searches all messages to find the tool call by ID.\n */\nexport function updateToolCallWithOutput(\n messages: Array<UIMessage>,\n toolCallId: string,\n output: any,\n state?: ToolCallState,\n errorText?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = {\n ...toolCallPart,\n output: errorText ? { error: errorText } : output,\n state: state ?? (errorText ? 'error' : 'complete'),\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with approval response.\n * Searches all messages to find the tool call by approval ID.\n */\nexport function updateToolCallApprovalResponse(\n messages: Array<UIMessage>,\n approvalId: string,\n approved: boolean,\n): Array<UIMessage> {\n return messages.map((msg) => {\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart =>\n p.type === 'tool-call' && p.approval?.id === approvalId,\n )\n\n if (toolCallPart && toolCallPart.approval) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = {\n ...toolCallPart,\n approval: { ...toolCallPart.approval, approved },\n state: 'approval-responded',\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Append a delta to the structured-output part on `messageId`, or create one\n * if absent. Progressive parse of the accumulated buffer fills `partial`.\n *\n * Callers must only invoke this while the part is still in flight — the\n * helper unconditionally writes `status: 'streaming'`, so feeding it a delta\n * after a `complete`/`error` terminal would regress the part. In practice the\n * processor gates calls via `structuredMessageIds`, which is dropped on\n * terminal events.\n *\n * If the progressive parse returns null/undefined (the buffer is not yet a\n * parseable JSON prefix), the previously-good `partial` is preserved so the\n * UI doesn't flicker back to empty for a single render.\n */\nexport function appendStructuredOutputDelta(\n messages: Array<UIMessage>,\n messageId: string,\n delta: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n const existing =\n existingIndex >= 0 ? (parts[existingIndex] as StructuredOutputPart) : null\n\n const nextRaw = (existing?.raw ?? '') + delta\n const progressive = parsePartialJSON(nextRaw)\n const nextPartial =\n progressive !== undefined && progressive !== null\n ? progressive\n : existing?.partial\n\n const nextPart: StructuredOutputPart = {\n type: 'structured-output',\n status: 'streaming',\n raw: nextRaw,\n ...(nextPartial !== undefined ? { partial: nextPartial } : {}),\n ...(existing?.reasoning !== undefined\n ? { reasoning: existing.reasoning }\n : {}),\n }\n\n if (existingIndex >= 0) {\n parts[existingIndex] = nextPart\n } else {\n parts.push(nextPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Snap the structured-output part on `messageId` to `complete` with the\n * validated `data`. Picks the freshest available `raw` so the wire\n * round-trip stays internally consistent:\n *\n * 1. Caller-supplied `raw` (the original streamed bytes from the model).\n * 2. The existing part's `raw` (deltas accumulated before this terminal).\n * 3. `JSON.stringify(data)` as a defensive fallback for terminal-only\n * completes that never shipped raw — keeps the part self-consistent\n * so downstream consumers never see a complete part with empty raw.\n */\nexport function completeStructuredOutputPart(\n messages: Array<UIMessage>,\n messageId: string,\n data: unknown,\n raw: string,\n reasoning?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n\n const existingRaw =\n existingIndex >= 0\n ? (parts[existingIndex] as StructuredOutputPart).raw\n : ''\n let resolvedRaw = raw || existingRaw\n if (resolvedRaw === '' && data !== undefined) {\n try {\n resolvedRaw = JSON.stringify(data)\n } catch {\n // Unserializable (circular, BigInt, throwing toJSON). Leave raw\n // empty. Both downstream paths handle this: `ag-ui-wire.ts`\n // `collectText` skips complete parts with empty raw entirely, and\n // `uiMessageToModelMessages` falls back to a defensive\n // `safeJsonStringify(data)` which itself returns `''` for the same\n // unserializable inputs — so the turn is silently dropped from the\n // next request rather than shipping garbage or crashing the stream.\n }\n }\n\n const nextPart: StructuredOutputPart = {\n type: 'structured-output',\n status: 'complete',\n data,\n partial: data,\n raw: resolvedRaw,\n ...(reasoning !== undefined ? { reasoning } : {}),\n }\n\n if (existingIndex >= 0) {\n parts[existingIndex] = nextPart\n } else {\n parts.push(nextPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Mark the structured-output part on `messageId` as errored. If no part\n * exists yet — RUN_ERROR fired after `structured-output.start` but before\n * any delta — create an empty errored placeholder so consumers have\n * something renderable. Existing complete parts are left alone (an error\n * after a successful complete should not retroactively un-complete it).\n */\nexport function errorStructuredOutputPart(\n messages: Array<UIMessage>,\n messageId: string,\n errorMessage: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n\n if (existingIndex < 0) {\n parts.push({\n type: 'structured-output',\n status: 'error',\n raw: '',\n errorMessage,\n })\n return { ...msg, parts }\n }\n\n const existing = parts[existingIndex] as StructuredOutputPart\n if (existing.status === 'complete') {\n return msg\n }\n parts[existingIndex] = {\n ...existing,\n status: 'error',\n errorMessage,\n }\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a thinking part to a message, keyed by stepId.\n * Each distinct stepId produces its own ThinkingPart.\n */\nexport function updateThinkingPart(\n messages: Array<UIMessage>,\n messageId: string,\n stepId: string,\n content: string,\n signature?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n let thinkingPartIndex = parts.findIndex(\n (p) => p.type === 'thinking' && p.stepId === stepId,\n )\n\n // A hydrated message carries its thinking without a stepId: the stored form\n // has no field for one, so `modelMessageToUIMessage` cannot put it back.\n // When a run is rejoined mid-stream the replayed reasoning is keyed by\n // stepId, matches nothing, and gets appended -- leaving a second thinking\n // part sitting after the answer text. Adopt the first stepId-less thinking\n // part instead, so the replay lands on the part it belongs to. Live\n // streaming always writes a stepId, so the only parts this can match are\n // hydrated ones.\n let adopted: ThinkingPart | undefined\n if (thinkingPartIndex < 0) {\n thinkingPartIndex = parts.findIndex(\n (p) => p.type === 'thinking' && p.stepId === undefined,\n )\n const candidate = parts[thinkingPartIndex]\n if (candidate?.type === 'thinking') adopted = candidate\n }\n\n // Keep the signature the hydrated part already had when this update does\n // not carry one; losing it would strip the provider's encrypted reasoning\n // from a message that is about to be sent back.\n const nextSignature = signature ?? adopted?.signature\n\n const thinkingPart: ThinkingPart = {\n type: 'thinking',\n content,\n stepId,\n ...(nextSignature && { signature: nextSignature }),\n }\n\n if (thinkingPartIndex >= 0) {\n // Update existing thinking part for this step\n parts[thinkingPartIndex] = thinkingPart\n } else {\n // Add new thinking part at the end (preserve natural streaming order)\n parts.push(thinkingPart)\n }\n\n return { ...msg, parts }\n })\n}\n"],"mappings":"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"}
|
|
@@ -369,16 +369,17 @@ export declare class StreamProcessor {
|
|
|
369
369
|
* Handle TOOL_CALL_END event — arguments are finalized (input-complete).
|
|
370
370
|
* Tool output arrives on TOOL_CALL_RESULT, not on this event.
|
|
371
371
|
*
|
|
372
|
-
* If TOOL_CALL_END carries parsed `input`,
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
* server_tool_use / web_search — issue #839) and
|
|
376
|
-
*
|
|
372
|
+
* If TOOL_CALL_END carries parsed `input`, it is the canonical arguments:
|
|
373
|
+
* write it into the accumulated string and override the rendered part's
|
|
374
|
+
* `input` with it. That covers adapters that deliver the whole input on END
|
|
375
|
+
* (e.g. Anthropic server_tool_use / web_search — issue #839) and adapters
|
|
376
|
+
* that stream the wire arguments and then normalize them (OpenAI strict-mode
|
|
377
|
+
* null widening, undone by the adapter after #939), so `arguments` and
|
|
378
|
+
* `input` never disagree on the persisted part.
|
|
377
379
|
*
|
|
378
380
|
* @see docs/chat-architecture.md#single-shot-tool-call-response — End-to-end flow
|
|
379
381
|
*/
|
|
380
382
|
private handleToolCallEndEvent;
|
|
381
|
-
private extractToolResultError;
|
|
382
383
|
/**
|
|
383
384
|
* Handle TOOL_CALL_RESULT event (AG-UI spec).
|
|
384
385
|
*
|
|
@@ -487,11 +488,11 @@ export declare class StreamProcessor {
|
|
|
487
488
|
private completeToolCall;
|
|
488
489
|
private isToolCallPartAwaitingUserAction;
|
|
489
490
|
/**
|
|
490
|
-
* Whether the rendered tool-call part for the given id has reached
|
|
491
|
-
* terminal 'error' state. Used to prevent the completion
|
|
492
|
-
* downgrading a
|
|
491
|
+
* Whether the rendered tool-call part for the given id has reached a
|
|
492
|
+
* terminal 'error' or 'complete' state. Used to prevent the completion
|
|
493
|
+
* safety net from downgrading a finished call back to 'input-complete'.
|
|
493
494
|
*/
|
|
494
|
-
private
|
|
495
|
+
private isToolCallPartTerminal;
|
|
495
496
|
/**
|
|
496
497
|
* Emit pending text update for a specific message.
|
|
497
498
|
*
|
|
@@ -2,9 +2,9 @@ import { EventType } from "../../../types.js";
|
|
|
2
2
|
import { mergeMetadata, tanstackMetadata } from "../../../utilities/merge-metadata.js";
|
|
3
3
|
import { runErrorEventToError } from "../../../utilities/errors.js";
|
|
4
4
|
import { INTERRUPT_BINDING_METADATA_KEY } from "../../../interrupt-resume.js";
|
|
5
|
-
import {
|
|
5
|
+
import { isAssistantSegmentOf, isProviderExecutedToolCall } from "../../../utilities/provider-executed.js";
|
|
6
|
+
import { isContentPartArray, isToolResultOutcome, normalizeToolResult, toolResultErrorText } from "../../../utilities/tool-result.js";
|
|
6
7
|
import { splitSubagentWire } from "../../../utilities/subagent-wire.js";
|
|
7
|
-
import { isProviderExecutedToolCall } from "../../../utilities/provider-executed.js";
|
|
8
8
|
import { aguiContentToContentParts, aguiSnapshotMessageToUIMessage, coerceCreatedAt, generateMessageId, uiMessageToModelMessages } from "../messages.js";
|
|
9
9
|
import { getChunkRunId } from "../../../utilities/chunk-ids.js";
|
|
10
10
|
import { defaultJSONParser } from "./json-parser.js";
|
|
@@ -40,6 +40,22 @@ function interruptBatchHasGeneric(interrupts) {
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
|
+
* The canonical arguments string for a `TOOL_CALL_END.input`, or `undefined`
|
|
44
|
+
* when JSON cannot carry it: `JSON.stringify` returns `undefined` (despite
|
|
45
|
+
* its declared type) for a top-level function or symbol and throws on BigInt
|
|
46
|
+
* and circular references. `null` is not tool arguments either, so it also
|
|
47
|
+
* keeps the streamed value instead of writing `arguments = "null"`.
|
|
48
|
+
*/
|
|
49
|
+
function serializeToolInput(input) {
|
|
50
|
+
if (input === void 0 || input === null) return void 0;
|
|
51
|
+
try {
|
|
52
|
+
const serialized = JSON.stringify(input);
|
|
53
|
+
return typeof serialized === "string" ? serialized : void 0;
|
|
54
|
+
} catch {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
43
59
|
* StreamProcessor - State machine for processing AI response streams
|
|
44
60
|
*
|
|
45
61
|
* Manages the full UIMessage[] conversation and emits events on changes.
|
|
@@ -890,16 +906,23 @@ var StreamProcessor = class StreamProcessor {
|
|
|
890
906
|
pending.push(msg);
|
|
891
907
|
continue;
|
|
892
908
|
}
|
|
909
|
+
let next = msg;
|
|
893
910
|
if (msg.role === "assistant" && pending.length > 0 && !isToolResultOnly(msg)) {
|
|
894
|
-
|
|
911
|
+
next = {
|
|
895
912
|
...msg,
|
|
896
913
|
parts: [...pending.flatMap(thinkingParts), ...msg.parts]
|
|
897
|
-
}
|
|
914
|
+
};
|
|
898
915
|
pending = [];
|
|
916
|
+
} else flushPending();
|
|
917
|
+
const prev = out.at(-1);
|
|
918
|
+
if (prev?.role === "assistant" && next.role === "assistant" && isAssistantSegmentOf(next.id, prev.id)) {
|
|
919
|
+
out[out.length - 1] = {
|
|
920
|
+
...prev,
|
|
921
|
+
parts: [...prev.parts, ...next.parts]
|
|
922
|
+
};
|
|
899
923
|
continue;
|
|
900
924
|
}
|
|
901
|
-
|
|
902
|
-
out.push(msg);
|
|
925
|
+
out.push(next);
|
|
903
926
|
}
|
|
904
927
|
flushPending();
|
|
905
928
|
return out;
|
|
@@ -965,7 +988,7 @@ var StreamProcessor = class StreamProcessor {
|
|
|
965
988
|
} catch {
|
|
966
989
|
output = result.content;
|
|
967
990
|
}
|
|
968
|
-
const errorText = result.state === "error" ?
|
|
991
|
+
const errorText = result.state === "error" ? toolResultErrorText(output) : void 0;
|
|
969
992
|
next = {
|
|
970
993
|
...next,
|
|
971
994
|
output: errorText ? { error: errorText } : output,
|
|
@@ -1103,11 +1126,13 @@ var StreamProcessor = class StreamProcessor {
|
|
|
1103
1126
|
* Handle TOOL_CALL_END event — arguments are finalized (input-complete).
|
|
1104
1127
|
* Tool output arrives on TOOL_CALL_RESULT, not on this event.
|
|
1105
1128
|
*
|
|
1106
|
-
* If TOOL_CALL_END carries parsed `input`,
|
|
1107
|
-
*
|
|
1108
|
-
*
|
|
1109
|
-
* server_tool_use / web_search — issue #839) and
|
|
1110
|
-
*
|
|
1129
|
+
* If TOOL_CALL_END carries parsed `input`, it is the canonical arguments:
|
|
1130
|
+
* write it into the accumulated string and override the rendered part's
|
|
1131
|
+
* `input` with it. That covers adapters that deliver the whole input on END
|
|
1132
|
+
* (e.g. Anthropic server_tool_use / web_search — issue #839) and adapters
|
|
1133
|
+
* that stream the wire arguments and then normalize them (OpenAI strict-mode
|
|
1134
|
+
* null widening, undone by the adapter after #939), so `arguments` and
|
|
1135
|
+
* `input` never disagree on the persisted part.
|
|
1111
1136
|
*
|
|
1112
1137
|
* @see docs/chat-architecture.md#single-shot-tool-call-response — End-to-end flow
|
|
1113
1138
|
*/
|
|
@@ -1119,12 +1144,11 @@ var StreamProcessor = class StreamProcessor {
|
|
|
1119
1144
|
const input = chunk.input !== void 0 ? chunk.input : tanstackMetadata(chunk)?.input;
|
|
1120
1145
|
const existingToolCall = msgState.toolCalls.get(chunk.toolCallId);
|
|
1121
1146
|
if (existingToolCall && existingToolCall.state !== "input-complete") {
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
} catch {}
|
|
1147
|
+
const serializedInput = serializeToolInput(input);
|
|
1148
|
+
if (serializedInput !== void 0) existingToolCall.arguments = serializedInput;
|
|
1125
1149
|
const index = msgState.toolCallOrder.indexOf(chunk.toolCallId);
|
|
1126
1150
|
this.completeToolCall(messageId, index, existingToolCall);
|
|
1127
|
-
if (
|
|
1151
|
+
if (serializedInput !== void 0) {
|
|
1128
1152
|
existingToolCall.parsedArguments = input;
|
|
1129
1153
|
this.messages = updateToolCallPart(this.messages, messageId, {
|
|
1130
1154
|
id: existingToolCall.id,
|
|
@@ -1138,10 +1162,6 @@ var StreamProcessor = class StreamProcessor {
|
|
|
1138
1162
|
}
|
|
1139
1163
|
}
|
|
1140
1164
|
}
|
|
1141
|
-
extractToolResultError(output) {
|
|
1142
|
-
if (output && typeof output === "object" && "error" in output && typeof output.error === "string") return output.error;
|
|
1143
|
-
return typeof output === "string" ? output : "Tool execution failed";
|
|
1144
|
-
}
|
|
1145
1165
|
/**
|
|
1146
1166
|
* Handle TOOL_CALL_RESULT event (AG-UI spec).
|
|
1147
1167
|
*
|
|
@@ -1152,7 +1172,10 @@ var StreamProcessor = class StreamProcessor {
|
|
|
1152
1172
|
handleToolCallResultEvent(chunk) {
|
|
1153
1173
|
const messageId = this.toolCallToMessage.get(chunk.toolCallId) ?? this.messages.find((m) => m.parts.some((p) => p.type === "tool-call" && p.id === chunk.toolCallId))?.id;
|
|
1154
1174
|
if (!messageId) return;
|
|
1155
|
-
const
|
|
1175
|
+
const extra = chunk;
|
|
1176
|
+
const rawToolResultOutcome = tanstackMetadata(chunk)?.toolResultOutcome;
|
|
1177
|
+
const toolResultOutcome = isToolResultOutcome(rawToolResultOutcome) ? rawToolResultOutcome : void 0;
|
|
1178
|
+
const isOutputError = extra.state === "output-error" || tanstackMetadata(chunk)?.state === "output-error" || toolResultOutcome !== void 0;
|
|
1156
1179
|
let output;
|
|
1157
1180
|
try {
|
|
1158
1181
|
output = typeof chunk.content === "string" ? JSON.parse(chunk.content) : chunk.content;
|
|
@@ -1161,7 +1184,7 @@ var StreamProcessor = class StreamProcessor {
|
|
|
1161
1184
|
}
|
|
1162
1185
|
this.messages = updateToolCallWithOutput(this.messages, chunk.toolCallId, output, isOutputError ? "error" : void 0);
|
|
1163
1186
|
const resultState = isOutputError ? "error" : "complete";
|
|
1164
|
-
this.messages = updateToolResultPart(this.messages, messageId, chunk.toolCallId, aguiContentToContentParts(chunk.content), resultState, resultState === "error" ?
|
|
1187
|
+
this.messages = updateToolResultPart(this.messages, messageId, chunk.toolCallId, isContentPartArray(output) ? output : aguiContentToContentParts(chunk.content), resultState, resultState === "error" ? toolResultErrorText(output) : void 0, toolResultOutcome);
|
|
1165
1188
|
this.emitMessagesChange();
|
|
1166
1189
|
}
|
|
1167
1190
|
/**
|
|
@@ -1527,7 +1550,7 @@ var StreamProcessor = class StreamProcessor {
|
|
|
1527
1550
|
} catch {
|
|
1528
1551
|
toolCall.parsedArguments = void 0;
|
|
1529
1552
|
}
|
|
1530
|
-
if (this.
|
|
1553
|
+
if (this.isToolCallPartTerminal(toolCall.id)) return;
|
|
1531
1554
|
if (this.isToolCallPartAwaitingUserAction(toolCall.id)) return;
|
|
1532
1555
|
this.messages = updateToolCallPart(this.messages, messageId, {
|
|
1533
1556
|
id: toolCall.id,
|
|
@@ -1544,12 +1567,12 @@ var StreamProcessor = class StreamProcessor {
|
|
|
1544
1567
|
return this.messages.some((msg) => msg.parts?.some((part) => part.type === "tool-call" && part.id === toolCallId && (part.state === "approval-requested" || part.state === "approval-responded")));
|
|
1545
1568
|
}
|
|
1546
1569
|
/**
|
|
1547
|
-
* Whether the rendered tool-call part for the given id has reached
|
|
1548
|
-
* terminal 'error' state. Used to prevent the completion
|
|
1549
|
-
* downgrading a
|
|
1570
|
+
* Whether the rendered tool-call part for the given id has reached a
|
|
1571
|
+
* terminal 'error' or 'complete' state. Used to prevent the completion
|
|
1572
|
+
* safety net from downgrading a finished call back to 'input-complete'.
|
|
1550
1573
|
*/
|
|
1551
|
-
|
|
1552
|
-
return this.messages.some((msg) => msg.parts?.some((part) => part.type === "tool-call" && part.id === toolCallId && part.state === "error"));
|
|
1574
|
+
isToolCallPartTerminal(toolCallId) {
|
|
1575
|
+
return this.messages.some((msg) => msg.parts?.some((part) => part.type === "tool-call" && part.id === toolCallId && (part.state === "error" || part.state === "complete")));
|
|
1553
1576
|
}
|
|
1554
1577
|
/**
|
|
1555
1578
|
* Emit pending text update for a specific message.
|