@tanstack/ai 0.52.2 → 0.53.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/dist/esm/activities/chat/index.js +1 -1
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +10 -11
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js +2 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/byok/define-provider.d.ts +6 -0
- package/dist/esm/byok/define-provider.js +2 -1
- package/dist/esm/byok/define-provider.js.map +1 -1
- package/dist/esm/byok/get-key.d.ts +7 -0
- package/dist/esm/byok/get-key.js +8 -1
- package/dist/esm/byok/get-key.js.map +1 -1
- package/dist/esm/byok/server.d.ts +1 -1
- package/dist/esm/byok/server.js +2 -2
- package/package.json +1 -1
- package/skills/ai-core/adapter-configuration/SKILL.md +32 -12
- package/skills/ai-core/adapter-configuration/references/openai-adapter.md +5 -0
- package/skills/ai-core/structured-outputs/SKILL.md +1 -0
- package/src/activities/chat/index.ts +1 -1
- package/src/activities/chat/messages.ts +16 -14
- package/src/activities/summarize/chat-stream-summarize.ts +2 -0
- package/src/byok/define-provider.ts +7 -0
- package/src/byok/get-key.ts +18 -0
- package/src/byok/server.ts +1 -1
|
@@ -95,13 +95,11 @@ function convertMessagesToModelMessages(messages) {
|
|
|
95
95
|
if (role === "tool" && modelMessage.toolCallId && anchoredToolCallIds.has(modelMessage.toolCallId)) continue;
|
|
96
96
|
if (role === "reasoning") {
|
|
97
97
|
const content = msg.content;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
});
|
|
104
|
-
}
|
|
98
|
+
const signature = encryptedValueFrom(msg);
|
|
99
|
+
if (content || signature !== void 0) pendingThinking.push({
|
|
100
|
+
content: typeof content === "string" ? content : "",
|
|
101
|
+
...signature !== void 0 ? { signature } : {}
|
|
102
|
+
});
|
|
105
103
|
continue;
|
|
106
104
|
}
|
|
107
105
|
if (role === "activity") continue;
|
|
@@ -381,7 +379,7 @@ function buildAssistantMessages(uiMessage) {
|
|
|
381
379
|
}
|
|
382
380
|
break;
|
|
383
381
|
case "thinking":
|
|
384
|
-
if (part.content) {
|
|
382
|
+
if (part.content || part.signature) {
|
|
385
383
|
if (current.toolCalls.some(isProviderExecutedToolCall)) flushSegment();
|
|
386
384
|
pendingThinking.push({
|
|
387
385
|
content: part.content,
|
|
@@ -448,7 +446,7 @@ function modelMessageToUIMessage(modelMessage, id) {
|
|
|
448
446
|
const parts = [];
|
|
449
447
|
const createdAt = coerceCreatedAt(modelMessage.createdAt);
|
|
450
448
|
if (modelMessage.role === "assistant" && modelMessage.thinking?.length) for (const thinking of modelMessage.thinking) {
|
|
451
|
-
if (!thinking.content) continue;
|
|
449
|
+
if (!thinking.content && !thinking.signature) continue;
|
|
452
450
|
parts.push({
|
|
453
451
|
type: "thinking",
|
|
454
452
|
content: thinking.content,
|
|
@@ -585,12 +583,13 @@ function aguiSnapshotMessageToUIMessage(message) {
|
|
|
585
583
|
});
|
|
586
584
|
case "reasoning": {
|
|
587
585
|
const signature = encryptedValueFrom(message);
|
|
586
|
+
const content = typeof message.content === "string" ? message.content : "";
|
|
588
587
|
return applySnapshotMetadata(message, {
|
|
589
588
|
id,
|
|
590
589
|
role: "assistant",
|
|
591
|
-
parts:
|
|
590
|
+
parts: content || signature !== void 0 ? [{
|
|
592
591
|
type: "thinking",
|
|
593
|
-
content
|
|
592
|
+
content,
|
|
594
593
|
...signature !== void 0 ? { signature } : {}
|
|
595
594
|
}] : []
|
|
596
595
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","names":[],"sources":["../../../../src/activities/chat/messages.ts"],"sourcesContent":["import { isProviderExecutedToolCall } from '../../utilities/provider-executed'\nimport {\n isContentPartArray,\n normalizeToolResult,\n} from '../../utilities/tool-result'\nimport { tanstackMetadata } from '../../utilities/merge-metadata'\nimport type { Message as AGUIMessage } from '@ag-ui/core'\nimport type {\n ContentPart,\n MessagePart,\n ModelMessage,\n StructuredOutputPart,\n TanStackMessageMetadata,\n TextPart,\n ToolCall,\n ToolCallPart,\n UIMessage,\n UIResourcePart,\n} from '../../types'\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\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\nexport function safeJsonStringify(value: unknown): string {\n try {\n return JSON.stringify(value) ?? ''\n } catch {\n return ''\n }\n}\n\nfunction nonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value !== '' ? value : undefined\n}\n\nfunction encryptedValueFrom(value: object): string | undefined {\n if ('encryptedValue' in value) {\n const fromSpec = nonEmptyString(value.encryptedValue)\n if (fromSpec !== undefined) return fromSpec\n }\n return nonEmptyString(tanstackMetadata(value)?.signature)\n}\n\nfunction toolCallFromWire(toolCall: ToolCall, bag: unknown): ToolCall {\n const fromBag =\n bag != null && typeof bag === 'object' && !Array.isArray(bag)\n ? bag\n : undefined\n const encrypted = encryptedValueFrom(toolCall)\n if (bag === null && encrypted === undefined) {\n return { ...toolCall, metadata: null }\n }\n if (fromBag === undefined && encrypted === undefined) return toolCall\n return {\n ...toolCall,\n metadata: {\n ...(fromBag ?? {}),\n ...(encrypted !== undefined ? { thoughtSignature: encrypted } : {}),\n },\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(\n content: string | null | undefined | Array<ContentPart>,\n): string {\n // Tool-call-only assistant turns carry no text and reach here as `null` or\n // `undefined`; both must collapse to an empty string rather than crash on\n // `.filter` (issue #532 — the interrupt-boundary MessagesSnapshot).\n if (content === null || content === undefined) 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\nfunction toolResultContent(\n content: string | null | undefined | Array<ContentPart>,\n): string | Array<ContentPart> {\n return Array.isArray(content) ? content : getTextContent(content)\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 let pendingThinking: Array<{ content: string; signature?: string }> = []\n for (const msg of messages) {\n if ('parts' in msg) {\n modelMessages.push(...uiMessageToModelMessages(msg))\n continue\n }\n\n const modelMessage = restoreModelMessageCreatedAt(\n restoreToolResultOwnership(msg),\n )\n const role = (modelMessage as { role: string }).role\n\n if (\n role === 'tool' &&\n modelMessage.toolCallId &&\n anchoredToolCallIds.has(modelMessage.toolCallId)\n ) {\n continue\n }\n\n if (role === 'reasoning') {\n const content = (msg as { content?: string }).content\n if (content) {\n const signature = encryptedValueFrom(msg)\n pendingThinking.push({\n content,\n ...(signature !== undefined ? { signature } : {}),\n })\n }\n continue\n }\n\n if (role === 'activity') {\n continue\n }\n\n if (role === 'developer') {\n modelMessages.push({\n role: 'system' as ModelMessage['role'],\n content: (msg as { content: string }).content,\n ...optionalName(modelMessage),\n ...optionalCreatedAt(modelMessage),\n ...(modelMessage.metadata !== undefined && {\n metadata: modelMessage.metadata,\n }),\n })\n continue\n }\n\n if (\n role === 'user' &&\n Array.isArray((msg as { content?: unknown }).content)\n ) {\n const content = (msg as { content: Array<{ type: string }> }).content\n // TanStack ModelMessage text parts use `{ content }`. AG-UI wire text\n // parts use `{ text }`. Only rewrite the AG-UI shape.\n if (\n !content.some(\n (part) =>\n part.type === 'text' && 'text' in part && !('content' in part),\n )\n ) {\n modelMessages.push(modelMessage)\n continue\n }\n const parts = aguiUserContentToParts(\n content as Extract<AGUIMessage, { role: 'user' }>['content'],\n )\n const contentParts = parts.filter(isContentPart)\n modelMessages.push({\n role: 'user',\n content: collapseContentParts(contentParts),\n ...((msg as { id?: string }).id !== undefined && {\n id: (msg as { id: string }).id,\n }),\n ...optionalName(modelMessage),\n ...optionalCreatedAt(modelMessage),\n ...(modelMessage.metadata !== undefined && {\n metadata: modelMessage.metadata,\n }),\n })\n continue\n }\n\n if (role === 'assistant') {\n const source = modelMessage\n const toolCallMetadata = tanstackMetadata(msg)?.toolCallMetadata\n const toolCalls = source.toolCalls?.map((toolCall) =>\n toolCallFromWire(toolCall, toolCallMetadata?.[toolCall.id]),\n )\n modelMessages.push({\n ...source,\n ...(toolCalls !== undefined ? { toolCalls } : {}),\n ...(pendingThinking.length > 0\n ? {\n thinking: [...(source.thinking ?? []), ...pendingThinking],\n }\n : {}),\n })\n pendingThinking = []\n continue\n }\n\n modelMessages.push(modelMessage)\n }\n return modelMessages\n}\n\nfunction restoreModelMessageCreatedAt(message: ModelMessage): ModelMessage {\n const createdAt =\n coerceCreatedAt(message.createdAt) ?? createdAtFromMetadata(message)\n if (createdAt === undefined) {\n if (message.createdAt === undefined) return message\n const { createdAt: _invalid, ...rest } = message\n return rest\n }\n return Object.is(message.createdAt, createdAt)\n ? message\n : { ...message, createdAt }\n}\n\nexport function restoreToolResultOwnership<T extends object>(message: T): T {\n if (!('role' in message) || message.role !== 'tool') return message\n const source = message\n const metadata = tanstackMetadata(source)\n const owned = metadata?.toolResult\n if (!isRecord(owned)) return message\n if ('content' in owned && !isContentPartArray(owned.content)) return message\n const next = { ...message }\n if (!('id' in owned)) Reflect.deleteProperty(next, 'id')\n if (!('createdAt' in owned)) Reflect.deleteProperty(next, 'createdAt')\n if (typeof owned.id === 'string') Reflect.set(next, 'id', owned.id)\n if (typeof owned.createdAt === 'string') {\n const createdAt = coerceCreatedAt(owned.createdAt)\n if (createdAt) Reflect.set(next, 'createdAt', createdAt)\n }\n if (isContentPartArray(owned.content)) {\n Reflect.set(next, 'content', owned.content)\n }\n const sourceMetadata: unknown = Reflect.get(source, 'metadata')\n if (isRecord(sourceMetadata)) {\n const tanstack = sourceMetadata.tanstack\n if (isRecord(tanstack)) {\n const { toolResult: _toolResult, ...restTanstack } = tanstack\n const restMetadata = { ...sourceMetadata }\n if (Object.keys(restTanstack).length) restMetadata.tanstack = restTanstack\n else delete restMetadata.tanstack\n if (Object.keys(restMetadata).length)\n Reflect.set(next, 'metadata', restMetadata)\n else Reflect.deleteProperty(next, 'metadata')\n }\n }\n return next\n}\n\n/**\n * Rebuild a `Date` from a live `Date` or from an ISO string.\n * `JSON.stringify` turns `Date` into a string, so persistence reload\n * and AG-UI wire both land here as strings.\n */\nexport function coerceCreatedAt(value: unknown): Date | undefined {\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? undefined : value\n }\n if (typeof value !== 'string') return undefined\n const createdAt = new Date(value)\n return Number.isNaN(createdAt.getTime()) ? undefined : createdAt\n}\n\nfunction normalizeMessagePart(part: MessagePart): MessagePart {\n if (part.type !== 'tool-result') return part\n const createdAt = coerceCreatedAt(part.createdAt)\n const { createdAt: _createdAt, ...rest } = part\n return createdAt === undefined ? rest : { ...rest, createdAt }\n}\n\nfunction createdAtFromMetadata(source: object): Date | undefined {\n return coerceCreatedAt(tanstackMetadata(source)?.createdAt)\n}\n\nfunction optionalCreatedAt(source: { createdAt?: unknown }): {\n createdAt?: Date\n} {\n const createdAt = coerceCreatedAt(source.createdAt)\n return createdAt !== undefined ? { createdAt } : {}\n}\n\nfunction optionalName(source: { name?: string }): { name?: string } {\n return source.name !== undefined ? { name: source.name } : {}\n}\n\nfunction isUiResourcePart(value: unknown): value is UIResourcePart {\n if (value == null || typeof value !== 'object' || Array.isArray(value)) {\n return false\n }\n if (!('type' in value) || value.type !== 'ui-resource') return false\n if (!('toolCallId' in value) || typeof value.toolCallId !== 'string') {\n return false\n }\n if (!('toolName' in value) || typeof value.toolName !== 'string') {\n return false\n }\n if (\n !('resource' in value) ||\n value.resource == null ||\n typeof value.resource !== 'object' ||\n Array.isArray(value.resource)\n ) {\n return false\n }\n const resource = value.resource\n return (\n 'uri' in resource &&\n typeof resource.uri === 'string' &&\n 'mimeType' in resource &&\n typeof resource.mimeType === 'string'\n )\n}\n\nfunction uiResourceKey(part: UIResourcePart): string {\n return `${part.toolCallId}\\0${part.toolName}\\0${part.resource.uri}`\n}\n\nfunction appendUiResources(\n ui: UIMessage,\n resources: ReadonlyArray<UIResourcePart>,\n): UIMessage {\n if (resources.length === 0) return ui\n const seen = new Set(\n ui.parts.filter(isUiResourcePart).map((part) => uiResourceKey(part)),\n )\n const extra = resources.filter((part) => !seen.has(uiResourceKey(part)))\n if (extra.length === 0) return ui\n return { ...ui, parts: [...ui.parts, ...extra] }\n}\n\nfunction assistantMetadata(\n uiMessage: UIMessage,\n): UIMessage['metadata'] | undefined {\n const fromParts = uiMessage.parts.filter(isUiResourcePart)\n const current = uiMessage.metadata ?? {}\n const previous = tanstackMetadata(uiMessage)\n const tanstack: TanStackMessageMetadata = {}\n if (previous?.model !== undefined) tanstack.model = previous.model\n if (previous?.signature !== undefined) tanstack.signature = previous.signature\n if (fromParts.length > 0) tanstack.uiResources = fromParts\n const result = { ...current }\n if (Object.keys(tanstack).length > 0) result.tanstack = tanstack\n else delete result.tanstack\n return Object.keys(result).length > 0 ? result : undefined\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 id: uiMessage.id,\n role: uiMessage.role as 'user' | 'assistant' | 'tool',\n content: collapseContentParts(contentParts),\n ...optionalName(uiMessage),\n ...optionalCreatedAt(uiMessage),\n ...(uiMessage.metadata !== undefined && { metadata: uiMessage.metadata }),\n }\n}\n\n// Accumulator for building an assistant segment (content + tool calls)\ninterface AssistantSegment {\n contentParts: Array<ContentPart>\n structuredOutput?: StructuredOutputPart\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-requested' ||\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 // A single UI message can fan out into several model messages. Keep the\n // shared UI id on each one so persistence can retain the original identity.\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 const identityFields = {\n id: uiMessage.id,\n ...optionalName(uiMessage),\n ...optionalCreatedAt(uiMessage),\n }\n const metadata = assistantMetadata(uiMessage)\n const assistantFields = {\n ...identityFields,\n ...(metadata !== undefined && { metadata }),\n }\n\n function flushSegment(force = false): void {\n const content = collapseContentParts(current.contentParts)\n const hasContent = content !== null\n const hasToolCalls = current.toolCalls.length > 0\n const hasThinking = pendingThinking.length > 0\n\n if (force || hasContent || hasToolCalls || hasThinking) {\n messageList.push({\n ...assistantFields,\n role: 'assistant',\n content,\n ...(hasToolCalls && { toolCalls: current.toolCalls }),\n ...(hasThinking && { thinking: pendingThinking }),\n ...(current.structuredOutput && {\n structuredOutput: current.structuredOutput,\n }),\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(messageList.length === 0)\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 ...(part.id !== undefined && { id: part.id }),\n ...optionalCreatedAt(part),\n role: 'tool',\n content: part.content,\n toolCallId: part.toolCallId,\n ...(part.name !== undefined && { name: part.name }),\n ...(part.metadata !== undefined && { metadata: part.metadata }),\n ...(part.error !== undefined && { error: part.error }),\n })\n emittedToolResultIds.add(part.toolCallId)\n }\n break\n\n case 'thinking':\n if (part.content) {\n // Provider-executed tools have no tool-result part, so thinking\n // after them has to start the next segment or it replays first.\n if (current.toolCalls.some(isProviderExecutedToolCall)) {\n flushSegment()\n }\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 current.structuredOutput = part\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 ...assistantFields,\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 const createdAt = coerceCreatedAt(modelMessage.createdAt)\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 const structuredOutput =\n modelMessage.structuredOutput ??\n snapshotStructuredOutput(tanstackMetadata(modelMessage)?.structuredOutput)\n if (modelMessage.role === 'assistant' && structuredOutput) {\n if (\n typeof modelMessage.content === 'string' &&\n modelMessage.content &&\n modelMessage.content !== structuredOutput.raw\n ) {\n const suffix = structuredOutput.raw\n const text =\n suffix !== '' && modelMessage.content.endsWith(suffix)\n ? modelMessage.content.slice(0, -suffix.length)\n : modelMessage.content\n if (text) parts.push({ type: 'text', content: text })\n }\n parts.push(structuredOutput)\n } else if (modelMessage.role === 'tool' && modelMessage.toolCallId) {\n parts.push({\n type: 'tool-result',\n toolCallId: modelMessage.toolCallId,\n content: toolResultContent(modelMessage.content),\n state: modelMessage.error === undefined ? 'complete' : 'error',\n ...(modelMessage.id !== undefined && { id: modelMessage.id }),\n ...(modelMessage.name !== undefined && { name: modelMessage.name }),\n ...(modelMessage.metadata !== undefined && {\n metadata: modelMessage.metadata,\n }),\n ...(createdAt !== undefined && { createdAt }),\n ...(modelMessage.error !== undefined && { error: modelMessage.error }),\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 const ui: UIMessage = {\n id: id || generateMessageId(),\n role: modelMessage.role === 'tool' ? 'assistant' : modelMessage.role,\n parts,\n ...optionalName(modelMessage),\n ...(createdAt !== undefined && { createdAt }),\n ...(modelMessage.metadata !== undefined && {\n metadata: modelMessage.metadata,\n }),\n }\n const storedResources = tanstackMetadata(modelMessage)?.uiResources\n return appendUiResources(\n ui,\n Array.isArray(storedResources)\n ? storedResources.filter(isUiResourcePart)\n : [],\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 applySnapshotMetadata(message, {\n ...message,\n id: message.id || generateMessageId(),\n })\n }\n\n const id = message.id || generateMessageId()\n\n switch (message.role) {\n case 'user':\n return applySnapshotMetadata(message, {\n id,\n role: 'user',\n parts: aguiUserContentToParts(message.content),\n })\n case 'assistant': {\n const metadata = tanstackMetadata(message)\n const toolCallMetadata = metadata?.toolCallMetadata\n const structuredOutput = snapshotStructuredOutput(\n metadata?.structuredOutput,\n )\n const toolCalls = message.toolCalls?.map((toolCall) => {\n const callMetadata =\n toolCallMetadata != null && typeof toolCallMetadata === 'object'\n ? (toolCallMetadata as Record<string, unknown>)[toolCall.id]\n : undefined\n return callMetadata !== undefined\n ? { ...toolCall, metadata: callMetadata }\n : toolCall\n })\n return applySnapshotMetadata(\n message,\n modelMessageToUIMessage(\n {\n role: 'assistant',\n content: message.content ?? null,\n ...optionalName(message),\n ...(toolCalls && { toolCalls }),\n ...(structuredOutput && { structuredOutput }),\n },\n id,\n ),\n )\n }\n case 'tool': {\n message = restoreToolResultOwnership(message)\n const createdAt =\n coerceCreatedAt(\n 'createdAt' in message ? message.createdAt : undefined,\n ) ?? createdAtFromMetadata(message)\n return applySnapshotMetadata(\n message,\n modelMessageToUIMessage(\n {\n role: 'tool',\n content: message.content,\n toolCallId: message.toolCallId,\n ...('name' in message && typeof message.name === 'string'\n ? { name: message.name }\n : {}),\n ...('id' in message && typeof message.id === 'string'\n ? { id: message.id }\n : {}),\n ...('metadata' in message && message.metadata != null\n ? { metadata: message.metadata }\n : {}),\n ...(createdAt !== undefined ? { createdAt } : {}),\n ...(message.error !== undefined && { error: message.error }),\n },\n id,\n ),\n )\n }\n case 'system':\n case 'developer':\n // `ModelMessage` has no system/developer role; build the part directly.\n return applySnapshotMetadata(message, {\n id,\n role: 'system',\n parts: message.content\n ? [{ type: 'text', content: message.content }]\n : [],\n })\n case 'reasoning': {\n const signature = encryptedValueFrom(message)\n return applySnapshotMetadata(message, {\n id,\n role: 'assistant',\n parts: message.content\n ? [\n {\n type: 'thinking' as const,\n content: message.content,\n ...(signature !== undefined ? { signature } : {}),\n },\n ]\n : [],\n })\n }\n case 'activity':\n default:\n // `activity` (and any future role) has no text/parts equivalent today.\n return applySnapshotMetadata(message, {\n id,\n role: 'assistant',\n parts: [],\n })\n }\n}\n\n/** Copy snapshot metadata when it is a record. Rebuild createdAt from tanstack.createdAt. */\nfunction applySnapshotMetadata(source: object, ui: UIMessage): UIMessage {\n const normalizedParts = ui.parts.map((part) => normalizeMessagePart(part))\n if (normalizedParts !== ui.parts) ui = { ...ui, parts: normalizedParts }\n const name =\n 'name' in source && typeof source.name === 'string'\n ? source.name\n : undefined\n let next = name !== undefined ? { ...ui, name } : ui\n\n let metadata: NonNullable<UIMessage['metadata']> | undefined\n if ('metadata' in source) {\n const raw = source.metadata\n if (raw != null && typeof raw === 'object' && !Array.isArray(raw)) {\n metadata = raw as NonNullable<UIMessage['metadata']>\n next = { ...next, metadata }\n }\n }\n\n const createdAt =\n coerceCreatedAt(next.createdAt) ??\n (metadata !== undefined ? createdAtFromMetadata(metadata) : undefined)\n if (createdAt !== undefined && !Object.is(createdAt, next.createdAt)) {\n next = { ...next, createdAt }\n } else if (createdAt === undefined && next.createdAt !== undefined) {\n const { createdAt: _invalid, ...rest } = next\n next = rest\n }\n\n const uiResources = tanstackMetadata(metadata)?.uiResources\n const resources = Array.isArray(uiResources)\n ? uiResources.filter(isUiResourcePart)\n : []\n return appendUiResources(next, resources)\n}\n\nfunction snapshotStructuredOutput(\n value: TanStackMessageMetadata['structuredOutput'],\n): StructuredOutputPart | undefined {\n if (\n value == null ||\n (value.status !== 'streaming' &&\n value.status !== 'complete' &&\n value.status !== 'error') ||\n typeof value.raw !== 'string'\n ) {\n return undefined\n }\n return {\n type: 'structured-output',\n status: value.status,\n raw: value.raw,\n ...(value.partial !== undefined ? { partial: value.partial } : {}),\n ...(value.data !== undefined ? { data: value.data } : {}),\n ...(value.reasoning ? { reasoning: value.reasoning } : {}),\n ...(value.errorMessage !== undefined\n ? { errorMessage: value.errorMessage }\n : {}),\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 = toolResultContent(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 =\n typeof content === 'string'\n ? parseToolResultContent(content)\n : content\n toolCallPart.state = msg.error === undefined ? 'complete' : 'error'\n }\n\n currentAssistantMessage.parts.push({\n type: 'tool-result',\n toolCallId: msg.toolCallId,\n content,\n state: msg.error === undefined ? 'complete' : 'error',\n ...(msg.id !== undefined &&\n msg.id !== currentAssistantMessage.id && { id: msg.id }),\n ...(msg.name !== undefined && { name: msg.name }),\n ...(msg.metadata !== undefined && { metadata: msg.metadata }),\n ...optionalCreatedAt(msg),\n ...(msg.error !== undefined && { error: msg.error }),\n })\n } else {\n // No assistant message to merge into, create a standalone one\n const toolResultUIMessage = modelMessageToUIMessage(msg, msg.id)\n uiMessages.push(toolResultUIMessage)\n }\n } else {\n // Regular message. Preserve a persisted stable id so a hydrated message\n // keeps the same identity as its live stream (enables in-place resume).\n const uiMessage = modelMessageToUIMessage(msg, msg.id)\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 const parts = message.parts.map((part) => normalizeMessagePart(part))\n return {\n ...message,\n parts,\n id: message.id || generateId(),\n createdAt: coerceCreatedAt(message.createdAt) ?? new Date(),\n }\n } else {\n // ModelMessage - convert to UIMessage\n return {\n ...modelMessageToUIMessage(message, generateId()),\n createdAt: coerceCreatedAt(message.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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"messages.js","names":[],"sources":["../../../../src/activities/chat/messages.ts"],"sourcesContent":["import { isProviderExecutedToolCall } from '../../utilities/provider-executed'\nimport {\n isContentPartArray,\n normalizeToolResult,\n} from '../../utilities/tool-result'\nimport { tanstackMetadata } from '../../utilities/merge-metadata'\nimport type { Message as AGUIMessage } from '@ag-ui/core'\nimport type {\n ContentPart,\n MessagePart,\n ModelMessage,\n StructuredOutputPart,\n TanStackMessageMetadata,\n TextPart,\n ToolCall,\n ToolCallPart,\n UIMessage,\n UIResourcePart,\n} from '../../types'\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\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\nexport function safeJsonStringify(value: unknown): string {\n try {\n return JSON.stringify(value) ?? ''\n } catch {\n return ''\n }\n}\n\nfunction nonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value !== '' ? value : undefined\n}\n\nfunction encryptedValueFrom(value: object): string | undefined {\n if ('encryptedValue' in value) {\n const fromSpec = nonEmptyString(value.encryptedValue)\n if (fromSpec !== undefined) return fromSpec\n }\n return nonEmptyString(tanstackMetadata(value)?.signature)\n}\n\nfunction toolCallFromWire(toolCall: ToolCall, bag: unknown): ToolCall {\n const fromBag =\n bag != null && typeof bag === 'object' && !Array.isArray(bag)\n ? bag\n : undefined\n const encrypted = encryptedValueFrom(toolCall)\n if (bag === null && encrypted === undefined) {\n return { ...toolCall, metadata: null }\n }\n if (fromBag === undefined && encrypted === undefined) return toolCall\n return {\n ...toolCall,\n metadata: {\n ...(fromBag ?? {}),\n ...(encrypted !== undefined ? { thoughtSignature: encrypted } : {}),\n },\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(\n content: string | null | undefined | Array<ContentPart>,\n): string {\n // Tool-call-only assistant turns carry no text and reach here as `null` or\n // `undefined`; both must collapse to an empty string rather than crash on\n // `.filter` (issue #532 — the interrupt-boundary MessagesSnapshot).\n if (content === null || content === undefined) 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\nfunction toolResultContent(\n content: string | null | undefined | Array<ContentPart>,\n): string | Array<ContentPart> {\n return Array.isArray(content) ? content : getTextContent(content)\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 let pendingThinking: Array<{ content: string; signature?: string }> = []\n for (const msg of messages) {\n if ('parts' in msg) {\n modelMessages.push(...uiMessageToModelMessages(msg))\n continue\n }\n\n const modelMessage = restoreModelMessageCreatedAt(\n restoreToolResultOwnership(msg),\n )\n const role = (modelMessage as { role: string }).role\n\n if (\n role === 'tool' &&\n modelMessage.toolCallId &&\n anchoredToolCallIds.has(modelMessage.toolCallId)\n ) {\n continue\n }\n\n if (role === 'reasoning') {\n const content = (msg as { content?: string }).content\n const signature = encryptedValueFrom(msg)\n if (content || signature !== undefined) {\n pendingThinking.push({\n content: typeof content === 'string' ? content : '',\n ...(signature !== undefined ? { signature } : {}),\n })\n }\n continue\n }\n\n if (role === 'activity') {\n continue\n }\n\n if (role === 'developer') {\n modelMessages.push({\n role: 'system' as ModelMessage['role'],\n content: (msg as { content: string }).content,\n ...optionalName(modelMessage),\n ...optionalCreatedAt(modelMessage),\n ...(modelMessage.metadata !== undefined && {\n metadata: modelMessage.metadata,\n }),\n })\n continue\n }\n\n if (\n role === 'user' &&\n Array.isArray((msg as { content?: unknown }).content)\n ) {\n const content = (msg as { content: Array<{ type: string }> }).content\n // TanStack ModelMessage text parts use `{ content }`. AG-UI wire text\n // parts use `{ text }`. Only rewrite the AG-UI shape.\n if (\n !content.some(\n (part) =>\n part.type === 'text' && 'text' in part && !('content' in part),\n )\n ) {\n modelMessages.push(modelMessage)\n continue\n }\n const parts = aguiUserContentToParts(\n content as Extract<AGUIMessage, { role: 'user' }>['content'],\n )\n const contentParts = parts.filter(isContentPart)\n modelMessages.push({\n role: 'user',\n content: collapseContentParts(contentParts),\n ...((msg as { id?: string }).id !== undefined && {\n id: (msg as { id: string }).id,\n }),\n ...optionalName(modelMessage),\n ...optionalCreatedAt(modelMessage),\n ...(modelMessage.metadata !== undefined && {\n metadata: modelMessage.metadata,\n }),\n })\n continue\n }\n\n if (role === 'assistant') {\n const source = modelMessage\n const toolCallMetadata = tanstackMetadata(msg)?.toolCallMetadata\n const toolCalls = source.toolCalls?.map((toolCall) =>\n toolCallFromWire(toolCall, toolCallMetadata?.[toolCall.id]),\n )\n modelMessages.push({\n ...source,\n ...(toolCalls !== undefined ? { toolCalls } : {}),\n ...(pendingThinking.length > 0\n ? {\n thinking: [...(source.thinking ?? []), ...pendingThinking],\n }\n : {}),\n })\n pendingThinking = []\n continue\n }\n\n modelMessages.push(modelMessage)\n }\n return modelMessages\n}\n\nfunction restoreModelMessageCreatedAt(message: ModelMessage): ModelMessage {\n const createdAt =\n coerceCreatedAt(message.createdAt) ?? createdAtFromMetadata(message)\n if (createdAt === undefined) {\n if (message.createdAt === undefined) return message\n const { createdAt: _invalid, ...rest } = message\n return rest\n }\n return Object.is(message.createdAt, createdAt)\n ? message\n : { ...message, createdAt }\n}\n\nexport function restoreToolResultOwnership<T extends object>(message: T): T {\n if (!('role' in message) || message.role !== 'tool') return message\n const source = message\n const metadata = tanstackMetadata(source)\n const owned = metadata?.toolResult\n if (!isRecord(owned)) return message\n if ('content' in owned && !isContentPartArray(owned.content)) return message\n const next = { ...message }\n if (!('id' in owned)) Reflect.deleteProperty(next, 'id')\n if (!('createdAt' in owned)) Reflect.deleteProperty(next, 'createdAt')\n if (typeof owned.id === 'string') Reflect.set(next, 'id', owned.id)\n if (typeof owned.createdAt === 'string') {\n const createdAt = coerceCreatedAt(owned.createdAt)\n if (createdAt) Reflect.set(next, 'createdAt', createdAt)\n }\n if (isContentPartArray(owned.content)) {\n Reflect.set(next, 'content', owned.content)\n }\n const sourceMetadata: unknown = Reflect.get(source, 'metadata')\n if (isRecord(sourceMetadata)) {\n const tanstack = sourceMetadata.tanstack\n if (isRecord(tanstack)) {\n const { toolResult: _toolResult, ...restTanstack } = tanstack\n const restMetadata = { ...sourceMetadata }\n if (Object.keys(restTanstack).length) restMetadata.tanstack = restTanstack\n else delete restMetadata.tanstack\n if (Object.keys(restMetadata).length)\n Reflect.set(next, 'metadata', restMetadata)\n else Reflect.deleteProperty(next, 'metadata')\n }\n }\n return next\n}\n\n/**\n * Rebuild a `Date` from a live `Date` or from an ISO string.\n * `JSON.stringify` turns `Date` into a string, so persistence reload\n * and AG-UI wire both land here as strings.\n */\nexport function coerceCreatedAt(value: unknown): Date | undefined {\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? undefined : value\n }\n if (typeof value !== 'string') return undefined\n const createdAt = new Date(value)\n return Number.isNaN(createdAt.getTime()) ? undefined : createdAt\n}\n\nfunction normalizeMessagePart(part: MessagePart): MessagePart {\n if (part.type !== 'tool-result') return part\n const createdAt = coerceCreatedAt(part.createdAt)\n const { createdAt: _createdAt, ...rest } = part\n return createdAt === undefined ? rest : { ...rest, createdAt }\n}\n\nfunction createdAtFromMetadata(source: object): Date | undefined {\n return coerceCreatedAt(tanstackMetadata(source)?.createdAt)\n}\n\nfunction optionalCreatedAt(source: { createdAt?: unknown }): {\n createdAt?: Date\n} {\n const createdAt = coerceCreatedAt(source.createdAt)\n return createdAt !== undefined ? { createdAt } : {}\n}\n\nfunction optionalName(source: { name?: string }): { name?: string } {\n return source.name !== undefined ? { name: source.name } : {}\n}\n\nfunction isUiResourcePart(value: unknown): value is UIResourcePart {\n if (value == null || typeof value !== 'object' || Array.isArray(value)) {\n return false\n }\n if (!('type' in value) || value.type !== 'ui-resource') return false\n if (!('toolCallId' in value) || typeof value.toolCallId !== 'string') {\n return false\n }\n if (!('toolName' in value) || typeof value.toolName !== 'string') {\n return false\n }\n if (\n !('resource' in value) ||\n value.resource == null ||\n typeof value.resource !== 'object' ||\n Array.isArray(value.resource)\n ) {\n return false\n }\n const resource = value.resource\n return (\n 'uri' in resource &&\n typeof resource.uri === 'string' &&\n 'mimeType' in resource &&\n typeof resource.mimeType === 'string'\n )\n}\n\nfunction uiResourceKey(part: UIResourcePart): string {\n return `${part.toolCallId}\\0${part.toolName}\\0${part.resource.uri}`\n}\n\nfunction appendUiResources(\n ui: UIMessage,\n resources: ReadonlyArray<UIResourcePart>,\n): UIMessage {\n if (resources.length === 0) return ui\n const seen = new Set(\n ui.parts.filter(isUiResourcePart).map((part) => uiResourceKey(part)),\n )\n const extra = resources.filter((part) => !seen.has(uiResourceKey(part)))\n if (extra.length === 0) return ui\n return { ...ui, parts: [...ui.parts, ...extra] }\n}\n\nfunction assistantMetadata(\n uiMessage: UIMessage,\n): UIMessage['metadata'] | undefined {\n const fromParts = uiMessage.parts.filter(isUiResourcePart)\n const current = uiMessage.metadata ?? {}\n const previous = tanstackMetadata(uiMessage)\n const tanstack: TanStackMessageMetadata = {}\n if (previous?.model !== undefined) tanstack.model = previous.model\n if (previous?.signature !== undefined) tanstack.signature = previous.signature\n if (fromParts.length > 0) tanstack.uiResources = fromParts\n const result = { ...current }\n if (Object.keys(tanstack).length > 0) result.tanstack = tanstack\n else delete result.tanstack\n return Object.keys(result).length > 0 ? result : undefined\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 id: uiMessage.id,\n role: uiMessage.role as 'user' | 'assistant' | 'tool',\n content: collapseContentParts(contentParts),\n ...optionalName(uiMessage),\n ...optionalCreatedAt(uiMessage),\n ...(uiMessage.metadata !== undefined && { metadata: uiMessage.metadata }),\n }\n}\n\n// Accumulator for building an assistant segment (content + tool calls)\ninterface AssistantSegment {\n contentParts: Array<ContentPart>\n structuredOutput?: StructuredOutputPart\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-requested' ||\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 // A single UI message can fan out into several model messages. Keep the\n // shared UI id on each one so persistence can retain the original identity.\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 const identityFields = {\n id: uiMessage.id,\n ...optionalName(uiMessage),\n ...optionalCreatedAt(uiMessage),\n }\n const metadata = assistantMetadata(uiMessage)\n const assistantFields = {\n ...identityFields,\n ...(metadata !== undefined && { metadata }),\n }\n\n function flushSegment(force = false): void {\n const content = collapseContentParts(current.contentParts)\n const hasContent = content !== null\n const hasToolCalls = current.toolCalls.length > 0\n const hasThinking = pendingThinking.length > 0\n\n if (force || hasContent || hasToolCalls || hasThinking) {\n messageList.push({\n ...assistantFields,\n role: 'assistant',\n content,\n ...(hasToolCalls && { toolCalls: current.toolCalls }),\n ...(hasThinking && { thinking: pendingThinking }),\n ...(current.structuredOutput && {\n structuredOutput: current.structuredOutput,\n }),\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(messageList.length === 0)\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 ...(part.id !== undefined && { id: part.id }),\n ...optionalCreatedAt(part),\n role: 'tool',\n content: part.content,\n toolCallId: part.toolCallId,\n ...(part.name !== undefined && { name: part.name }),\n ...(part.metadata !== undefined && { metadata: part.metadata }),\n ...(part.error !== undefined && { error: part.error }),\n })\n emittedToolResultIds.add(part.toolCallId)\n }\n break\n\n case 'thinking':\n if (part.content || part.signature) {\n // Provider-executed tools have no tool-result part, so thinking\n // after them has to start the next segment or it replays first.\n if (current.toolCalls.some(isProviderExecutedToolCall)) {\n flushSegment()\n }\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 current.structuredOutput = part\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 ...assistantFields,\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 const createdAt = coerceCreatedAt(modelMessage.createdAt)\n\n if (modelMessage.role === 'assistant' && modelMessage.thinking?.length) {\n for (const thinking of modelMessage.thinking) {\n if (!thinking.content && !thinking.signature) 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 const structuredOutput =\n modelMessage.structuredOutput ??\n snapshotStructuredOutput(tanstackMetadata(modelMessage)?.structuredOutput)\n if (modelMessage.role === 'assistant' && structuredOutput) {\n if (\n typeof modelMessage.content === 'string' &&\n modelMessage.content &&\n modelMessage.content !== structuredOutput.raw\n ) {\n const suffix = structuredOutput.raw\n const text =\n suffix !== '' && modelMessage.content.endsWith(suffix)\n ? modelMessage.content.slice(0, -suffix.length)\n : modelMessage.content\n if (text) parts.push({ type: 'text', content: text })\n }\n parts.push(structuredOutput)\n } else if (modelMessage.role === 'tool' && modelMessage.toolCallId) {\n parts.push({\n type: 'tool-result',\n toolCallId: modelMessage.toolCallId,\n content: toolResultContent(modelMessage.content),\n state: modelMessage.error === undefined ? 'complete' : 'error',\n ...(modelMessage.id !== undefined && { id: modelMessage.id }),\n ...(modelMessage.name !== undefined && { name: modelMessage.name }),\n ...(modelMessage.metadata !== undefined && {\n metadata: modelMessage.metadata,\n }),\n ...(createdAt !== undefined && { createdAt }),\n ...(modelMessage.error !== undefined && { error: modelMessage.error }),\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 const ui: UIMessage = {\n id: id || generateMessageId(),\n role: modelMessage.role === 'tool' ? 'assistant' : modelMessage.role,\n parts,\n ...optionalName(modelMessage),\n ...(createdAt !== undefined && { createdAt }),\n ...(modelMessage.metadata !== undefined && {\n metadata: modelMessage.metadata,\n }),\n }\n const storedResources = tanstackMetadata(modelMessage)?.uiResources\n return appendUiResources(\n ui,\n Array.isArray(storedResources)\n ? storedResources.filter(isUiResourcePart)\n : [],\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 applySnapshotMetadata(message, {\n ...message,\n id: message.id || generateMessageId(),\n })\n }\n\n const id = message.id || generateMessageId()\n\n switch (message.role) {\n case 'user':\n return applySnapshotMetadata(message, {\n id,\n role: 'user',\n parts: aguiUserContentToParts(message.content),\n })\n case 'assistant': {\n const metadata = tanstackMetadata(message)\n const toolCallMetadata = metadata?.toolCallMetadata\n const structuredOutput = snapshotStructuredOutput(\n metadata?.structuredOutput,\n )\n const toolCalls = message.toolCalls?.map((toolCall) => {\n const callMetadata =\n toolCallMetadata != null && typeof toolCallMetadata === 'object'\n ? (toolCallMetadata as Record<string, unknown>)[toolCall.id]\n : undefined\n return callMetadata !== undefined\n ? { ...toolCall, metadata: callMetadata }\n : toolCall\n })\n return applySnapshotMetadata(\n message,\n modelMessageToUIMessage(\n {\n role: 'assistant',\n content: message.content ?? null,\n ...optionalName(message),\n ...(toolCalls && { toolCalls }),\n ...(structuredOutput && { structuredOutput }),\n },\n id,\n ),\n )\n }\n case 'tool': {\n message = restoreToolResultOwnership(message)\n const createdAt =\n coerceCreatedAt(\n 'createdAt' in message ? message.createdAt : undefined,\n ) ?? createdAtFromMetadata(message)\n return applySnapshotMetadata(\n message,\n modelMessageToUIMessage(\n {\n role: 'tool',\n content: message.content,\n toolCallId: message.toolCallId,\n ...('name' in message && typeof message.name === 'string'\n ? { name: message.name }\n : {}),\n ...('id' in message && typeof message.id === 'string'\n ? { id: message.id }\n : {}),\n ...('metadata' in message && message.metadata != null\n ? { metadata: message.metadata }\n : {}),\n ...(createdAt !== undefined ? { createdAt } : {}),\n ...(message.error !== undefined && { error: message.error }),\n },\n id,\n ),\n )\n }\n case 'system':\n case 'developer':\n // `ModelMessage` has no system/developer role; build the part directly.\n return applySnapshotMetadata(message, {\n id,\n role: 'system',\n parts: message.content\n ? [{ type: 'text', content: message.content }]\n : [],\n })\n case 'reasoning': {\n const signature = encryptedValueFrom(message)\n const content = typeof message.content === 'string' ? message.content : ''\n return applySnapshotMetadata(message, {\n id,\n role: 'assistant',\n parts:\n content || signature !== undefined\n ? [\n {\n type: 'thinking' as const,\n content,\n ...(signature !== undefined ? { signature } : {}),\n },\n ]\n : [],\n })\n }\n case 'activity':\n default:\n // `activity` (and any future role) has no text/parts equivalent today.\n return applySnapshotMetadata(message, {\n id,\n role: 'assistant',\n parts: [],\n })\n }\n}\n\n/** Copy snapshot metadata when it is a record. Rebuild createdAt from tanstack.createdAt. */\nfunction applySnapshotMetadata(source: object, ui: UIMessage): UIMessage {\n const normalizedParts = ui.parts.map((part) => normalizeMessagePart(part))\n if (normalizedParts !== ui.parts) ui = { ...ui, parts: normalizedParts }\n const name =\n 'name' in source && typeof source.name === 'string'\n ? source.name\n : undefined\n let next = name !== undefined ? { ...ui, name } : ui\n\n let metadata: NonNullable<UIMessage['metadata']> | undefined\n if ('metadata' in source) {\n const raw = source.metadata\n if (raw != null && typeof raw === 'object' && !Array.isArray(raw)) {\n metadata = raw as NonNullable<UIMessage['metadata']>\n next = { ...next, metadata }\n }\n }\n\n const createdAt =\n coerceCreatedAt(next.createdAt) ??\n (metadata !== undefined ? createdAtFromMetadata(metadata) : undefined)\n if (createdAt !== undefined && !Object.is(createdAt, next.createdAt)) {\n next = { ...next, createdAt }\n } else if (createdAt === undefined && next.createdAt !== undefined) {\n const { createdAt: _invalid, ...rest } = next\n next = rest\n }\n\n const uiResources = tanstackMetadata(metadata)?.uiResources\n const resources = Array.isArray(uiResources)\n ? uiResources.filter(isUiResourcePart)\n : []\n return appendUiResources(next, resources)\n}\n\nfunction snapshotStructuredOutput(\n value: TanStackMessageMetadata['structuredOutput'],\n): StructuredOutputPart | undefined {\n if (\n value == null ||\n (value.status !== 'streaming' &&\n value.status !== 'complete' &&\n value.status !== 'error') ||\n typeof value.raw !== 'string'\n ) {\n return undefined\n }\n return {\n type: 'structured-output',\n status: value.status,\n raw: value.raw,\n ...(value.partial !== undefined ? { partial: value.partial } : {}),\n ...(value.data !== undefined ? { data: value.data } : {}),\n ...(value.reasoning ? { reasoning: value.reasoning } : {}),\n ...(value.errorMessage !== undefined\n ? { errorMessage: value.errorMessage }\n : {}),\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 = toolResultContent(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 =\n typeof content === 'string'\n ? parseToolResultContent(content)\n : content\n toolCallPart.state = msg.error === undefined ? 'complete' : 'error'\n }\n\n currentAssistantMessage.parts.push({\n type: 'tool-result',\n toolCallId: msg.toolCallId,\n content,\n state: msg.error === undefined ? 'complete' : 'error',\n ...(msg.id !== undefined &&\n msg.id !== currentAssistantMessage.id && { id: msg.id }),\n ...(msg.name !== undefined && { name: msg.name }),\n ...(msg.metadata !== undefined && { metadata: msg.metadata }),\n ...optionalCreatedAt(msg),\n ...(msg.error !== undefined && { error: msg.error }),\n })\n } else {\n // No assistant message to merge into, create a standalone one\n const toolResultUIMessage = modelMessageToUIMessage(msg, msg.id)\n uiMessages.push(toolResultUIMessage)\n }\n } else {\n // Regular message. Preserve a persisted stable id so a hydrated message\n // keeps the same identity as its live stream (enables in-place resume).\n const uiMessage = modelMessageToUIMessage(msg, msg.id)\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 const parts = message.parts.map((part) => normalizeMessagePart(part))\n return {\n ...message,\n parts,\n id: message.id || generateId(),\n createdAt: coerceCreatedAt(message.createdAt) ?? new Date(),\n }\n } else {\n // ModelMessage - convert to UIMessage\n return {\n ...modelMessageToUIMessage(message, generateId()),\n createdAt: coerceCreatedAt(message.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"],"mappings":"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"}
|
|
@@ -48,7 +48,8 @@ var MAX_TOKENS_KEY_BY_ADAPTER = {
|
|
|
48
48
|
groq: "max_completion_tokens",
|
|
49
49
|
gemini: "maxOutputTokens",
|
|
50
50
|
openrouter: "maxCompletionTokens",
|
|
51
|
-
llmgateway: "max_tokens"
|
|
51
|
+
llmgateway: "max_tokens",
|
|
52
|
+
cloudflare: "max_tokens"
|
|
52
53
|
};
|
|
53
54
|
/**
|
|
54
55
|
* Every flat key any supported provider uses to cap output tokens (plus the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-stream-summarize.js","names":[],"sources":["../../../../src/activities/summarize/chat-stream-summarize.ts"],"sourcesContent":["import { EventType } from '@ag-ui/core'\nimport { toRunErrorPayload } from '../error-payload'\nimport { MAX_TOKENS_KEYS } from '../../utilities/sampling-keys'\nimport { rebuildTokenUsage } from '../../utilities/ag-ui-usage'\nimport type { AdapterYieldChunk } from '../../utilities/adapter-yield-chunk'\nimport { tanstackMetadata } from '../../utilities/merge-metadata'\nimport { normalizeStreamChunk } from '../../utilities/normalize-stream-chunk'\nimport { BaseSummarizeAdapter } from './adapter'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n TextOptions,\n TokenUsage,\n} from '../../types'\n\nfunction consumeSpecSummarizeChunk(\n chunk: StreamChunk,\n state: { summary: string; model: string; usage: TokenUsage },\n): void {\n if (chunk.type === EventType.TEXT_MESSAGE_CONTENT) {\n if (chunk.delta) state.summary += chunk.delta\n return\n }\n\n const tanstack = tanstackMetadata(chunk)\n if (\n (chunk.type === EventType.RUN_STARTED ||\n chunk.type === EventType.RUN_FINISHED ||\n chunk.type === EventType.TEXT_MESSAGE_START) &&\n typeof tanstack?.model === 'string'\n ) {\n state.model = tanstack.model\n }\n\n if (chunk.type === EventType.RUN_FINISHED) {\n const rebuilt = rebuildTokenUsage(chunk.usage, tanstack?.usage)\n if (rebuilt) state.usage = rebuilt\n }\n}\n\nfunction throwRunError(\n chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,\n): never {\n const message =\n typeof chunk.message === 'string' && chunk.message.length > 0\n ? chunk.message\n : 'Summarization failed'\n const err = new Error(message)\n if (typeof chunk.code === 'string') {\n ;(err as Error & { code?: string }).code = chunk.code\n }\n throw err\n}\n\n/**\n * Minimal contract for a text adapter that supports `chatStream`. Lets\n * `ChatStreamSummarizeAdapter` work with any text adapter without coupling\n * to a specific implementation.\n *\n * The provider-options shape is intentionally `any` here — the wrapper only\n * forwards `modelOptions` straight through, so a text adapter with a richer\n * per-model options type (e.g. `ResolveProviderOptions<TModel>`) is still\n * acceptable. Summarize-level type safety is enforced via\n * `SummarizationOptions<TProviderOptions>` on the wrapper itself.\n */\nexport interface ChatStreamCapable {\n chatStream: (options: TextOptions<any>) => AsyncIterable<AdapterYieldChunk>\n}\n\n/**\n * Provider-native max-output-tokens key per summarize-adapter `name`. summarize\n * is provider-agnostic and forwards `modelOptions` opaquely to the wrapped text\n * adapter, so `maxLength` must be written under the exact key the underlying\n * provider reads — no adapter reads a generic `maxTokens`. Ollama is the one\n * exception: it nests sampling under `options`, so it has no entry here and is\n * handled as a special nested case in `applyMaxLength`/`applyDefaultTemperature`.\n *\n * Keep in sync with each adapter's wire mapping:\n * - OpenAI (Responses): `max_output_tokens`\n * - Anthropic / Grok: `max_tokens`\n * - Groq: `max_completion_tokens`\n * - Gemini: `maxOutputTokens`\n * - OpenRouter: `maxCompletionTokens`\n * - LLM Gateway: `max_tokens`\n * - Ollama: nested `options.num_predict` (no entry — see `applyMaxLength`)\n */\nconst MAX_TOKENS_KEY_BY_ADAPTER: Record<string, string> = {\n openai: 'max_output_tokens',\n anthropic: 'max_tokens',\n grok: 'max_tokens',\n groq: 'max_completion_tokens',\n gemini: 'maxOutputTokens',\n openrouter: 'maxCompletionTokens',\n // LLM Gateway exposes an OpenAI-compatible Chat Completions surface whose\n // only output cap is `max_tokens` — it does not read `max_completion_tokens`.\n llmgateway: 'max_tokens',\n}\n\n/**\n * Every flat key any supported provider uses to cap output tokens (plus the\n * generic `maxTokens` spelling no adapter reads). Used to detect a\n * caller-supplied token limit so the summarize default never overrides an\n * explicit caller value. Shared with the OTel middleware via\n * `MAX_TOKENS_KEYS` so the two spelling sets cannot drift.\n */\nconst KNOWN_MAX_TOKENS_KEYS = MAX_TOKENS_KEYS\n\n/**\n * Whether `applyMaxLength` knows how to place a token limit for this adapter\n * `name` (either the nested Ollama shape or a flat provider-native key).\n * Used to surface a warning when `maxLength` would otherwise be silently\n * dropped for an unrecognised adapter name.\n */\nfunction isKnownMaxTokensAdapter(adapterName: string): boolean {\n return (\n adapterName === 'ollama' ||\n MAX_TOKENS_KEY_BY_ADAPTER[adapterName] !== undefined\n )\n}\n\n/**\n * Apply the low-temperature summarize default to a working copy of the\n * caller's `modelOptions`, placed where the wrapped provider actually reads\n * it (nested under `options` for Ollama, flat otherwise). The caller always\n * wins: if they already set `temperature` in that location, it is untouched.\n */\nfunction applyDefaultTemperature(\n adapterName: string,\n temperature: number,\n modelOptions: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...modelOptions }\n\n if (adapterName === 'ollama') {\n const existing =\n merged.options && typeof merged.options === 'object'\n ? (merged.options as Record<string, unknown>)\n : undefined\n if (existing && 'temperature' in existing) return merged\n merged.options = { temperature, ...existing }\n return merged\n }\n\n if ('temperature' in merged) return merged\n merged.temperature = temperature\n return merged\n}\n\n/**\n * Resolve `maxLength` to the provider-native max-output-tokens key for the\n * given summarize-adapter `name` (this wrapper's OWN `name`, not the wrapped\n * text adapter's) and merge it into a working copy of the caller's\n * `modelOptions`. The caller always wins: if they already set any recognised\n * token-limit key (flat or, for Ollama, nested `options.num_predict`), the\n * default is left untouched. Unknown/unrecognised adapter names fall back to\n * NOT setting a token key (the prompt hint still asks the model to stay under\n * `maxLength`) rather than writing a dead key no provider reads.\n *\n * Caveat (intentional): \"caller wins\" keys off ANY recognised spelling in\n * `KNOWN_MAX_TOKENS_KEYS`, but only the adapter's native key is read on the\n * wire. So a caller who sets a NON-native spelling for this provider — e.g.\n * `maxTokens`, or Anthropic's `max_tokens` against an OpenAI adapter — suppresses\n * the summarize default WITHOUT getting their own value applied either: neither\n * cap reaches the wire. This favours never clobbering a migration leftover over\n * guaranteeing a cap; the prompt-level hint still asks the model to stay under\n * `maxLength`. Rename the key to the provider-native spelling to forward it.\n */\nfunction applyMaxLength(\n adapterName: string,\n maxLength: number,\n modelOptions: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...modelOptions }\n\n if (adapterName === 'ollama') {\n // Honor a caller-set limit in either shape: a recognised flat key (e.g.\n // left over from a migration) or the nested `options.num_predict`.\n const callerSetFlatLimit = KNOWN_MAX_TOKENS_KEYS.some(\n (k) => typeof merged[k] === 'number',\n )\n const existing =\n merged.options && typeof merged.options === 'object'\n ? (merged.options as Record<string, unknown>)\n : undefined\n if (\n callerSetFlatLimit ||\n (existing && typeof existing.num_predict === 'number')\n ) {\n return merged\n }\n merged.options = { num_predict: maxLength, ...existing }\n return merged\n }\n\n const key = MAX_TOKENS_KEY_BY_ADAPTER[adapterName]\n if (key === undefined) return merged\n\n const callerSetLimit = KNOWN_MAX_TOKENS_KEYS.some(\n (k) => typeof merged[k] === 'number',\n )\n if (callerSetLimit) return merged\n\n merged[key] = maxLength\n return merged\n}\n\n/**\n * Extract the per-model `modelOptions` type a text adapter accepts. Used by\n * provider summarize factories so their `modelOptions` IntelliSense matches\n * what the underlying text adapter actually understands.\n */\nexport type InferTextProviderOptions<TAdapter> = TAdapter extends {\n '~types': { providerOptions: infer P }\n}\n ? P extends object\n ? P\n : object\n : object\n\n/**\n * Summarize adapter that wraps any `ChatStreamCapable` text adapter and\n * prompts it for summarization. Not tied to any wire format.\n */\nexport class ChatStreamSummarizeAdapter<\n TModel extends string,\n TProviderOptions extends object = Record<string, unknown>,\n> extends BaseSummarizeAdapter<TModel, TProviderOptions> {\n readonly name: string\n\n private readonly textAdapter: ChatStreamCapable\n\n constructor(\n textAdapter: ChatStreamCapable,\n model: TModel,\n name: string = 'chat-stream-summarize',\n ) {\n super({}, model)\n this.name = name\n this.textAdapter = textAdapter\n }\n\n async summarize(\n options: SummarizationOptions<TProviderOptions>,\n ): Promise<SummarizationResult> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n const id = this.generateId()\n const state = {\n summary: '',\n model: options.model,\n usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n }\n\n options.logger.request(\n `activity=summarize provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? 'unset'}`,\n { provider: this.name, model: options.model },\n )\n\n try {\n for await (const raw of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n for (const chunk of normalizeStreamChunk(raw as AdapterYieldChunk)) {\n // Surface failures: the underlying chatStream emits RUN_ERROR instead\n // of throwing, so without this branch summarize() would return an\n // empty summary and pretend a failed run succeeded.\n if (chunk.type === EventType.RUN_ERROR) throwRunError(chunk)\n consumeSpecSummarizeChunk(chunk, state)\n }\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n options.logger.errors(`${this.name}.summarize fatal`, {\n error: toRunErrorPayload(error, `${this.name}.summarize failed`),\n source: `${this.name}.summarize`,\n })\n throw error\n }\n\n return {\n id,\n model: state.model,\n summary: state.summary,\n usage: state.usage,\n }\n }\n\n override async *summarizeStream(\n options: SummarizationOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n options.logger.request(\n `activity=summarizeStream provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? 'unset'}`,\n { provider: this.name, model: options.model },\n )\n\n const id = this.generateId()\n const state = {\n summary: '',\n model: options.model,\n usage: {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n } satisfies SummarizationResult['usage'],\n }\n\n try {\n for await (const raw of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n for (const chunk of normalizeStreamChunk(raw as AdapterYieldChunk)) {\n // Accumulate the same way `summarize()` does so consumers see deltas\n // AND the terminal `generation:result` event below carries the same\n // final summary that non-streaming returns.\n consumeSpecSummarizeChunk(chunk, state)\n\n // Emit the GenerationClient-shaped result event just before the\n // terminal RUN_FINISHED so subscribers (useSummarize) populate\n // `result` before flipping `status` to success.\n if (chunk.type === EventType.RUN_FINISHED) {\n yield {\n type: EventType.CUSTOM,\n name: 'generation:result',\n value: {\n id,\n model: state.model,\n summary: state.summary,\n usage: state.usage,\n } satisfies SummarizationResult,\n timestamp: Date.now(),\n }\n }\n\n yield chunk\n }\n }\n } catch (error: unknown) {\n options.logger.errors(`${this.name}.summarizeStream fatal`, {\n error: toRunErrorPayload(error, `${this.name}.summarizeStream failed`),\n source: `${this.name}.summarizeStream`,\n })\n throw error\n }\n }\n\n /**\n * Build the TextOptions passed to the underlying chatStream. Provider\n * `modelOptions` from the summarize call are forwarded as-is so knobs like\n * Anthropic cache headers, Gemini safety settings, or Ollama tuning params\n * still reach the wire layer.\n */\n protected buildTextOptions(\n options: SummarizationOptions<TProviderOptions>,\n systemPrompt: string,\n ): TextOptions<TProviderOptions> {\n // Sampling knobs now live in provider-native `modelOptions`. Apply the\n // low-temperature default where the wrapped provider actually reads it\n // (nested under `options` for Ollama, flat otherwise) so callers can still\n // override it. Resolving the placement from this summarize adapter's OWN\n // `name` keeps the default off the wire correctly per provider — a flat\n // `temperature` would be silently dropped by Ollama while still showing up\n // in OTel.\n let working: Record<string, unknown> = {\n ...(options.modelOptions as Record<string, unknown> | undefined),\n }\n working = applyDefaultTemperature(this.name, 0.3, working)\n // `maxLength` must reach the wire under the provider-native token key (it\n // differs per provider, and no adapter reads a generic `maxTokens`).\n // Resolve it from this summarize adapter's `name` (the constructor arg,\n // not the wrapped text adapter's name), never overriding a caller-supplied\n // token limit.\n if (options.maxLength !== undefined) {\n if (!isKnownMaxTokensAdapter(this.name)) {\n options.logger.warn(\n `summarize: maxLength=${options.maxLength} could not be mapped to a provider token key for adapter name \"${this.name}\" — it was dropped from modelOptions (the prompt still asks the model to stay under it). Construct ChatStreamSummarizeAdapter with a recognised provider name to forward the cap.`,\n { provider: this.name },\n )\n }\n working = applyMaxLength(this.name, options.maxLength, working)\n }\n const modelOptions = working as TProviderOptions\n\n return {\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n systemPrompts: [systemPrompt],\n modelOptions,\n logger: options.logger,\n // Forward the run identity so the wrapped chat stamps it onto RUN_STARTED\n // (chat uses `runId` as its `runIdOverride`). Conditional spreads keep the\n // fields absent when unset, under `exactOptionalPropertyTypes`.\n ...(options.runId !== undefined ? { runId: options.runId } : {}),\n ...(options.threadId !== undefined ? { threadId: options.threadId } : {}),\n }\n }\n\n protected buildSummarizationPrompt(\n options: SummarizationOptions<TProviderOptions>,\n ): string {\n let prompt = 'You are a professional summarizer. '\n\n switch (options.style) {\n case 'bullet-points':\n prompt += 'Provide a summary in bullet point format. '\n break\n case 'paragraph':\n prompt += 'Provide a summary in paragraph format. '\n break\n case 'concise':\n prompt += 'Provide a very concise summary in 1-2 sentences. '\n break\n case undefined:\n prompt += 'Provide a clear and concise summary. '\n break\n default:\n prompt += 'Provide a clear and concise summary. '\n }\n\n if (options.focus && options.focus.length > 0) {\n prompt += `Focus on the following aspects: ${options.focus.join(', ')}. `\n }\n\n if (options.maxLength) {\n prompt += `Keep the summary under ${options.maxLength} tokens. `\n }\n\n return prompt\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chat-stream-summarize.js","names":[],"sources":["../../../../src/activities/summarize/chat-stream-summarize.ts"],"sourcesContent":["import { EventType } from '@ag-ui/core'\nimport { toRunErrorPayload } from '../error-payload'\nimport { MAX_TOKENS_KEYS } from '../../utilities/sampling-keys'\nimport { rebuildTokenUsage } from '../../utilities/ag-ui-usage'\nimport type { AdapterYieldChunk } from '../../utilities/adapter-yield-chunk'\nimport { tanstackMetadata } from '../../utilities/merge-metadata'\nimport { normalizeStreamChunk } from '../../utilities/normalize-stream-chunk'\nimport { BaseSummarizeAdapter } from './adapter'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n TextOptions,\n TokenUsage,\n} from '../../types'\n\nfunction consumeSpecSummarizeChunk(\n chunk: StreamChunk,\n state: { summary: string; model: string; usage: TokenUsage },\n): void {\n if (chunk.type === EventType.TEXT_MESSAGE_CONTENT) {\n if (chunk.delta) state.summary += chunk.delta\n return\n }\n\n const tanstack = tanstackMetadata(chunk)\n if (\n (chunk.type === EventType.RUN_STARTED ||\n chunk.type === EventType.RUN_FINISHED ||\n chunk.type === EventType.TEXT_MESSAGE_START) &&\n typeof tanstack?.model === 'string'\n ) {\n state.model = tanstack.model\n }\n\n if (chunk.type === EventType.RUN_FINISHED) {\n const rebuilt = rebuildTokenUsage(chunk.usage, tanstack?.usage)\n if (rebuilt) state.usage = rebuilt\n }\n}\n\nfunction throwRunError(\n chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,\n): never {\n const message =\n typeof chunk.message === 'string' && chunk.message.length > 0\n ? chunk.message\n : 'Summarization failed'\n const err = new Error(message)\n if (typeof chunk.code === 'string') {\n ;(err as Error & { code?: string }).code = chunk.code\n }\n throw err\n}\n\n/**\n * Minimal contract for a text adapter that supports `chatStream`. Lets\n * `ChatStreamSummarizeAdapter` work with any text adapter without coupling\n * to a specific implementation.\n *\n * The provider-options shape is intentionally `any` here — the wrapper only\n * forwards `modelOptions` straight through, so a text adapter with a richer\n * per-model options type (e.g. `ResolveProviderOptions<TModel>`) is still\n * acceptable. Summarize-level type safety is enforced via\n * `SummarizationOptions<TProviderOptions>` on the wrapper itself.\n */\nexport interface ChatStreamCapable {\n chatStream: (options: TextOptions<any>) => AsyncIterable<AdapterYieldChunk>\n}\n\n/**\n * Provider-native max-output-tokens key per summarize-adapter `name`. summarize\n * is provider-agnostic and forwards `modelOptions` opaquely to the wrapped text\n * adapter, so `maxLength` must be written under the exact key the underlying\n * provider reads — no adapter reads a generic `maxTokens`. Ollama is the one\n * exception: it nests sampling under `options`, so it has no entry here and is\n * handled as a special nested case in `applyMaxLength`/`applyDefaultTemperature`.\n *\n * Keep in sync with each adapter's wire mapping:\n * - OpenAI (Responses): `max_output_tokens`\n * - Anthropic / Grok: `max_tokens`\n * - Groq: `max_completion_tokens`\n * - Gemini: `maxOutputTokens`\n * - OpenRouter: `maxCompletionTokens`\n * - LLM Gateway: `max_tokens`\n * - Ollama: nested `options.num_predict` (no entry — see `applyMaxLength`)\n */\nconst MAX_TOKENS_KEY_BY_ADAPTER: Record<string, string> = {\n openai: 'max_output_tokens',\n anthropic: 'max_tokens',\n grok: 'max_tokens',\n groq: 'max_completion_tokens',\n gemini: 'maxOutputTokens',\n openrouter: 'maxCompletionTokens',\n // LLM Gateway exposes an OpenAI-compatible Chat Completions surface whose\n // only output cap is `max_tokens` — it does not read `max_completion_tokens`.\n llmgateway: 'max_tokens',\n // Workers AI's OpenAI-compatible Chat Completions surface reads `max_tokens`.\n cloudflare: 'max_tokens',\n}\n\n/**\n * Every flat key any supported provider uses to cap output tokens (plus the\n * generic `maxTokens` spelling no adapter reads). Used to detect a\n * caller-supplied token limit so the summarize default never overrides an\n * explicit caller value. Shared with the OTel middleware via\n * `MAX_TOKENS_KEYS` so the two spelling sets cannot drift.\n */\nconst KNOWN_MAX_TOKENS_KEYS = MAX_TOKENS_KEYS\n\n/**\n * Whether `applyMaxLength` knows how to place a token limit for this adapter\n * `name` (either the nested Ollama shape or a flat provider-native key).\n * Used to surface a warning when `maxLength` would otherwise be silently\n * dropped for an unrecognised adapter name.\n */\nfunction isKnownMaxTokensAdapter(adapterName: string): boolean {\n return (\n adapterName === 'ollama' ||\n MAX_TOKENS_KEY_BY_ADAPTER[adapterName] !== undefined\n )\n}\n\n/**\n * Apply the low-temperature summarize default to a working copy of the\n * caller's `modelOptions`, placed where the wrapped provider actually reads\n * it (nested under `options` for Ollama, flat otherwise). The caller always\n * wins: if they already set `temperature` in that location, it is untouched.\n */\nfunction applyDefaultTemperature(\n adapterName: string,\n temperature: number,\n modelOptions: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...modelOptions }\n\n if (adapterName === 'ollama') {\n const existing =\n merged.options && typeof merged.options === 'object'\n ? (merged.options as Record<string, unknown>)\n : undefined\n if (existing && 'temperature' in existing) return merged\n merged.options = { temperature, ...existing }\n return merged\n }\n\n if ('temperature' in merged) return merged\n merged.temperature = temperature\n return merged\n}\n\n/**\n * Resolve `maxLength` to the provider-native max-output-tokens key for the\n * given summarize-adapter `name` (this wrapper's OWN `name`, not the wrapped\n * text adapter's) and merge it into a working copy of the caller's\n * `modelOptions`. The caller always wins: if they already set any recognised\n * token-limit key (flat or, for Ollama, nested `options.num_predict`), the\n * default is left untouched. Unknown/unrecognised adapter names fall back to\n * NOT setting a token key (the prompt hint still asks the model to stay under\n * `maxLength`) rather than writing a dead key no provider reads.\n *\n * Caveat (intentional): \"caller wins\" keys off ANY recognised spelling in\n * `KNOWN_MAX_TOKENS_KEYS`, but only the adapter's native key is read on the\n * wire. So a caller who sets a NON-native spelling for this provider — e.g.\n * `maxTokens`, or Anthropic's `max_tokens` against an OpenAI adapter — suppresses\n * the summarize default WITHOUT getting their own value applied either: neither\n * cap reaches the wire. This favours never clobbering a migration leftover over\n * guaranteeing a cap; the prompt-level hint still asks the model to stay under\n * `maxLength`. Rename the key to the provider-native spelling to forward it.\n */\nfunction applyMaxLength(\n adapterName: string,\n maxLength: number,\n modelOptions: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...modelOptions }\n\n if (adapterName === 'ollama') {\n // Honor a caller-set limit in either shape: a recognised flat key (e.g.\n // left over from a migration) or the nested `options.num_predict`.\n const callerSetFlatLimit = KNOWN_MAX_TOKENS_KEYS.some(\n (k) => typeof merged[k] === 'number',\n )\n const existing =\n merged.options && typeof merged.options === 'object'\n ? (merged.options as Record<string, unknown>)\n : undefined\n if (\n callerSetFlatLimit ||\n (existing && typeof existing.num_predict === 'number')\n ) {\n return merged\n }\n merged.options = { num_predict: maxLength, ...existing }\n return merged\n }\n\n const key = MAX_TOKENS_KEY_BY_ADAPTER[adapterName]\n if (key === undefined) return merged\n\n const callerSetLimit = KNOWN_MAX_TOKENS_KEYS.some(\n (k) => typeof merged[k] === 'number',\n )\n if (callerSetLimit) return merged\n\n merged[key] = maxLength\n return merged\n}\n\n/**\n * Extract the per-model `modelOptions` type a text adapter accepts. Used by\n * provider summarize factories so their `modelOptions` IntelliSense matches\n * what the underlying text adapter actually understands.\n */\nexport type InferTextProviderOptions<TAdapter> = TAdapter extends {\n '~types': { providerOptions: infer P }\n}\n ? P extends object\n ? P\n : object\n : object\n\n/**\n * Summarize adapter that wraps any `ChatStreamCapable` text adapter and\n * prompts it for summarization. Not tied to any wire format.\n */\nexport class ChatStreamSummarizeAdapter<\n TModel extends string,\n TProviderOptions extends object = Record<string, unknown>,\n> extends BaseSummarizeAdapter<TModel, TProviderOptions> {\n readonly name: string\n\n private readonly textAdapter: ChatStreamCapable\n\n constructor(\n textAdapter: ChatStreamCapable,\n model: TModel,\n name: string = 'chat-stream-summarize',\n ) {\n super({}, model)\n this.name = name\n this.textAdapter = textAdapter\n }\n\n async summarize(\n options: SummarizationOptions<TProviderOptions>,\n ): Promise<SummarizationResult> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n const id = this.generateId()\n const state = {\n summary: '',\n model: options.model,\n usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n }\n\n options.logger.request(\n `activity=summarize provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? 'unset'}`,\n { provider: this.name, model: options.model },\n )\n\n try {\n for await (const raw of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n for (const chunk of normalizeStreamChunk(raw as AdapterYieldChunk)) {\n // Surface failures: the underlying chatStream emits RUN_ERROR instead\n // of throwing, so without this branch summarize() would return an\n // empty summary and pretend a failed run succeeded.\n if (chunk.type === EventType.RUN_ERROR) throwRunError(chunk)\n consumeSpecSummarizeChunk(chunk, state)\n }\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n options.logger.errors(`${this.name}.summarize fatal`, {\n error: toRunErrorPayload(error, `${this.name}.summarize failed`),\n source: `${this.name}.summarize`,\n })\n throw error\n }\n\n return {\n id,\n model: state.model,\n summary: state.summary,\n usage: state.usage,\n }\n }\n\n override async *summarizeStream(\n options: SummarizationOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n options.logger.request(\n `activity=summarizeStream provider=${this.name} model=${options.model} text-length=${options.text.length} maxLength=${options.maxLength ?? 'unset'}`,\n { provider: this.name, model: options.model },\n )\n\n const id = this.generateId()\n const state = {\n summary: '',\n model: options.model,\n usage: {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n } satisfies SummarizationResult['usage'],\n }\n\n try {\n for await (const raw of this.textAdapter.chatStream(\n this.buildTextOptions(options, systemPrompt),\n )) {\n for (const chunk of normalizeStreamChunk(raw as AdapterYieldChunk)) {\n // Accumulate the same way `summarize()` does so consumers see deltas\n // AND the terminal `generation:result` event below carries the same\n // final summary that non-streaming returns.\n consumeSpecSummarizeChunk(chunk, state)\n\n // Emit the GenerationClient-shaped result event just before the\n // terminal RUN_FINISHED so subscribers (useSummarize) populate\n // `result` before flipping `status` to success.\n if (chunk.type === EventType.RUN_FINISHED) {\n yield {\n type: EventType.CUSTOM,\n name: 'generation:result',\n value: {\n id,\n model: state.model,\n summary: state.summary,\n usage: state.usage,\n } satisfies SummarizationResult,\n timestamp: Date.now(),\n }\n }\n\n yield chunk\n }\n }\n } catch (error: unknown) {\n options.logger.errors(`${this.name}.summarizeStream fatal`, {\n error: toRunErrorPayload(error, `${this.name}.summarizeStream failed`),\n source: `${this.name}.summarizeStream`,\n })\n throw error\n }\n }\n\n /**\n * Build the TextOptions passed to the underlying chatStream. Provider\n * `modelOptions` from the summarize call are forwarded as-is so knobs like\n * Anthropic cache headers, Gemini safety settings, or Ollama tuning params\n * still reach the wire layer.\n */\n protected buildTextOptions(\n options: SummarizationOptions<TProviderOptions>,\n systemPrompt: string,\n ): TextOptions<TProviderOptions> {\n // Sampling knobs now live in provider-native `modelOptions`. Apply the\n // low-temperature default where the wrapped provider actually reads it\n // (nested under `options` for Ollama, flat otherwise) so callers can still\n // override it. Resolving the placement from this summarize adapter's OWN\n // `name` keeps the default off the wire correctly per provider — a flat\n // `temperature` would be silently dropped by Ollama while still showing up\n // in OTel.\n let working: Record<string, unknown> = {\n ...(options.modelOptions as Record<string, unknown> | undefined),\n }\n working = applyDefaultTemperature(this.name, 0.3, working)\n // `maxLength` must reach the wire under the provider-native token key (it\n // differs per provider, and no adapter reads a generic `maxTokens`).\n // Resolve it from this summarize adapter's `name` (the constructor arg,\n // not the wrapped text adapter's name), never overriding a caller-supplied\n // token limit.\n if (options.maxLength !== undefined) {\n if (!isKnownMaxTokensAdapter(this.name)) {\n options.logger.warn(\n `summarize: maxLength=${options.maxLength} could not be mapped to a provider token key for adapter name \"${this.name}\" — it was dropped from modelOptions (the prompt still asks the model to stay under it). Construct ChatStreamSummarizeAdapter with a recognised provider name to forward the cap.`,\n { provider: this.name },\n )\n }\n working = applyMaxLength(this.name, options.maxLength, working)\n }\n const modelOptions = working as TProviderOptions\n\n return {\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n systemPrompts: [systemPrompt],\n modelOptions,\n logger: options.logger,\n // Forward the run identity so the wrapped chat stamps it onto RUN_STARTED\n // (chat uses `runId` as its `runIdOverride`). Conditional spreads keep the\n // fields absent when unset, under `exactOptionalPropertyTypes`.\n ...(options.runId !== undefined ? { runId: options.runId } : {}),\n ...(options.threadId !== undefined ? { threadId: options.threadId } : {}),\n }\n }\n\n protected buildSummarizationPrompt(\n options: SummarizationOptions<TProviderOptions>,\n ): string {\n let prompt = 'You are a professional summarizer. '\n\n switch (options.style) {\n case 'bullet-points':\n prompt += 'Provide a summary in bullet point format. '\n break\n case 'paragraph':\n prompt += 'Provide a summary in paragraph format. '\n break\n case 'concise':\n prompt += 'Provide a very concise summary in 1-2 sentences. '\n break\n case undefined:\n prompt += 'Provide a clear and concise summary. '\n break\n default:\n prompt += 'Provide a clear and concise summary. '\n }\n\n if (options.focus && options.focus.length > 0) {\n prompt += `Focus on the following aspects: ${options.focus.join(', ')}. `\n }\n\n if (options.maxLength) {\n prompt += `Keep the summary under ${options.maxLength} tokens. `\n }\n\n return prompt\n }\n}\n"],"mappings":"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"}
|
|
@@ -10,6 +10,11 @@ export interface ByokProvider<TId extends string = string> {
|
|
|
10
10
|
* values here. This object is imported on the client.
|
|
11
11
|
*/
|
|
12
12
|
readonly env?: ReadonlyArray<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Other descriptors this credential needs. A send for this provider also
|
|
15
|
+
* carries their headers and prompts for each one that is missing.
|
|
16
|
+
*/
|
|
17
|
+
readonly with?: ReadonlyArray<ByokProvider>;
|
|
13
18
|
}
|
|
14
19
|
/**
|
|
15
20
|
* Input for {@link defineByokProvider}. `id` cannot be optional: if `TId`
|
|
@@ -19,5 +24,6 @@ export type ByokProviderInit<TId extends string> = {
|
|
|
19
24
|
readonly id: undefined extends TId ? never : TId;
|
|
20
25
|
readonly label: string;
|
|
21
26
|
readonly env?: string | ReadonlyArray<string>;
|
|
27
|
+
readonly with?: ReadonlyArray<ByokProvider>;
|
|
22
28
|
};
|
|
23
29
|
export declare function defineByokProvider<const TId extends string>(provider: ByokProviderInit<TId>): ByokProvider<TId>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-provider.js","names":[],"sources":["../../../src/byok/define-provider.ts"],"sourcesContent":["import { isProviderId } from './providers'\n\n/**\n * A BYOK provider declared by an adapter. `id` is the `x-byok-<id>` slug and\n * is required — `{ id?: string }` is not a {@link ByokProvider}.\n */\nexport interface ByokProvider<TId extends string = string> {\n readonly id: TId\n readonly label: string\n /**\n * Env var names the relay may read. Names only — never put `process.env`\n * values here. This object is imported on the client.\n */\n readonly env?: ReadonlyArray<string>\n}\n\n/**\n * Input for {@link defineByokProvider}. `id` cannot be optional: if `TId`\n * includes `undefined`, `id` becomes `never` and the object is unassignable.\n */\nexport type ByokProviderInit<TId extends string> = {\n readonly id: undefined extends TId ? never : TId\n readonly label: string\n readonly env?: string | ReadonlyArray<string>\n}\n\nfunction normalizeEnv(\n env: string | ReadonlyArray<string> | undefined,\n): ReadonlyArray<string> | undefined {\n if (env === undefined) return undefined\n return typeof env === 'string' ? [env] : env\n}\n\nexport function defineByokProvider<const TId extends string>(\n provider: ByokProviderInit<TId>,\n): ByokProvider<TId> {\n if (!isProviderId(provider.id)) {\n throw new Error(`Invalid BYOK provider id: ${String(provider.id)}`)\n }\n const env = normalizeEnv(provider.env)\n return {\n id: provider.id,\n label: provider.label,\n ...(env ? { env } : {}),\n }\n}\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"define-provider.js","names":[],"sources":["../../../src/byok/define-provider.ts"],"sourcesContent":["import { isProviderId } from './providers'\n\n/**\n * A BYOK provider declared by an adapter. `id` is the `x-byok-<id>` slug and\n * is required — `{ id?: string }` is not a {@link ByokProvider}.\n */\nexport interface ByokProvider<TId extends string = string> {\n readonly id: TId\n readonly label: string\n /**\n * Env var names the relay may read. Names only — never put `process.env`\n * values here. This object is imported on the client.\n */\n readonly env?: ReadonlyArray<string>\n /**\n * Other descriptors this credential needs. A send for this provider also\n * carries their headers and prompts for each one that is missing.\n */\n readonly with?: ReadonlyArray<ByokProvider>\n}\n\n/**\n * Input for {@link defineByokProvider}. `id` cannot be optional: if `TId`\n * includes `undefined`, `id` becomes `never` and the object is unassignable.\n */\nexport type ByokProviderInit<TId extends string> = {\n readonly id: undefined extends TId ? never : TId\n readonly label: string\n readonly env?: string | ReadonlyArray<string>\n readonly with?: ReadonlyArray<ByokProvider>\n}\n\nfunction normalizeEnv(\n env: string | ReadonlyArray<string> | undefined,\n): ReadonlyArray<string> | undefined {\n if (env === undefined) return undefined\n return typeof env === 'string' ? [env] : env\n}\n\nexport function defineByokProvider<const TId extends string>(\n provider: ByokProviderInit<TId>,\n): ByokProvider<TId> {\n if (!isProviderId(provider.id)) {\n throw new Error(`Invalid BYOK provider id: ${String(provider.id)}`)\n }\n const env = normalizeEnv(provider.env)\n return {\n id: provider.id,\n label: provider.label,\n ...(env ? { env } : {}),\n ...(provider.with ? { with: provider.with } : {}),\n }\n}\n"],"mappings":";;AAgCA,SAAS,aACP,KACmC;CACnC,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAA;CAC9B,OAAO,OAAO,QAAQ,WAAW,CAAC,GAAG,IAAI;AAC3C;AAEA,SAAgB,mBACd,UACmB;CACnB,IAAI,CAAC,aAAa,SAAS,EAAE,GAC3B,MAAM,IAAI,MAAM,6BAA6B,OAAO,SAAS,EAAE,GAAG;CAEpE,MAAM,MAAM,aAAa,SAAS,GAAG;CACrC,OAAO;EACL,IAAI,SAAS;EACb,OAAO,SAAS;EAChB,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;EACrB,GAAI,SAAS,OAAO,EAAE,MAAM,SAAS,KAAK,IAAI,CAAC;CACjD;AACF"}
|
|
@@ -8,3 +8,10 @@ import { ProviderId } from './providers.js';
|
|
|
8
8
|
* A slug is header-only.
|
|
9
9
|
*/
|
|
10
10
|
export declare function getByokKey(request: Request, provider: ProviderId | ByokProvider): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Read several keys at once, one per name. Same rules as {@link getByokKey}
|
|
13
|
+
* for each entry. Use it for a credential made of more than one value.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getByokKeys<const TProviders extends Record<string, ProviderId | ByokProvider>>(request: Request, providers: TProviders): {
|
|
16
|
+
[K in keyof TProviders]: string | null;
|
|
17
|
+
};
|
package/dist/esm/byok/get-key.js
CHANGED
|
@@ -20,7 +20,14 @@ function getByokKey(request, provider) {
|
|
|
20
20
|
}
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Read several keys at once, one per name. Same rules as {@link getByokKey}
|
|
25
|
+
* for each entry. Use it for a credential made of more than one value.
|
|
26
|
+
*/
|
|
27
|
+
function getByokKeys(request, providers) {
|
|
28
|
+
return Object.fromEntries(Object.entries(providers).map(([name, provider]) => [name, getByokKey(request, provider)]));
|
|
29
|
+
}
|
|
23
30
|
//#endregion
|
|
24
|
-
export { getByokKey };
|
|
31
|
+
export { getByokKey, getByokKeys };
|
|
25
32
|
|
|
26
33
|
//# sourceMappingURL=get-key.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-key.js","names":[],"sources":["../../../src/byok/get-key.ts"],"sourcesContent":["import { byokHeaderName, resolveProviderId } from './providers'\nimport type { ByokProvider } from './define-provider'\nimport type { ProviderId } from './providers'\n\n/**\n * Read a key on the relay. Import from `@tanstack/ai/byok/server` so this\n * `process.env` access is not in the client graph.\n *\n * The header wins. A {@link ByokProvider} then tries `provider.env` in order.\n * A slug is header-only.\n */\nexport function getByokKey(\n request: Request,\n provider: ProviderId | ByokProvider,\n): string | null {\n const value = request.headers.get(byokHeaderName(resolveProviderId(provider)))\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed.length > 0) return trimmed\n }\n if (typeof provider === 'string') return null\n for (const name of provider.env ?? []) {\n const envValue = process.env[name]\n if (typeof envValue === 'string' && envValue.length > 0) return envValue\n }\n return null\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,WACd,SACA,UACe;CACf,MAAM,QAAQ,QAAQ,QAAQ,IAAI,eAAe,kBAAkB,QAAQ,CAAC,CAAC;CAC7E,IAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,UAAU,MAAM,KAAK;EAC3B,IAAI,QAAQ,SAAS,GAAG,OAAO;CACjC;CACA,IAAI,OAAO,aAAa,UAAU,OAAO;CACzC,KAAK,MAAM,QAAQ,SAAS,OAAO,CAAC,GAAG;EACrC,MAAM,WAAW,QAAQ,IAAI;EAC7B,IAAI,OAAO,aAAa,YAAY,SAAS,SAAS,GAAG,OAAO;CAClE;CACA,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"get-key.js","names":[],"sources":["../../../src/byok/get-key.ts"],"sourcesContent":["import { byokHeaderName, resolveProviderId } from './providers'\nimport type { ByokProvider } from './define-provider'\nimport type { ProviderId } from './providers'\n\n/**\n * Read a key on the relay. Import from `@tanstack/ai/byok/server` so this\n * `process.env` access is not in the client graph.\n *\n * The header wins. A {@link ByokProvider} then tries `provider.env` in order.\n * A slug is header-only.\n */\nexport function getByokKey(\n request: Request,\n provider: ProviderId | ByokProvider,\n): string | null {\n const value = request.headers.get(byokHeaderName(resolveProviderId(provider)))\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed.length > 0) return trimmed\n }\n if (typeof provider === 'string') return null\n for (const name of provider.env ?? []) {\n const envValue = process.env[name]\n if (typeof envValue === 'string' && envValue.length > 0) return envValue\n }\n return null\n}\n\n/**\n * Read several keys at once, one per name. Same rules as {@link getByokKey}\n * for each entry. Use it for a credential made of more than one value.\n */\nexport function getByokKeys<\n const TProviders extends Record<string, ProviderId | ByokProvider>,\n>(\n request: Request,\n providers: TProviders,\n): { [K in keyof TProviders]: string | null } {\n return Object.fromEntries(\n Object.entries(providers).map(([name, provider]) => [\n name,\n getByokKey(request, provider),\n ]),\n ) as { [K in keyof TProviders]: string | null }\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,WACd,SACA,UACe;CACf,MAAM,QAAQ,QAAQ,QAAQ,IAAI,eAAe,kBAAkB,QAAQ,CAAC,CAAC;CAC7E,IAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,UAAU,MAAM,KAAK;EAC3B,IAAI,QAAQ,SAAS,GAAG,OAAO;CACjC;CACA,IAAI,OAAO,aAAa,UAAU,OAAO;CACzC,KAAK,MAAM,QAAQ,SAAS,OAAO,CAAC,GAAG;EACrC,MAAM,WAAW,QAAQ,IAAI;EAC7B,IAAI,OAAO,aAAa,YAAY,SAAS,SAAS,GAAG,OAAO;CAClE;CACA,OAAO;AACT;;;;;AAMA,SAAgB,YAGd,SACA,WAC4C;CAC5C,OAAO,OAAO,YACZ,OAAO,QAAQ,SAAS,CAAC,CAAC,KAAK,CAAC,MAAM,cAAc,CAClD,MACA,WAAW,SAAS,QAAQ,CAC9B,CAAC,CACH;AACF"}
|
package/dist/esm/byok/server.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { byokMissing, isByokMissingBody } from "./missing.js";
|
|
2
|
-
import { getByokKey } from "./get-key.js";
|
|
3
|
-
export { byokMissing, getByokKey, isByokMissingBody };
|
|
2
|
+
import { getByokKey, getByokKeys } from "./get-key.js";
|
|
3
|
+
export { byokMissing, getByokKey, getByokKeys, isByokMissingBody };
|
package/package.json
CHANGED