@tanstack/ai 0.40.0 → 0.42.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.
Files changed (57) hide show
  1. package/dist/esm/activities/chat/agent-loop-strategies.d.ts +40 -3
  2. package/dist/esm/activities/chat/agent-loop-strategies.js +4 -0
  3. package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -1
  4. package/dist/esm/activities/chat/index.d.ts +5 -0
  5. package/dist/esm/activities/chat/index.js +102 -33
  6. package/dist/esm/activities/chat/index.js.map +1 -1
  7. package/dist/esm/activities/chat/messages.js +7 -0
  8. package/dist/esm/activities/chat/messages.js.map +1 -1
  9. package/dist/esm/activities/chat/stream/message-updaters.d.ts +2 -0
  10. package/dist/esm/activities/chat/stream/message-updaters.js +3 -1
  11. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
  12. package/dist/esm/activities/chat/stream/processor.js +18 -1
  13. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  14. package/dist/esm/activities/chat/tools/tool-definition.d.ts +14 -11
  15. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
  16. package/dist/esm/activities/generateAudio/index.d.ts +1 -1
  17. package/dist/esm/activities/generateAudio/index.js.map +1 -1
  18. package/dist/esm/activities/generateImage/index.d.ts +1 -1
  19. package/dist/esm/activities/generateImage/index.js +1 -1
  20. package/dist/esm/activities/generateImage/index.js.map +1 -1
  21. package/dist/esm/activities/generateSpeech/index.d.ts +1 -1
  22. package/dist/esm/activities/generateSpeech/index.js.map +1 -1
  23. package/dist/esm/activities/generateTranscription/index.d.ts +1 -1
  24. package/dist/esm/activities/generateTranscription/index.js.map +1 -1
  25. package/dist/esm/activities/generateVideo/index.d.ts +12 -12
  26. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  27. package/dist/esm/index.d.ts +3 -3
  28. package/dist/esm/index.js +4 -1
  29. package/dist/esm/index.js.map +1 -1
  30. package/dist/esm/realtime/event-emitter.d.ts +5 -0
  31. package/dist/esm/realtime/event-emitter.js +27 -0
  32. package/dist/esm/realtime/event-emitter.js.map +1 -0
  33. package/dist/esm/realtime/index.d.ts +1 -0
  34. package/dist/esm/realtime/index.js.map +1 -1
  35. package/dist/esm/realtime/types.d.ts +9 -1
  36. package/dist/esm/types.d.ts +43 -2
  37. package/package.json +1 -1
  38. package/skills/ai-core/chat-experience/SKILL.md +58 -0
  39. package/skills/ai-core/media-generation/SKILL.md +42 -2
  40. package/skills/ai-core/middleware/SKILL.md +18 -3
  41. package/skills/ai-core/tool-calling/SKILL.md +15 -1
  42. package/src/activities/chat/agent-loop-strategies.ts +43 -3
  43. package/src/activities/chat/index.ts +144 -36
  44. package/src/activities/chat/messages.ts +9 -0
  45. package/src/activities/chat/stream/message-updaters.ts +7 -1
  46. package/src/activities/chat/stream/processor.ts +30 -2
  47. package/src/activities/chat/tools/tool-definition.ts +33 -11
  48. package/src/activities/generateAudio/index.ts +2 -2
  49. package/src/activities/generateImage/index.ts +2 -2
  50. package/src/activities/generateSpeech/index.ts +2 -2
  51. package/src/activities/generateTranscription/index.ts +2 -2
  52. package/src/activities/generateVideo/index.ts +29 -15
  53. package/src/index.ts +3 -1
  54. package/src/realtime/event-emitter.ts +46 -0
  55. package/src/realtime/index.ts +2 -0
  56. package/src/realtime/types.ts +9 -0
  57. package/src/types.ts +43 -2
@@ -230,6 +230,12 @@ function modelMessageToUIMessage(modelMessage, id) {
230
230
  }
231
231
  if (modelMessage.toolCalls && modelMessage.toolCalls.length > 0) {
232
232
  for (const toolCall of modelMessage.toolCalls) {
233
+ let input;
234
+ try {
235
+ input = JSON.parse(toolCall.function.arguments);
236
+ } catch {
237
+ input = void 0;
238
+ }
233
239
  parts.push({
234
240
  type: "tool-call",
235
241
  id: toolCall.id,
@@ -237,6 +243,7 @@ function modelMessageToUIMessage(modelMessage, id) {
237
243
  arguments: toolCall.function.arguments,
238
244
  state: "input-complete",
239
245
  // Model messages have complete arguments
246
+ ...input !== void 0 && { input },
240
247
  ...toolCall.metadata !== void 0 && { metadata: toolCall.metadata }
241
248
  });
242
249
  }
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sources":["../../../../src/activities/chat/messages.ts"],"sourcesContent":["import { normalizeToolResult } from '../../utilities/tool-result'\nimport type { Message as AGUIMessage } from '@ag-ui/core'\nimport type {\n ContentPart,\n MessagePart,\n ModelMessage,\n TextPart,\n ToolCallPart,\n UIMessage,\n} from '../../types'\n// ===========================\n// Message Converters\n// ===========================\n\n/**\n * Check if a MessagePart is a content part (text, image, audio, video, document)\n * that maps directly to a ModelMessage ContentPart.\n */\nfunction isContentPart(part: MessagePart): part is ContentPart {\n return (\n part.type === 'text' ||\n part.type === 'image' ||\n part.type === 'audio' ||\n part.type === 'video' ||\n part.type === 'document'\n )\n}\n\nfunction safeJsonStringify(value: unknown): string {\n try {\n return JSON.stringify(value)\n } catch {\n return ''\n }\n}\n\nfunction parseToolResultContent(content: string): unknown {\n try {\n return JSON.parse(content)\n } catch {\n return content\n }\n}\n\n/**\n * Collapse an array of ContentParts into the most compact ModelMessage content:\n * - Empty array → null\n * - All text parts → joined string (or null if empty)\n * - Mixed content → ContentPart array as-is\n */\nfunction collapseContentParts(\n parts: Array<ContentPart>,\n): string | null | Array<ContentPart> {\n if (parts.length === 0) return null\n\n const allText = parts.every((p) => p.type === 'text')\n if (allText) {\n const joined = parts.map((p) => p.content).join('')\n return joined || null\n }\n\n return parts\n}\n\n/**\n * Extract text content from ModelMessage content (string, null, or ContentPart array).\n * Used when only the text portion is needed (e.g., tool result content).\n */\nfunction getTextContent(content: string | null | Array<ContentPart>): string {\n if (content === null) return ''\n if (typeof content === 'string') return content\n return content\n .filter((part): part is TextPart => part.type === 'text')\n .map((part) => part.content)\n .join('')\n}\n\n/**\n * Convert UIMessages or ModelMessages to ModelMessages\n */\nexport function convertMessagesToModelMessages(\n messages: Array<UIMessage | ModelMessage>,\n): Array<ModelMessage> {\n // Pre-pass: collect toolCallIds already represented in anchor UIMessage parts.\n // Fan-out tool messages whose toolCallId matches an anchored ToolResultPart\n // are AG-UI duplicates and must be dropped to avoid double-feeding the LLM.\n const anchoredToolCallIds = new Set<string>()\n for (const msg of messages) {\n if ('parts' in msg) {\n for (const part of msg.parts) {\n if (part.type === 'tool-result') {\n anchoredToolCallIds.add(part.toolCallId)\n }\n }\n }\n }\n\n const modelMessages: Array<ModelMessage> = []\n for (const msg of messages) {\n if ('parts' in msg) {\n // UIMessage anchor — existing fan-out path\n modelMessages.push(...uiMessageToModelMessages(msg))\n continue\n }\n\n const role = (msg as { role: string }).role\n\n // AG-UI tool fan-out duplicate — drop if anchor already covers it\n if (\n role === 'tool' &&\n msg.toolCallId &&\n anchoredToolCallIds.has(msg.toolCallId)\n ) {\n continue\n }\n\n // AG-UI reasoning and activity — no ModelMessage equivalent today\n if (role === 'reasoning' || role === 'activity') {\n continue\n }\n\n // AG-UI developer — collapse to system\n if (role === 'developer') {\n modelMessages.push({\n role: 'system' as ModelMessage['role'],\n content: (msg as { content: string }).content,\n })\n continue\n }\n\n // Already a ModelMessage (user, assistant, system, tool with no anchor) — pass through\n modelMessages.push(msg)\n }\n return modelMessages\n}\n\n/**\n * Convert a UIMessage to ModelMessage(s)\n *\n * Walks the parts array IN ORDER to preserve the interleaving of text,\n * tool calls, and tool results. This is critical for multi-round tool\n * flows where the model generates text, calls a tool, gets the result,\n * then generates more text and calls another tool.\n *\n * The output preserves the sequential structure:\n * text1 → toolCall1 → toolResult1 → text2 → toolCall2 → toolResult2\n * becomes:\n * assistant: {content: \"text1\", toolCalls: [toolCall1]}\n * tool: toolResult1\n * assistant: {content: \"text2\", toolCalls: [toolCall2]}\n * tool: toolResult2\n *\n * @param uiMessage - The UIMessage to convert\n * @returns An array of ModelMessages preserving part ordering\n */\nexport function uiMessageToModelMessages(\n uiMessage: UIMessage,\n): Array<ModelMessage> {\n // Skip system messages - they're handled via systemPrompts, not ModelMessages\n if (uiMessage.role === 'system') {\n return []\n }\n\n // For non-assistant messages (user), use the simpler path since they\n // don't have tool calls or tool results to interleave\n if (uiMessage.role !== 'assistant') {\n return [buildUserOrToolMessage(uiMessage)]\n }\n\n // For assistant messages, walk parts in order to preserve interleaving\n return buildAssistantMessages(uiMessage)\n}\n\n/**\n * Build a single ModelMessage for user messages (simple path).\n * Preserves ordering of text and multimodal content parts.\n */\nfunction buildUserOrToolMessage(uiMessage: UIMessage): ModelMessage {\n const contentParts: Array<ContentPart> = []\n for (const part of uiMessage.parts) {\n if (isContentPart(part)) {\n contentParts.push(part)\n }\n }\n\n return {\n role: uiMessage.role as 'user' | 'assistant' | 'tool',\n content: collapseContentParts(contentParts),\n }\n}\n\n// Accumulator for building an assistant segment (content + tool calls)\ninterface AssistantSegment {\n contentParts: Array<ContentPart>\n toolCalls: Array<{\n id: string\n type: 'function'\n function: { name: string; arguments: string }\n /** Provider-specific metadata that round-trips with the tool call.\n * Untyped at this framework layer; adapters narrow it via their\n * `TToolCallMetadata` generic. */\n metadata?: unknown\n }>\n}\n\nfunction createSegment(): AssistantSegment {\n return { contentParts: [], toolCalls: [] }\n}\n\nfunction isToolCallIncluded(part: ToolCallPart): boolean {\n return (\n part.state === 'input-complete' ||\n part.state === 'complete' ||\n part.state === 'approval-responded' ||\n part.state === 'error' ||\n part.output !== undefined\n )\n}\n\n/**\n * Build ModelMessages for an assistant UIMessage, preserving the\n * sequential interleaving of text, tool calls, and tool results.\n *\n * Walks parts in order. Text and tool-call parts accumulate into the\n * current \"segment\". When a tool-result part is encountered, the\n * current segment is flushed as an assistant message, then the tool\n * result is emitted as a tool message.\n */\nfunction buildAssistantMessages(uiMessage: UIMessage): Array<ModelMessage> {\n const messageList: Array<ModelMessage> = []\n let current = createSegment()\n let pendingThinking: Array<{ content: string; signature?: string }> = []\n\n // Track emitted tool result IDs to avoid duplicates.\n // A tool call can have BOTH an explicit tool-result part AND an output\n // field on the tool-call part. We only want one per tool call ID.\n const emittedToolResultIds = new Set<string>()\n\n function flushSegment(): void {\n const content = collapseContentParts(current.contentParts)\n const hasContent = content !== null\n const hasToolCalls = current.toolCalls.length > 0\n\n if (hasContent || hasToolCalls) {\n messageList.push({\n role: 'assistant',\n content,\n ...(hasToolCalls && { toolCalls: current.toolCalls }),\n ...(pendingThinking.length > 0 && { thinking: pendingThinking }),\n })\n pendingThinking = []\n }\n current = createSegment()\n }\n\n for (const part of uiMessage.parts) {\n switch (part.type) {\n case 'text':\n case 'image':\n case 'audio':\n case 'video':\n case 'document':\n current.contentParts.push(part)\n break\n\n case 'tool-call':\n if (isToolCallIncluded(part)) {\n current.toolCalls.push({\n id: part.id,\n type: 'function' as const,\n function: {\n name: part.name,\n arguments: part.arguments,\n },\n ...(part.metadata !== undefined && { metadata: part.metadata }),\n })\n }\n break\n\n case 'tool-result':\n // Flush the current assistant segment before emitting the tool result\n flushSegment()\n\n // Emit the tool result\n if (\n (part.state === 'complete' || part.state === 'error') &&\n !emittedToolResultIds.has(part.toolCallId)\n ) {\n messageList.push({\n role: 'tool',\n content: part.content,\n toolCallId: part.toolCallId,\n })\n emittedToolResultIds.add(part.toolCallId)\n }\n break\n\n case 'thinking':\n if (part.content) {\n pendingThinking.push({\n content: part.content,\n ...(part.signature && { signature: part.signature }),\n })\n }\n break\n\n case 'structured-output':\n // Only emit completed structured responses into history. Streaming or\n // errored buffers would push malformed JSON into the next LLM turn's\n // assistant content. `raw` is the source of truth; `data` is the\n // defensive fallback for terminal-only completes that didn't ship raw.\n if (part.status === 'complete') {\n const serialized =\n part.raw !== ''\n ? part.raw\n : part.data !== undefined\n ? safeJsonStringify(part.data)\n : ''\n if (serialized !== '') {\n current.contentParts.push({ type: 'text', content: serialized })\n }\n }\n break\n\n case 'ui-resource':\n // MCP Apps widget — rendered client-side only. It must never enter\n // model input, so it is intentionally dropped from the model message.\n break\n\n default:\n break\n }\n }\n\n // Flush any remaining accumulated content\n flushSegment()\n\n // Emit tool results from client tool-call parts with output or approval,\n // but only if not already covered by an explicit tool-result part above.\n // These are appended at the end since they don't have explicit tool-result\n // parts in the parts array to trigger inline emission.\n for (const part of uiMessage.parts) {\n if (part.type !== 'tool-call') continue\n\n // Output takes priority — if the tool has already produced a result,\n // emit the concrete output regardless of approval metadata.\n if (part.output !== undefined && !emittedToolResultIds.has(part.id)) {\n messageList.push({\n role: 'tool',\n content: normalizeToolResult(part.output),\n toolCallId: part.id,\n })\n emittedToolResultIds.add(part.id)\n }\n\n // Approval response without output — emit approval status for iteration tracking\n if (\n part.output === undefined &&\n part.state === 'approval-responded' &&\n part.approval?.approved !== undefined &&\n !emittedToolResultIds.has(part.id)\n ) {\n const approved = part.approval.approved\n messageList.push({\n role: 'tool',\n content: JSON.stringify({\n approved,\n ...(approved && { pendingExecution: true }),\n message: approved\n ? 'User approved this action'\n : 'User denied this action',\n }),\n toolCallId: part.id,\n })\n emittedToolResultIds.add(part.id)\n }\n }\n\n // If no messages were produced (e.g., empty parts), emit a minimal assistant message\n if (messageList.length === 0) {\n messageList.push({\n role: 'assistant',\n content: null,\n })\n }\n\n return messageList\n}\n\n/**\n * Convert a ModelMessage to UIMessage\n *\n * This conversion creates a parts-based structure:\n * - content field → TextPart\n * - toolCalls array → ToolCallPart[]\n * - role=\"tool\" messages should be converted separately and merged\n *\n * @param modelMessage - The ModelMessage to convert\n * @param id - Optional ID for the UIMessage (generated if not provided)\n * @returns A UIMessage with parts\n */\nexport function modelMessageToUIMessage(\n modelMessage: ModelMessage,\n id?: string,\n): UIMessage {\n const parts: Array<MessagePart> = []\n\n if (modelMessage.role === 'assistant' && modelMessage.thinking?.length) {\n for (const thinking of modelMessage.thinking) {\n if (!thinking.content) continue\n parts.push({\n type: 'thinking',\n content: thinking.content,\n ...(thinking.signature && { signature: thinking.signature }),\n })\n }\n }\n\n // Handle tool results (when role is \"tool\") - only produce tool-result part,\n // not a text part (the content IS the tool result, not display text)\n if (modelMessage.role === 'tool' && modelMessage.toolCallId) {\n parts.push({\n type: 'tool-result',\n toolCallId: modelMessage.toolCallId,\n content: getTextContent(modelMessage.content),\n state: 'complete',\n })\n } else if (Array.isArray(modelMessage.content)) {\n // Multimodal content - preserve all content parts as MessageParts\n for (const part of modelMessage.content) {\n parts.push(part)\n }\n } else {\n // String or null content\n const textContent = getTextContent(modelMessage.content)\n if (textContent) {\n parts.push({\n type: 'text',\n content: textContent,\n })\n }\n }\n\n // Handle tool calls\n if (modelMessage.toolCalls && modelMessage.toolCalls.length > 0) {\n for (const toolCall of modelMessage.toolCalls) {\n parts.push({\n type: 'tool-call',\n id: toolCall.id,\n name: toolCall.function.name,\n arguments: toolCall.function.arguments,\n state: 'input-complete', // Model messages have complete arguments\n ...(toolCall.metadata !== undefined && { metadata: toolCall.metadata }),\n })\n }\n }\n\n return {\n id: id || generateMessageId(),\n role: modelMessage.role === 'tool' ? 'assistant' : modelMessage.role,\n parts,\n }\n}\n\n/**\n * Normalize a single AG-UI `MESSAGES_SNAPSHOT` message into a `UIMessage`.\n *\n * AG-UI snapshot messages use the wire shape `{ id, role, content }` and have\n * no `parts` array. Casting them directly to `UIMessage` is unsafe: any code\n * that later reads `message.parts` (e.g. the devtools `onToolCallStateChange`\n * handler) crashes with \"Cannot read properties of undefined (reading 'find')\".\n *\n * Each role is mapped to the canonical `UIMessage` shape, reusing\n * `modelMessageToUIMessage` for the roles that share `ModelMessage`'s structure.\n * The original AG-UI `id` is preserved so later `TEXT_MESSAGE_CONTENT` /\n * `TOOL_CALL_*` events still route by `messageId` (falling back to a generated\n * id only when the snapshot omits one). Messages that already carry `parts`\n * (e.g. a TanStack server echoing `UIMessage`s back over the wire) pass through\n * unchanged apart from ensuring an id.\n */\nexport function aguiSnapshotMessageToUIMessage(\n message: AGUIMessage | UIMessage,\n): UIMessage {\n if ('parts' in message) {\n return { ...message, id: message.id || generateMessageId() }\n }\n\n const id = message.id || generateMessageId()\n\n switch (message.role) {\n case 'user':\n return {\n id,\n role: 'user',\n parts: aguiUserContentToParts(message.content),\n }\n case 'assistant':\n return modelMessageToUIMessage(\n {\n role: 'assistant',\n content: message.content ?? null,\n ...(message.toolCalls && { toolCalls: message.toolCalls }),\n },\n id,\n )\n case 'tool':\n return modelMessageToUIMessage(\n {\n role: 'tool',\n content: message.content,\n toolCallId: message.toolCallId,\n },\n id,\n )\n case 'system':\n case 'developer':\n // `ModelMessage` has no system/developer role; build the part directly.\n return {\n id,\n role: 'system',\n parts: message.content\n ? [{ type: 'text', content: message.content }]\n : [],\n }\n case 'reasoning':\n return {\n id,\n role: 'assistant',\n parts: message.content\n ? [{ type: 'thinking', content: message.content }]\n : [],\n }\n case 'activity':\n default:\n // `activity` (and any future role) has no text/parts equivalent today.\n return { id, role: 'assistant', parts: [] }\n }\n}\n\n/**\n * Convert AG-UI user message content into `UIMessage` parts.\n *\n * AG-UI user content is either a plain string or a multimodal array whose text\n * entries use `{ type: 'text', text }` (vs. TanStack's `{ type: 'text', content }`).\n * Text entries are rewritten to the TanStack shape; image/audio/video/document\n * entries already match `ContentPart` and pass through. `binary` entries have no\n * TanStack equivalent and are dropped.\n */\nfunction aguiUserContentToParts(\n content: Extract<AGUIMessage, { role: 'user' }>['content'],\n): Array<MessagePart> {\n if (typeof content === 'string') {\n return content ? [{ type: 'text', content }] : []\n }\n\n const parts: Array<MessagePart> = []\n for (const part of content) {\n if (part.type === 'text') {\n parts.push({ type: 'text', content: part.text })\n } else if (part.type !== 'binary') {\n parts.push(part)\n }\n }\n return parts\n}\n\n/**\n * Convert an array of ModelMessages to UIMessages\n *\n * This handles merging tool result messages with their corresponding assistant messages\n *\n * @param modelMessages - Array of ModelMessages to convert\n * @returns Array of UIMessages\n */\nexport function modelMessagesToUIMessages(\n modelMessages: Array<ModelMessage>,\n): Array<UIMessage> {\n const uiMessages: Array<UIMessage> = []\n let currentAssistantMessage: UIMessage | null = null\n\n for (const msg of modelMessages) {\n if (msg.role === 'tool') {\n // Tool result - merge into the last assistant message if possible\n if (\n msg.toolCallId !== undefined &&\n currentAssistantMessage &&\n currentAssistantMessage.role === 'assistant'\n ) {\n const content = getTextContent(msg.content)\n const toolCallPart = currentAssistantMessage.parts.find(\n (part): part is ToolCallPart =>\n part.type === 'tool-call' && part.id === msg.toolCallId,\n )\n\n if (toolCallPart) {\n toolCallPart.output = parseToolResultContent(content)\n toolCallPart.state = 'complete'\n }\n\n currentAssistantMessage.parts.push({\n type: 'tool-result',\n toolCallId: msg.toolCallId,\n content,\n state: 'complete',\n })\n } else {\n // No assistant message to merge into, create a standalone one\n const toolResultUIMessage = modelMessageToUIMessage(msg)\n uiMessages.push(toolResultUIMessage)\n }\n } else {\n // Regular message\n const uiMessage = modelMessageToUIMessage(msg)\n uiMessages.push(uiMessage)\n\n // Track assistant messages for potential tool result merging\n if (msg.role === 'assistant') {\n currentAssistantMessage = uiMessage\n } else {\n currentAssistantMessage = null\n }\n }\n }\n\n return uiMessages\n}\n\n/**\n * Normalize a message (UIMessage or ModelMessage) to a UIMessage\n * Ensures the message has an ID and createdAt timestamp\n *\n * @param message - Either a UIMessage or ModelMessage\n * @param generateId - Function to generate a message ID if needed\n * @returns A UIMessage with guaranteed id and createdAt\n */\nexport function normalizeToUIMessage(\n message: UIMessage | ModelMessage,\n generateId: () => string,\n): UIMessage {\n if ('parts' in message) {\n // Already a UIMessage\n return {\n ...message,\n id: message.id || generateId(),\n createdAt: message.createdAt || new Date(),\n }\n } else {\n // ModelMessage - convert to UIMessage\n return {\n ...modelMessageToUIMessage(message, generateId()),\n createdAt: new Date(),\n }\n }\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `msg-${Date.now()}-${Math.random().toString(36).substring(7)}`\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messages.js","sources":["../../../../src/activities/chat/messages.ts"],"sourcesContent":["import { normalizeToolResult } from '../../utilities/tool-result'\nimport type { Message as AGUIMessage } from '@ag-ui/core'\nimport type {\n ContentPart,\n MessagePart,\n ModelMessage,\n TextPart,\n ToolCallPart,\n UIMessage,\n} from '../../types'\n// ===========================\n// Message Converters\n// ===========================\n\n/**\n * Check if a MessagePart is a content part (text, image, audio, video, document)\n * that maps directly to a ModelMessage ContentPart.\n */\nfunction isContentPart(part: MessagePart): part is ContentPart {\n return (\n part.type === 'text' ||\n part.type === 'image' ||\n part.type === 'audio' ||\n part.type === 'video' ||\n part.type === 'document'\n )\n}\n\nfunction safeJsonStringify(value: unknown): string {\n try {\n return JSON.stringify(value)\n } catch {\n return ''\n }\n}\n\nfunction parseToolResultContent(content: string): unknown {\n try {\n return JSON.parse(content)\n } catch {\n return content\n }\n}\n\n/**\n * Collapse an array of ContentParts into the most compact ModelMessage content:\n * - Empty array → null\n * - All text parts → joined string (or null if empty)\n * - Mixed content → ContentPart array as-is\n */\nfunction collapseContentParts(\n parts: Array<ContentPart>,\n): string | null | Array<ContentPart> {\n if (parts.length === 0) return null\n\n const allText = parts.every((p) => p.type === 'text')\n if (allText) {\n const joined = parts.map((p) => p.content).join('')\n return joined || null\n }\n\n return parts\n}\n\n/**\n * Extract text content from ModelMessage content (string, null, or ContentPart array).\n * Used when only the text portion is needed (e.g., tool result content).\n */\nfunction getTextContent(content: string | null | Array<ContentPart>): string {\n if (content === null) return ''\n if (typeof content === 'string') return content\n return content\n .filter((part): part is TextPart => part.type === 'text')\n .map((part) => part.content)\n .join('')\n}\n\n/**\n * Convert UIMessages or ModelMessages to ModelMessages\n */\nexport function convertMessagesToModelMessages(\n messages: Array<UIMessage | ModelMessage>,\n): Array<ModelMessage> {\n // Pre-pass: collect toolCallIds already represented in anchor UIMessage parts.\n // Fan-out tool messages whose toolCallId matches an anchored ToolResultPart\n // are AG-UI duplicates and must be dropped to avoid double-feeding the LLM.\n const anchoredToolCallIds = new Set<string>()\n for (const msg of messages) {\n if ('parts' in msg) {\n for (const part of msg.parts) {\n if (part.type === 'tool-result') {\n anchoredToolCallIds.add(part.toolCallId)\n }\n }\n }\n }\n\n const modelMessages: Array<ModelMessage> = []\n for (const msg of messages) {\n if ('parts' in msg) {\n // UIMessage anchor — existing fan-out path\n modelMessages.push(...uiMessageToModelMessages(msg))\n continue\n }\n\n const role = (msg as { role: string }).role\n\n // AG-UI tool fan-out duplicate — drop if anchor already covers it\n if (\n role === 'tool' &&\n msg.toolCallId &&\n anchoredToolCallIds.has(msg.toolCallId)\n ) {\n continue\n }\n\n // AG-UI reasoning and activity — no ModelMessage equivalent today\n if (role === 'reasoning' || role === 'activity') {\n continue\n }\n\n // AG-UI developer — collapse to system\n if (role === 'developer') {\n modelMessages.push({\n role: 'system' as ModelMessage['role'],\n content: (msg as { content: string }).content,\n })\n continue\n }\n\n // Already a ModelMessage (user, assistant, system, tool with no anchor) — pass through\n modelMessages.push(msg)\n }\n return modelMessages\n}\n\n/**\n * Convert a UIMessage to ModelMessage(s)\n *\n * Walks the parts array IN ORDER to preserve the interleaving of text,\n * tool calls, and tool results. This is critical for multi-round tool\n * flows where the model generates text, calls a tool, gets the result,\n * then generates more text and calls another tool.\n *\n * The output preserves the sequential structure:\n * text1 → toolCall1 → toolResult1 → text2 → toolCall2 → toolResult2\n * becomes:\n * assistant: {content: \"text1\", toolCalls: [toolCall1]}\n * tool: toolResult1\n * assistant: {content: \"text2\", toolCalls: [toolCall2]}\n * tool: toolResult2\n *\n * @param uiMessage - The UIMessage to convert\n * @returns An array of ModelMessages preserving part ordering\n */\nexport function uiMessageToModelMessages(\n uiMessage: UIMessage,\n): Array<ModelMessage> {\n // Skip system messages - they're handled via systemPrompts, not ModelMessages\n if (uiMessage.role === 'system') {\n return []\n }\n\n // For non-assistant messages (user), use the simpler path since they\n // don't have tool calls or tool results to interleave\n if (uiMessage.role !== 'assistant') {\n return [buildUserOrToolMessage(uiMessage)]\n }\n\n // For assistant messages, walk parts in order to preserve interleaving\n return buildAssistantMessages(uiMessage)\n}\n\n/**\n * Build a single ModelMessage for user messages (simple path).\n * Preserves ordering of text and multimodal content parts.\n */\nfunction buildUserOrToolMessage(uiMessage: UIMessage): ModelMessage {\n const contentParts: Array<ContentPart> = []\n for (const part of uiMessage.parts) {\n if (isContentPart(part)) {\n contentParts.push(part)\n }\n }\n\n return {\n role: uiMessage.role as 'user' | 'assistant' | 'tool',\n content: collapseContentParts(contentParts),\n }\n}\n\n// Accumulator for building an assistant segment (content + tool calls)\ninterface AssistantSegment {\n contentParts: Array<ContentPart>\n toolCalls: Array<{\n id: string\n type: 'function'\n function: { name: string; arguments: string }\n /** Provider-specific metadata that round-trips with the tool call.\n * Untyped at this framework layer; adapters narrow it via their\n * `TToolCallMetadata` generic. */\n metadata?: unknown\n }>\n}\n\nfunction createSegment(): AssistantSegment {\n return { contentParts: [], toolCalls: [] }\n}\n\nfunction isToolCallIncluded(part: ToolCallPart): boolean {\n return (\n part.state === 'input-complete' ||\n part.state === 'complete' ||\n part.state === 'approval-responded' ||\n part.state === 'error' ||\n part.output !== undefined\n )\n}\n\n/**\n * Build ModelMessages for an assistant UIMessage, preserving the\n * sequential interleaving of text, tool calls, and tool results.\n *\n * Walks parts in order. Text and tool-call parts accumulate into the\n * current \"segment\". When a tool-result part is encountered, the\n * current segment is flushed as an assistant message, then the tool\n * result is emitted as a tool message.\n */\nfunction buildAssistantMessages(uiMessage: UIMessage): Array<ModelMessage> {\n const messageList: Array<ModelMessage> = []\n let current = createSegment()\n let pendingThinking: Array<{ content: string; signature?: string }> = []\n\n // Track emitted tool result IDs to avoid duplicates.\n // A tool call can have BOTH an explicit tool-result part AND an output\n // field on the tool-call part. We only want one per tool call ID.\n const emittedToolResultIds = new Set<string>()\n\n function flushSegment(): void {\n const content = collapseContentParts(current.contentParts)\n const hasContent = content !== null\n const hasToolCalls = current.toolCalls.length > 0\n\n if (hasContent || hasToolCalls) {\n messageList.push({\n role: 'assistant',\n content,\n ...(hasToolCalls && { toolCalls: current.toolCalls }),\n ...(pendingThinking.length > 0 && { thinking: pendingThinking }),\n })\n pendingThinking = []\n }\n current = createSegment()\n }\n\n for (const part of uiMessage.parts) {\n switch (part.type) {\n case 'text':\n case 'image':\n case 'audio':\n case 'video':\n case 'document':\n current.contentParts.push(part)\n break\n\n case 'tool-call':\n if (isToolCallIncluded(part)) {\n current.toolCalls.push({\n id: part.id,\n type: 'function' as const,\n function: {\n name: part.name,\n arguments: part.arguments,\n },\n ...(part.metadata !== undefined && { metadata: part.metadata }),\n })\n }\n break\n\n case 'tool-result':\n // Flush the current assistant segment before emitting the tool result\n flushSegment()\n\n // Emit the tool result\n if (\n (part.state === 'complete' || part.state === 'error') &&\n !emittedToolResultIds.has(part.toolCallId)\n ) {\n messageList.push({\n role: 'tool',\n content: part.content,\n toolCallId: part.toolCallId,\n })\n emittedToolResultIds.add(part.toolCallId)\n }\n break\n\n case 'thinking':\n if (part.content) {\n pendingThinking.push({\n content: part.content,\n ...(part.signature && { signature: part.signature }),\n })\n }\n break\n\n case 'structured-output':\n // Only emit completed structured responses into history. Streaming or\n // errored buffers would push malformed JSON into the next LLM turn's\n // assistant content. `raw` is the source of truth; `data` is the\n // defensive fallback for terminal-only completes that didn't ship raw.\n if (part.status === 'complete') {\n const serialized =\n part.raw !== ''\n ? part.raw\n : part.data !== undefined\n ? safeJsonStringify(part.data)\n : ''\n if (serialized !== '') {\n current.contentParts.push({ type: 'text', content: serialized })\n }\n }\n break\n\n case 'ui-resource':\n // MCP Apps widget — rendered client-side only. It must never enter\n // model input, so it is intentionally dropped from the model message.\n break\n\n default:\n break\n }\n }\n\n // Flush any remaining accumulated content\n flushSegment()\n\n // Emit tool results from client tool-call parts with output or approval,\n // but only if not already covered by an explicit tool-result part above.\n // These are appended at the end since they don't have explicit tool-result\n // parts in the parts array to trigger inline emission.\n for (const part of uiMessage.parts) {\n if (part.type !== 'tool-call') continue\n\n // Output takes priority — if the tool has already produced a result,\n // emit the concrete output regardless of approval metadata.\n if (part.output !== undefined && !emittedToolResultIds.has(part.id)) {\n messageList.push({\n role: 'tool',\n content: normalizeToolResult(part.output),\n toolCallId: part.id,\n })\n emittedToolResultIds.add(part.id)\n }\n\n // Approval response without output — emit approval status for iteration tracking\n if (\n part.output === undefined &&\n part.state === 'approval-responded' &&\n part.approval?.approved !== undefined &&\n !emittedToolResultIds.has(part.id)\n ) {\n const approved = part.approval.approved\n messageList.push({\n role: 'tool',\n content: JSON.stringify({\n approved,\n ...(approved && { pendingExecution: true }),\n message: approved\n ? 'User approved this action'\n : 'User denied this action',\n }),\n toolCallId: part.id,\n })\n emittedToolResultIds.add(part.id)\n }\n }\n\n // If no messages were produced (e.g., empty parts), emit a minimal assistant message\n if (messageList.length === 0) {\n messageList.push({\n role: 'assistant',\n content: null,\n })\n }\n\n return messageList\n}\n\n/**\n * Convert a ModelMessage to UIMessage\n *\n * This conversion creates a parts-based structure:\n * - content field → TextPart\n * - toolCalls array → ToolCallPart[]\n * - role=\"tool\" messages should be converted separately and merged\n *\n * @param modelMessage - The ModelMessage to convert\n * @param id - Optional ID for the UIMessage (generated if not provided)\n * @returns A UIMessage with parts\n */\nexport function modelMessageToUIMessage(\n modelMessage: ModelMessage,\n id?: string,\n): UIMessage {\n const parts: Array<MessagePart> = []\n\n if (modelMessage.role === 'assistant' && modelMessage.thinking?.length) {\n for (const thinking of modelMessage.thinking) {\n if (!thinking.content) continue\n parts.push({\n type: 'thinking',\n content: thinking.content,\n ...(thinking.signature && { signature: thinking.signature }),\n })\n }\n }\n\n // Handle tool results (when role is \"tool\") - only produce tool-result part,\n // not a text part (the content IS the tool result, not display text)\n if (modelMessage.role === 'tool' && modelMessage.toolCallId) {\n parts.push({\n type: 'tool-result',\n toolCallId: modelMessage.toolCallId,\n content: getTextContent(modelMessage.content),\n state: 'complete',\n })\n } else if (Array.isArray(modelMessage.content)) {\n // Multimodal content - preserve all content parts as MessageParts\n for (const part of modelMessage.content) {\n parts.push(part)\n }\n } else {\n // String or null content\n const textContent = getTextContent(modelMessage.content)\n if (textContent) {\n parts.push({\n type: 'text',\n content: textContent,\n })\n }\n }\n\n // Handle tool calls\n if (modelMessage.toolCalls && modelMessage.toolCalls.length > 0) {\n for (const toolCall of modelMessage.toolCalls) {\n // Model-message arguments are complete, so surface the parsed input.\n // A malformed arguments string just leaves `input` undefined.\n let input: unknown\n try {\n input = JSON.parse(toolCall.function.arguments)\n } catch {\n input = undefined\n }\n parts.push({\n type: 'tool-call',\n id: toolCall.id,\n name: toolCall.function.name,\n arguments: toolCall.function.arguments,\n state: 'input-complete', // Model messages have complete arguments\n ...(input !== undefined && { input }),\n ...(toolCall.metadata !== undefined && { metadata: toolCall.metadata }),\n })\n }\n }\n\n return {\n id: id || generateMessageId(),\n role: modelMessage.role === 'tool' ? 'assistant' : modelMessage.role,\n parts,\n }\n}\n\n/**\n * Normalize a single AG-UI `MESSAGES_SNAPSHOT` message into a `UIMessage`.\n *\n * AG-UI snapshot messages use the wire shape `{ id, role, content }` and have\n * no `parts` array. Casting them directly to `UIMessage` is unsafe: any code\n * that later reads `message.parts` (e.g. the devtools `onToolCallStateChange`\n * handler) crashes with \"Cannot read properties of undefined (reading 'find')\".\n *\n * Each role is mapped to the canonical `UIMessage` shape, reusing\n * `modelMessageToUIMessage` for the roles that share `ModelMessage`'s structure.\n * The original AG-UI `id` is preserved so later `TEXT_MESSAGE_CONTENT` /\n * `TOOL_CALL_*` events still route by `messageId` (falling back to a generated\n * id only when the snapshot omits one). Messages that already carry `parts`\n * (e.g. a TanStack server echoing `UIMessage`s back over the wire) pass through\n * unchanged apart from ensuring an id.\n */\nexport function aguiSnapshotMessageToUIMessage(\n message: AGUIMessage | UIMessage,\n): UIMessage {\n if ('parts' in message) {\n return { ...message, id: message.id || generateMessageId() }\n }\n\n const id = message.id || generateMessageId()\n\n switch (message.role) {\n case 'user':\n return {\n id,\n role: 'user',\n parts: aguiUserContentToParts(message.content),\n }\n case 'assistant':\n return modelMessageToUIMessage(\n {\n role: 'assistant',\n content: message.content ?? null,\n ...(message.toolCalls && { toolCalls: message.toolCalls }),\n },\n id,\n )\n case 'tool':\n return modelMessageToUIMessage(\n {\n role: 'tool',\n content: message.content,\n toolCallId: message.toolCallId,\n },\n id,\n )\n case 'system':\n case 'developer':\n // `ModelMessage` has no system/developer role; build the part directly.\n return {\n id,\n role: 'system',\n parts: message.content\n ? [{ type: 'text', content: message.content }]\n : [],\n }\n case 'reasoning':\n return {\n id,\n role: 'assistant',\n parts: message.content\n ? [{ type: 'thinking', content: message.content }]\n : [],\n }\n case 'activity':\n default:\n // `activity` (and any future role) has no text/parts equivalent today.\n return { id, role: 'assistant', parts: [] }\n }\n}\n\n/**\n * Convert AG-UI user message content into `UIMessage` parts.\n *\n * AG-UI user content is either a plain string or a multimodal array whose text\n * entries use `{ type: 'text', text }` (vs. TanStack's `{ type: 'text', content }`).\n * Text entries are rewritten to the TanStack shape; image/audio/video/document\n * entries already match `ContentPart` and pass through. `binary` entries have no\n * TanStack equivalent and are dropped.\n */\nfunction aguiUserContentToParts(\n content: Extract<AGUIMessage, { role: 'user' }>['content'],\n): Array<MessagePart> {\n if (typeof content === 'string') {\n return content ? [{ type: 'text', content }] : []\n }\n\n const parts: Array<MessagePart> = []\n for (const part of content) {\n if (part.type === 'text') {\n parts.push({ type: 'text', content: part.text })\n } else if (part.type !== 'binary') {\n parts.push(part)\n }\n }\n return parts\n}\n\n/**\n * Convert an array of ModelMessages to UIMessages\n *\n * This handles merging tool result messages with their corresponding assistant messages\n *\n * @param modelMessages - Array of ModelMessages to convert\n * @returns Array of UIMessages\n */\nexport function modelMessagesToUIMessages(\n modelMessages: Array<ModelMessage>,\n): Array<UIMessage> {\n const uiMessages: Array<UIMessage> = []\n let currentAssistantMessage: UIMessage | null = null\n\n for (const msg of modelMessages) {\n if (msg.role === 'tool') {\n // Tool result - merge into the last assistant message if possible\n if (\n msg.toolCallId !== undefined &&\n currentAssistantMessage &&\n currentAssistantMessage.role === 'assistant'\n ) {\n const content = getTextContent(msg.content)\n const toolCallPart = currentAssistantMessage.parts.find(\n (part): part is ToolCallPart =>\n part.type === 'tool-call' && part.id === msg.toolCallId,\n )\n\n if (toolCallPart) {\n toolCallPart.output = parseToolResultContent(content)\n toolCallPart.state = 'complete'\n }\n\n currentAssistantMessage.parts.push({\n type: 'tool-result',\n toolCallId: msg.toolCallId,\n content,\n state: 'complete',\n })\n } else {\n // No assistant message to merge into, create a standalone one\n const toolResultUIMessage = modelMessageToUIMessage(msg)\n uiMessages.push(toolResultUIMessage)\n }\n } else {\n // Regular message\n const uiMessage = modelMessageToUIMessage(msg)\n uiMessages.push(uiMessage)\n\n // Track assistant messages for potential tool result merging\n if (msg.role === 'assistant') {\n currentAssistantMessage = uiMessage\n } else {\n currentAssistantMessage = null\n }\n }\n }\n\n return uiMessages\n}\n\n/**\n * Normalize a message (UIMessage or ModelMessage) to a UIMessage\n * Ensures the message has an ID and createdAt timestamp\n *\n * @param message - Either a UIMessage or ModelMessage\n * @param generateId - Function to generate a message ID if needed\n * @returns A UIMessage with guaranteed id and createdAt\n */\nexport function normalizeToUIMessage(\n message: UIMessage | ModelMessage,\n generateId: () => string,\n): UIMessage {\n if ('parts' in message) {\n // Already a UIMessage\n return {\n ...message,\n id: message.id || generateId(),\n createdAt: message.createdAt || new Date(),\n }\n } else {\n // ModelMessage - convert to UIMessage\n return {\n ...modelMessageToUIMessage(message, generateId()),\n createdAt: new Date(),\n }\n }\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `msg-${Date.now()}-${Math.random().toString(36).substring(7)}`\n}\n"],"names":[],"mappings":"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"}
@@ -15,6 +15,8 @@ export declare function updateToolCallPart(messages: Array<UIMessage>, messageId
15
15
  name: string;
16
16
  arguments: string;
17
17
  state: ToolCallState;
18
+ /** Parsed input — set when the arguments are complete. */
19
+ input?: unknown;
18
20
  metadata?: Record<string, unknown>;
19
21
  }): Array<UIMessage>;
20
22
  /**
@@ -24,15 +24,17 @@ function updateToolCallPart(messages, messageId, toolCall) {
24
24
  (p) => p.type === "tool-call" && p.id === toolCall.id
25
25
  );
26
26
  const metadata = toolCall.metadata ?? existing?.metadata;
27
+ const input = toolCall.input ?? existing?.input;
27
28
  const toolCallPart = {
28
29
  type: "tool-call",
29
30
  id: toolCall.id,
30
31
  name: toolCall.name,
31
32
  arguments: toolCall.arguments,
32
33
  state: toolCall.state,
33
- // Carry forward approval and output from the existing part
34
+ // Carry forward approval, output and parsed input from the existing part
34
35
  ...existing?.approval && { approval: { ...existing.approval } },
35
36
  ...existing?.output !== void 0 && { output: existing.output },
37
+ ...input !== void 0 && { input },
36
38
  ...metadata !== void 0 && { metadata }
37
39
  };
38
40
  if (existing) {
@@ -1 +1 @@
1
- {"version":3,"file":"message-updaters.js","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 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\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 and output from the existing part\n ...(existing?.approval && { approval: { ...existing.approval } }),\n ...(existing?.output !== undefined && { output: existing.output }),\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"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"message-updaters.js","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"],"names":[],"mappings":"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"}
@@ -823,12 +823,26 @@ class StreamProcessor {
823
823
  const existingToolCall = msgState.toolCalls.get(chunk.toolCallId);
824
824
  if (existingToolCall && existingToolCall.state !== "input-complete") {
825
825
  if (chunk.input !== void 0 && !existingToolCall.arguments) {
826
- existingToolCall.arguments = JSON.stringify(chunk.input);
826
+ try {
827
+ existingToolCall.arguments = JSON.stringify(chunk.input);
828
+ } catch {
829
+ }
827
830
  }
828
831
  const index = msgState.toolCallOrder.indexOf(chunk.toolCallId);
829
832
  this.completeToolCall(messageId, index, existingToolCall);
830
833
  if (chunk.input !== void 0) {
831
834
  existingToolCall.parsedArguments = chunk.input;
835
+ this.messages = updateToolCallPart(this.messages, messageId, {
836
+ id: existingToolCall.id,
837
+ name: existingToolCall.name,
838
+ arguments: existingToolCall.arguments,
839
+ state: "input-complete",
840
+ input: chunk.input,
841
+ ...existingToolCall.metadata !== void 0 && {
842
+ metadata: existingToolCall.metadata
843
+ }
844
+ });
845
+ this.emitMessagesChange();
832
846
  }
833
847
  }
834
848
  if (chunk.result) {
@@ -1247,6 +1261,9 @@ class StreamProcessor {
1247
1261
  name: toolCall.name,
1248
1262
  arguments: toolCall.arguments,
1249
1263
  state: "input-complete",
1264
+ ...toolCall.parsedArguments !== void 0 && {
1265
+ input: toolCall.parsedArguments
1266
+ },
1250
1267
  ...toolCall.metadata !== void 0 && { metadata: toolCall.metadata }
1251
1268
  });
1252
1269
  this.emitMessagesChange();