@assistant-ui/ai-sdk 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"convertMessage.d.ts","names":[],"sources":["../../src/converters/convertMessage.ts"],"mappings":";;;;;KAyDY,gCACV,4BAA4B;EAC1B,wBAAwB,YAAY;EACpC,qBAAqB,YAAY;EACjC,sBAAsB,YAAY;;EAElC;;cA+VS;;;;;;;;EA9XmB,qBAAA,oCAAA,2CAAA,6CAAA;EAK9B,oBAAA,oCAAA,2CAAA,6CAAA"}
1
+ {"version":3,"file":"convertMessage.d.ts","names":[],"sources":["../../src/converters/convertMessage.ts"],"mappings":";;;;;KAyDY,gCACV,4BAA4B;EAC1B,wBAAwB,YAAY;EACpC,qBAAqB,YAAY;EACjC,sBAAsB,YAAY;;EAElC;;cAmXS;;;;;;;;EAlZmB,qBAAA,oCAAA,2CAAA,6CAAA;EAK9B,oBAAA,oCAAA,2CAAA,6CAAA"}
@@ -76,13 +76,17 @@ function extractMcpAppMetadata(part, cache) {
76
76
  return out;
77
77
  }
78
78
  function getToolApprovalAndInterrupt(part, toolStatus) {
79
- if (part.approval && typeof part.approval.id === "string") {
80
- const { id, approved, reason, isAutomatic } = part.approval;
81
- return { approval: {
79
+ if (part.approval) {
80
+ const { id, prompt, approved, reason, isAutomatic, resolution, display, allowFreeform, options, optionId, text, ...additionalApprovalFields } = part.approval;
81
+ const requestReason = additionalApprovalFields.requestReason;
82
+ if (typeof id === "string") return { approval: {
83
+ ...additionalApprovalFields,
82
84
  id,
85
+ ...typeof prompt === "string" ? { prompt } : typeof requestReason === "string" ? { prompt: requestReason } : {},
83
86
  ...typeof approved === "boolean" && { approved },
84
87
  ...typeof reason === "string" && { reason },
85
- ...isAutomatic === true && { isAutomatic: true }
88
+ ...isAutomatic === true && { isAutomatic: true },
89
+ ...(resolution === "cancelled" || resolution === "expired") && { resolution }
86
90
  } };
87
91
  }
88
92
  if (toolStatus?.type === "interrupt") return { interrupt: toolStatus.payload };
@@ -1 +1 @@
1
- {"version":3,"file":"convertMessage.js","names":["isToolUIPart","isReasoningFileUIPart","isCustomContentUIPart","getToolName","UIMessage","createMessageConverter","unstable_createMessageConverter","useExternalMessageConverter","isMcpAppUri","ReasoningMessagePart","ToolCallMessagePart","TextMessagePart","DataMessagePart","PartProviderMetadata","SourceMessagePart","SourceProviderMetadata","FileMessagePart","ThreadMessageLike","McpAppMetadata","MessagePartStreamStatus","stableStringifyToolArgs","parsePartialJsonObject","ReadonlyJSONObject","unwrapModelContentEnvelope","MessageMetadata","THREAD_METADATA_KEYS","Set","toThreadMetadata","metadata","undefined","result","Record","extra","key","value","Object","entries","has","keys","length","custom","AISDKMessageConverterMetadata","Metadata","toolArgsKeyOrderCache","Map","toolLastInputCache","mcpAppMetadataCache","optimisticMessageId","stripClosingDelimiters","json","replace","MCP_APP_METADATA_CACHE_MAX","extractMcpAppMetadata","part","cache","meta","callProviderMetadata","mcp","app","a","output","outMeta","_meta","ui","uiMeta","resourceUri","Array","isArray","visibility","flat","cacheKey","cached","get","delete","set","out","K","mimeType","filter","v","serverId","size","oldest","next","getToolApprovalAndInterrupt","approval","id","approved","reason","isAutomatic","toolStatus","type","payload","NonNullable","interrupt","MessageContent","Exclude","uiPartStateToStatus","state","convertParts","message","parts","converted","p","role","map","status","text","providerMetadata","toolName","toolCallId","argsKeyOrderCacheKey","rawInput","input","args","modelContent","isError","unwrapped","error","errorText","argsText","toolStatuses","mcpApp","sourceType","sourceId","url","title","data","mediaType","filename","startsWith","name","substring","kind","console","warn","Boolean","seenToolCallIds","add","AISDKMessageConverter","createdAt","Date","content","attachments","idx","isImage","toString","image","contentType","const","timing","messageTiming","isOptimistic"],"sources":["../../src/converters/convertMessage.ts"],"sourcesContent":["import {\n isToolUIPart,\n isReasoningFileUIPart,\n isCustomContentUIPart,\n getToolName,\n type UIMessage,\n} from \"ai\";\nimport {\n createMessageConverter as unstable_createMessageConverter,\n type useExternalMessageConverter,\n} from \"@assistant-ui/core/react\";\nimport {\n isMcpAppUri,\n type ReasoningMessagePart,\n type ToolCallMessagePart,\n type TextMessagePart,\n type DataMessagePart,\n type PartProviderMetadata,\n type SourceMessagePart,\n type SourceProviderMetadata,\n type FileMessagePart,\n type ThreadMessageLike,\n type McpAppMetadata,\n type MessagePartStreamStatus,\n} from \"@assistant-ui/core\";\nimport { stableStringifyToolArgs } from \"@assistant-ui/core/internal\";\nimport {\n parsePartialJsonObject,\n type ReadonlyJSONObject,\n} from \"assistant-stream/utils\";\nimport { unwrapModelContentEnvelope } from \"./modelContentEnvelope\";\n\ntype MessageMetadata = ThreadMessageLike[\"metadata\"];\n\nconst THREAD_METADATA_KEYS = new Set([\n \"unstable_state\",\n \"unstable_annotations\",\n \"unstable_data\",\n \"steps\",\n \"timing\",\n \"submittedFeedback\",\n \"isOptimistic\",\n \"custom\",\n]);\n\nconst toThreadMetadata = (metadata: unknown): MessageMetadata => {\n if (!metadata || typeof metadata !== \"object\") return undefined;\n const result: Record<string, unknown> = {};\n const extra: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(metadata)) {\n (THREAD_METADATA_KEYS.has(key) ? result : extra)[key] = value;\n }\n if (Object.keys(extra).length > 0) {\n result.custom = { ...extra, ...(result.custom as object | undefined) };\n }\n return result as MessageMetadata;\n};\nexport type AISDKMessageConverterMetadata =\n useExternalMessageConverter.Metadata & {\n toolArgsKeyOrderCache?: Map<string, Map<string, string[]>>;\n toolLastInputCache?: Map<string, ReadonlyJSONObject>;\n mcpAppMetadataCache?: Map<string, McpAppMetadata>;\n /** Id of the currently-streaming message, flagged optimistic (#4037). */\n optimisticMessageId?: string | undefined;\n };\n\nfunction stripClosingDelimiters(json: string): string {\n return json.replace(/[}\\]\"]+$/, \"\");\n}\n\nconst MCP_APP_METADATA_CACHE_MAX = 100;\n\nfunction extractMcpAppMetadata(\n part: unknown,\n cache: Map<string, McpAppMetadata> | undefined,\n): McpAppMetadata | undefined {\n if (!part || typeof part !== \"object\") return undefined;\n const meta = (part as { callProviderMetadata?: unknown })\n .callProviderMetadata;\n const mcp =\n meta && typeof meta === \"object\"\n ? (meta as { mcp?: unknown }).mcp\n : undefined;\n const app =\n mcp && typeof mcp === \"object\" ? (mcp as { app?: unknown }).app : undefined;\n let a: Record<string, unknown>;\n if (app && typeof app === \"object\") {\n a = app as Record<string, unknown>;\n } else {\n // MCP-UI tools surface the pointer on result._meta: canonical nested\n // `ui.resourceUri`, or the deprecated flat `\"ui/resourceUri\"` key.\n const output = (part as { output?: unknown }).output;\n const outMeta =\n output && typeof output === \"object\"\n ? (output as { _meta?: unknown })._meta\n : undefined;\n const ui =\n outMeta && typeof outMeta === \"object\"\n ? (outMeta as Record<string, unknown>)[\"ui\"]\n : undefined;\n if (\n ui &&\n typeof ui === \"object\" &&\n typeof (ui as Record<string, unknown>)[\"resourceUri\"] === \"string\" &&\n isMcpAppUri((ui as Record<string, unknown>)[\"resourceUri\"] as string)\n ) {\n // Only the spec'd ui fields cross from the result body; serverId is a\n // routing key and stays transport-derived via callProviderMetadata.\n const uiMeta = ui as Record<string, unknown>;\n a = {\n resourceUri: uiMeta[\"resourceUri\"],\n ...(Array.isArray(uiMeta[\"visibility\"])\n ? { visibility: uiMeta[\"visibility\"] }\n : {}),\n };\n } else {\n const flat =\n outMeta && typeof outMeta === \"object\"\n ? (outMeta as Record<string, unknown>)[\"ui/resourceUri\"]\n : undefined;\n if (typeof flat !== \"string\" || !isMcpAppUri(flat)) return undefined;\n a = { resourceUri: flat };\n }\n }\n if (typeof a[\"resourceUri\"] !== \"string\") return undefined;\n if (!isMcpAppUri(a[\"resourceUri\"])) return undefined;\n const cacheKey = `${typeof a[\"serverId\"] === \"string\" ? a[\"serverId\"] : \"\"} ${a[\"resourceUri\"]}`;\n const cached = cache?.get(cacheKey);\n if (cached) {\n cache!.delete(cacheKey);\n cache!.set(cacheKey, cached);\n return cached;\n }\n const out: { -readonly [K in keyof McpAppMetadata]: McpAppMetadata[K] } = {\n resourceUri: a[\"resourceUri\"],\n };\n if (typeof a[\"mimeType\"] === \"string\") out.mimeType = a[\"mimeType\"];\n if (Array.isArray(a[\"visibility\"])) {\n out.visibility = a[\"visibility\"].filter(\n (v): v is \"model\" | \"app\" => v === \"model\" || v === \"app\",\n );\n }\n if (typeof a[\"serverId\"] === \"string\" && a[\"serverId\"].length > 0)\n out.serverId = a[\"serverId\"];\n if (cache) {\n if (cache.size >= MCP_APP_METADATA_CACHE_MAX) {\n const oldest = cache.keys().next().value;\n if (oldest !== undefined) cache.delete(oldest);\n }\n cache.set(cacheKey, out);\n }\n return out;\n}\n\nfunction getToolApprovalAndInterrupt(\n part: {\n approval?:\n | {\n id: string;\n approved?: boolean;\n reason?: string;\n isAutomatic?: boolean;\n }\n | undefined;\n },\n toolStatus: { type: string; payload?: unknown } | undefined,\n): {\n approval?: NonNullable<ToolCallMessagePart[\"approval\"]>;\n interrupt?: NonNullable<ToolCallMessagePart[\"interrupt\"]>;\n} {\n if (part.approval && typeof part.approval.id === \"string\") {\n const { id, approved, reason, isAutomatic } = part.approval;\n return {\n approval: {\n id,\n ...(typeof approved === \"boolean\" && { approved }),\n ...(typeof reason === \"string\" && { reason }),\n ...(isAutomatic === true && { isAutomatic: true }),\n },\n };\n }\n\n if (toolStatus?.type === \"interrupt\") {\n return {\n interrupt: toolStatus.payload as NonNullable<\n ToolCallMessagePart[\"interrupt\"]\n >,\n };\n }\n\n return {};\n}\n\ntype MessageContent = Exclude<ThreadMessageLike[\"content\"], string>;\n\nconst uiPartStateToStatus = (\n state: \"streaming\" | \"done\" | undefined,\n): MessagePartStreamStatus | undefined => {\n if (state === \"streaming\") return { type: \"running\" };\n if (state === \"done\") return { type: \"complete\" };\n return undefined;\n};\n\nfunction convertParts(\n message: UIMessage,\n metadata: AISDKMessageConverterMetadata,\n): MessageContent {\n if (!message.parts || message.parts.length === 0) {\n return [];\n }\n\n const converted = message.parts\n .filter(\n (p) =>\n p.type !== \"step-start\" &&\n (message.role !== \"user\" || p.type !== \"file\"),\n )\n .map((part) => {\n if (part.type === \"text\") {\n const status = uiPartStateToStatus(part.state);\n return {\n type: \"text\",\n text: part.text,\n ...(status != null ? { status } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata: part.providerMetadata as PartProviderMetadata,\n }\n : undefined),\n } satisfies TextMessagePart;\n }\n\n if (part.type === \"reasoning\") {\n const status = uiPartStateToStatus(part.state);\n return {\n type: \"reasoning\",\n text: part.text,\n ...(status != null ? { status } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata: part.providerMetadata as PartProviderMetadata,\n }\n : undefined),\n } satisfies ReasoningMessagePart;\n }\n\n if (isToolUIPart(part)) {\n const toolName = getToolName(part);\n const toolCallId = part.toolCallId;\n const argsKeyOrderCacheKey = `${message.id}:${toolCallId}`;\n\n const rawInput = part.input as ReadonlyJSONObject | null | undefined;\n let args: ReadonlyJSONObject;\n if (\n rawInput != null &&\n typeof rawInput === \"object\" &&\n !Array.isArray(rawInput)\n ) {\n args = rawInput;\n metadata.toolLastInputCache?.set(argsKeyOrderCacheKey, args);\n } else {\n args = metadata.toolLastInputCache?.get(argsKeyOrderCacheKey) ?? {};\n }\n\n let result: unknown;\n let modelContent: ToolCallMessagePart[\"modelContent\"];\n let isError = false;\n\n if (part.state === \"output-available\") {\n const unwrapped = unwrapModelContentEnvelope(part.output);\n result = unwrapped.result;\n modelContent = unwrapped.modelContent;\n } else if (part.state === \"output-error\") {\n isError = true;\n result = { error: part.errorText };\n } else if (part.state === \"output-denied\") {\n isError = true;\n result = {\n error:\n (part as { approval?: { reason?: string } }).approval?.reason ||\n \"Tool approval denied\",\n };\n }\n\n let argsText = stableStringifyToolArgs(\n metadata.toolArgsKeyOrderCache,\n argsKeyOrderCacheKey,\n args,\n );\n if (part.state === \"input-streaming\") {\n // strip closing delimiters added by the AI SDK's fix-json\n argsText = stripClosingDelimiters(argsText);\n // Re-parse so args carries the partial-JSON meta that marks which\n // field is still mid-arrival, like every argsText-based runtime.\n // The key-order cache appends new keys last, so the trailing field\n // of the stripped text is the streaming frontier.\n args = parsePartialJsonObject(argsText) ?? args;\n } else {\n metadata.toolArgsKeyOrderCache?.delete(argsKeyOrderCacheKey);\n if (\n part.state === \"output-available\" ||\n part.state === \"output-error\" ||\n part.state === \"output-denied\"\n ) {\n metadata.toolLastInputCache?.delete(argsKeyOrderCacheKey);\n }\n }\n\n const toolStatus = metadata.toolStatuses?.[toolCallId];\n const mcpApp = extractMcpAppMetadata(\n part,\n metadata.mcpAppMetadataCache,\n );\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText,\n args,\n result,\n isError,\n ...(modelContent !== undefined && { modelContent }),\n ...(mcpApp && { mcp: { app: mcpApp } }),\n ...(part.callProviderMetadata != null\n ? {\n providerMetadata:\n part.callProviderMetadata as PartProviderMetadata,\n }\n : undefined),\n ...getToolApprovalAndInterrupt(part, toolStatus),\n } satisfies ToolCallMessagePart;\n }\n\n if (part.type === \"source-url\") {\n return {\n type: \"source\",\n sourceType: \"url\",\n id: part.sourceId,\n url: part.url,\n ...(part.title != null ? { title: part.title } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata:\n part.providerMetadata as SourceProviderMetadata,\n }\n : undefined),\n } satisfies SourceMessagePart;\n }\n\n if (part.type === \"file\") {\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mediaType,\n ...(part.filename != null && { filename: part.filename }),\n } satisfies FileMessagePart;\n }\n\n if (part.type === \"source-document\") {\n return {\n type: \"source\",\n sourceType: \"document\",\n id: part.sourceId,\n title: part.title,\n mediaType: part.mediaType,\n ...(part.filename != null ? { filename: part.filename } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata:\n part.providerMetadata as SourceProviderMetadata,\n }\n : undefined),\n } satisfies SourceMessagePart;\n }\n\n if (part.type.startsWith(\"data-\")) {\n return {\n type: \"data\",\n name: part.type.substring(5),\n data: (part as any).data,\n } satisfies DataMessagePart;\n }\n\n if (isReasoningFileUIPart(part)) {\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mediaType,\n } satisfies FileMessagePart;\n }\n\n if (isCustomContentUIPart(part)) {\n return {\n type: \"data\",\n name: part.kind,\n data: part.providerMetadata ?? null,\n } satisfies DataMessagePart;\n }\n\n console.warn(`Unsupported message part type: ${part.type}`);\n return null;\n })\n .filter(Boolean) as MessageContent[number][];\n\n const seenToolCallIds = new Set<string>();\n return converted.filter((part) => {\n if (part.type === \"tool-call\" && part.toolCallId != null) {\n if (seenToolCallIds.has(part.toolCallId)) return false;\n seenToolCallIds.add(part.toolCallId);\n }\n return true;\n });\n}\n\nexport const AISDKMessageConverter = unstable_createMessageConverter(\n (message: UIMessage, metadata: AISDKMessageConverterMetadata) => {\n const createdAt = new Date();\n const content = convertParts(message, metadata);\n\n switch (message.role) {\n case \"user\":\n return {\n role: \"user\",\n id: message.id,\n createdAt,\n content,\n attachments: message.parts\n ?.filter((p) => p.type === \"file\")\n .map((part, idx) => {\n const mediaType = part.mediaType ?? \"unknown/unknown\";\n const isImage = mediaType.startsWith(\"image/\");\n return {\n id: idx.toString(),\n type: isImage ? \"image\" : \"file\",\n name: part.filename ?? \"file\",\n content: [\n isImage\n ? {\n type: \"image\",\n image: part.url,\n filename: part.filename!,\n }\n : {\n type: \"file\",\n filename: part.filename!,\n data: part.url,\n mimeType: mediaType,\n },\n ],\n contentType: mediaType,\n status: { type: \"complete\" as const },\n };\n }),\n metadata: toThreadMetadata(message.metadata),\n };\n\n case \"system\":\n case \"assistant\": {\n const timing = metadata.messageTiming?.[message.id];\n const isOptimistic =\n message.role === \"assistant\" &&\n message.id === metadata.optimisticMessageId;\n return {\n role: message.role,\n id: message.id,\n createdAt,\n content,\n metadata: {\n ...toThreadMetadata(message.metadata),\n ...(timing && { timing }),\n ...(isOptimistic && { isOptimistic: true }),\n },\n };\n }\n\n default:\n console.warn(`Unsupported message role: ${message.role}`);\n return [];\n }\n },\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"convertMessage.js","names":["isToolUIPart","isReasoningFileUIPart","isCustomContentUIPart","getToolName","UIMessage","createMessageConverter","unstable_createMessageConverter","useExternalMessageConverter","isMcpAppUri","ReasoningMessagePart","ToolCallMessagePart","TextMessagePart","DataMessagePart","PartProviderMetadata","SourceMessagePart","SourceProviderMetadata","FileMessagePart","ThreadMessageLike","McpAppMetadata","MessagePartStreamStatus","stableStringifyToolArgs","parsePartialJsonObject","ReadonlyJSONObject","unwrapModelContentEnvelope","MessageMetadata","THREAD_METADATA_KEYS","Set","toThreadMetadata","metadata","undefined","result","Record","extra","key","value","Object","entries","has","keys","length","custom","AISDKMessageConverterMetadata","Metadata","toolArgsKeyOrderCache","Map","toolLastInputCache","mcpAppMetadataCache","optimisticMessageId","stripClosingDelimiters","json","replace","MCP_APP_METADATA_CACHE_MAX","extractMcpAppMetadata","part","cache","meta","callProviderMetadata","mcp","app","a","output","outMeta","_meta","ui","uiMeta","resourceUri","Array","isArray","visibility","flat","cacheKey","cached","get","delete","set","out","K","mimeType","filter","v","serverId","size","oldest","next","getToolApprovalAndInterrupt","approval","toolStatus","type","payload","NonNullable","interrupt","id","prompt","approved","reason","isAutomatic","resolution","display","allowFreeform","options","optionId","text","additionalApprovalFields","requestReason","MessageContent","Exclude","uiPartStateToStatus","state","convertParts","message","parts","converted","p","role","map","status","providerMetadata","toolName","toolCallId","argsKeyOrderCacheKey","rawInput","input","args","modelContent","isError","unwrapped","error","errorText","argsText","toolStatuses","mcpApp","sourceType","sourceId","url","title","data","mediaType","filename","startsWith","name","substring","kind","console","warn","Boolean","seenToolCallIds","add","AISDKMessageConverter","createdAt","Date","content","attachments","idx","isImage","toString","image","contentType","const","timing","messageTiming","isOptimistic"],"sources":["../../src/converters/convertMessage.ts"],"sourcesContent":["import {\n isToolUIPart,\n isReasoningFileUIPart,\n isCustomContentUIPart,\n getToolName,\n type UIMessage,\n} from \"ai\";\nimport {\n createMessageConverter as unstable_createMessageConverter,\n type useExternalMessageConverter,\n} from \"@assistant-ui/core/react\";\nimport {\n isMcpAppUri,\n type ReasoningMessagePart,\n type ToolCallMessagePart,\n type TextMessagePart,\n type DataMessagePart,\n type PartProviderMetadata,\n type SourceMessagePart,\n type SourceProviderMetadata,\n type FileMessagePart,\n type ThreadMessageLike,\n type McpAppMetadata,\n type MessagePartStreamStatus,\n} from \"@assistant-ui/core\";\nimport { stableStringifyToolArgs } from \"@assistant-ui/core/internal\";\nimport {\n parsePartialJsonObject,\n type ReadonlyJSONObject,\n} from \"assistant-stream/utils\";\nimport { unwrapModelContentEnvelope } from \"./modelContentEnvelope\";\n\ntype MessageMetadata = ThreadMessageLike[\"metadata\"];\n\nconst THREAD_METADATA_KEYS = new Set([\n \"unstable_state\",\n \"unstable_annotations\",\n \"unstable_data\",\n \"steps\",\n \"timing\",\n \"submittedFeedback\",\n \"isOptimistic\",\n \"custom\",\n]);\n\nconst toThreadMetadata = (metadata: unknown): MessageMetadata => {\n if (!metadata || typeof metadata !== \"object\") return undefined;\n const result: Record<string, unknown> = {};\n const extra: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(metadata)) {\n (THREAD_METADATA_KEYS.has(key) ? result : extra)[key] = value;\n }\n if (Object.keys(extra).length > 0) {\n result.custom = { ...extra, ...(result.custom as object | undefined) };\n }\n return result as MessageMetadata;\n};\nexport type AISDKMessageConverterMetadata =\n useExternalMessageConverter.Metadata & {\n toolArgsKeyOrderCache?: Map<string, Map<string, string[]>>;\n toolLastInputCache?: Map<string, ReadonlyJSONObject>;\n mcpAppMetadataCache?: Map<string, McpAppMetadata>;\n /** Id of the currently-streaming message, flagged optimistic (#4037). */\n optimisticMessageId?: string | undefined;\n };\n\nfunction stripClosingDelimiters(json: string): string {\n return json.replace(/[}\\]\"]+$/, \"\");\n}\n\nconst MCP_APP_METADATA_CACHE_MAX = 100;\n\nfunction extractMcpAppMetadata(\n part: unknown,\n cache: Map<string, McpAppMetadata> | undefined,\n): McpAppMetadata | undefined {\n if (!part || typeof part !== \"object\") return undefined;\n const meta = (part as { callProviderMetadata?: unknown })\n .callProviderMetadata;\n const mcp =\n meta && typeof meta === \"object\"\n ? (meta as { mcp?: unknown }).mcp\n : undefined;\n const app =\n mcp && typeof mcp === \"object\" ? (mcp as { app?: unknown }).app : undefined;\n let a: Record<string, unknown>;\n if (app && typeof app === \"object\") {\n a = app as Record<string, unknown>;\n } else {\n // MCP-UI tools surface the pointer on result._meta: canonical nested\n // `ui.resourceUri`, or the deprecated flat `\"ui/resourceUri\"` key.\n const output = (part as { output?: unknown }).output;\n const outMeta =\n output && typeof output === \"object\"\n ? (output as { _meta?: unknown })._meta\n : undefined;\n const ui =\n outMeta && typeof outMeta === \"object\"\n ? (outMeta as Record<string, unknown>)[\"ui\"]\n : undefined;\n if (\n ui &&\n typeof ui === \"object\" &&\n typeof (ui as Record<string, unknown>)[\"resourceUri\"] === \"string\" &&\n isMcpAppUri((ui as Record<string, unknown>)[\"resourceUri\"] as string)\n ) {\n // Only the spec'd ui fields cross from the result body; serverId is a\n // routing key and stays transport-derived via callProviderMetadata.\n const uiMeta = ui as Record<string, unknown>;\n a = {\n resourceUri: uiMeta[\"resourceUri\"],\n ...(Array.isArray(uiMeta[\"visibility\"])\n ? { visibility: uiMeta[\"visibility\"] }\n : {}),\n };\n } else {\n const flat =\n outMeta && typeof outMeta === \"object\"\n ? (outMeta as Record<string, unknown>)[\"ui/resourceUri\"]\n : undefined;\n if (typeof flat !== \"string\" || !isMcpAppUri(flat)) return undefined;\n a = { resourceUri: flat };\n }\n }\n if (typeof a[\"resourceUri\"] !== \"string\") return undefined;\n if (!isMcpAppUri(a[\"resourceUri\"])) return undefined;\n const cacheKey = `${typeof a[\"serverId\"] === \"string\" ? a[\"serverId\"] : \"\"} ${a[\"resourceUri\"]}`;\n const cached = cache?.get(cacheKey);\n if (cached) {\n cache!.delete(cacheKey);\n cache!.set(cacheKey, cached);\n return cached;\n }\n const out: { -readonly [K in keyof McpAppMetadata]: McpAppMetadata[K] } = {\n resourceUri: a[\"resourceUri\"],\n };\n if (typeof a[\"mimeType\"] === \"string\") out.mimeType = a[\"mimeType\"];\n if (Array.isArray(a[\"visibility\"])) {\n out.visibility = a[\"visibility\"].filter(\n (v): v is \"model\" | \"app\" => v === \"model\" || v === \"app\",\n );\n }\n if (typeof a[\"serverId\"] === \"string\" && a[\"serverId\"].length > 0)\n out.serverId = a[\"serverId\"];\n if (cache) {\n if (cache.size >= MCP_APP_METADATA_CACHE_MAX) {\n const oldest = cache.keys().next().value;\n if (oldest !== undefined) cache.delete(oldest);\n }\n cache.set(cacheKey, out);\n }\n return out;\n}\n\nfunction getToolApprovalAndInterrupt(\n part: {\n approval?: Record<string, unknown> | undefined;\n },\n toolStatus: { type: string; payload?: unknown } | undefined,\n): {\n approval?: NonNullable<ToolCallMessagePart[\"approval\"]>;\n interrupt?: NonNullable<ToolCallMessagePart[\"interrupt\"]>;\n} {\n if (part.approval) {\n // The AI SDK sends only id, approved and reason back to the server, so a\n // request shape promising any other answer would render controls whose\n // response cannot travel.\n const {\n id,\n prompt,\n approved,\n reason,\n isAutomatic,\n resolution,\n display,\n allowFreeform,\n options,\n optionId,\n text,\n ...additionalApprovalFields\n } = part.approval;\n const requestReason = additionalApprovalFields.requestReason;\n if (typeof id === \"string\")\n return {\n approval: {\n ...additionalApprovalFields,\n id,\n ...(typeof prompt === \"string\"\n ? { prompt }\n : typeof requestReason === \"string\"\n ? { prompt: requestReason }\n : {}),\n ...(typeof approved === \"boolean\" && { approved }),\n ...(typeof reason === \"string\" && { reason }),\n ...(isAutomatic === true && { isAutomatic: true }),\n ...((resolution === \"cancelled\" || resolution === \"expired\") && {\n resolution,\n }),\n } as NonNullable<ToolCallMessagePart[\"approval\"]>,\n };\n }\n\n if (toolStatus?.type === \"interrupt\") {\n return {\n interrupt: toolStatus.payload as NonNullable<\n ToolCallMessagePart[\"interrupt\"]\n >,\n };\n }\n\n return {};\n}\n\ntype MessageContent = Exclude<ThreadMessageLike[\"content\"], string>;\n\nconst uiPartStateToStatus = (\n state: \"streaming\" | \"done\" | undefined,\n): MessagePartStreamStatus | undefined => {\n if (state === \"streaming\") return { type: \"running\" };\n if (state === \"done\") return { type: \"complete\" };\n return undefined;\n};\n\nfunction convertParts(\n message: UIMessage,\n metadata: AISDKMessageConverterMetadata,\n): MessageContent {\n if (!message.parts || message.parts.length === 0) {\n return [];\n }\n\n const converted = message.parts\n .filter(\n (p) =>\n p.type !== \"step-start\" &&\n (message.role !== \"user\" || p.type !== \"file\"),\n )\n .map((part) => {\n if (part.type === \"text\") {\n const status = uiPartStateToStatus(part.state);\n return {\n type: \"text\",\n text: part.text,\n ...(status != null ? { status } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata: part.providerMetadata as PartProviderMetadata,\n }\n : undefined),\n } satisfies TextMessagePart;\n }\n\n if (part.type === \"reasoning\") {\n const status = uiPartStateToStatus(part.state);\n return {\n type: \"reasoning\",\n text: part.text,\n ...(status != null ? { status } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata: part.providerMetadata as PartProviderMetadata,\n }\n : undefined),\n } satisfies ReasoningMessagePart;\n }\n\n if (isToolUIPart(part)) {\n const toolName = getToolName(part);\n const toolCallId = part.toolCallId;\n const argsKeyOrderCacheKey = `${message.id}:${toolCallId}`;\n\n const rawInput = part.input as ReadonlyJSONObject | null | undefined;\n let args: ReadonlyJSONObject;\n if (\n rawInput != null &&\n typeof rawInput === \"object\" &&\n !Array.isArray(rawInput)\n ) {\n args = rawInput;\n metadata.toolLastInputCache?.set(argsKeyOrderCacheKey, args);\n } else {\n args = metadata.toolLastInputCache?.get(argsKeyOrderCacheKey) ?? {};\n }\n\n let result: unknown;\n let modelContent: ToolCallMessagePart[\"modelContent\"];\n let isError = false;\n\n if (part.state === \"output-available\") {\n const unwrapped = unwrapModelContentEnvelope(part.output);\n result = unwrapped.result;\n modelContent = unwrapped.modelContent;\n } else if (part.state === \"output-error\") {\n isError = true;\n result = { error: part.errorText };\n } else if (part.state === \"output-denied\") {\n isError = true;\n result = {\n error:\n (part as { approval?: { reason?: string } }).approval?.reason ||\n \"Tool approval denied\",\n };\n }\n\n let argsText = stableStringifyToolArgs(\n metadata.toolArgsKeyOrderCache,\n argsKeyOrderCacheKey,\n args,\n );\n if (part.state === \"input-streaming\") {\n // strip closing delimiters added by the AI SDK's fix-json\n argsText = stripClosingDelimiters(argsText);\n // Re-parse so args carries the partial-JSON meta that marks which\n // field is still mid-arrival, like every argsText-based runtime.\n // The key-order cache appends new keys last, so the trailing field\n // of the stripped text is the streaming frontier.\n args = parsePartialJsonObject(argsText) ?? args;\n } else {\n metadata.toolArgsKeyOrderCache?.delete(argsKeyOrderCacheKey);\n if (\n part.state === \"output-available\" ||\n part.state === \"output-error\" ||\n part.state === \"output-denied\"\n ) {\n metadata.toolLastInputCache?.delete(argsKeyOrderCacheKey);\n }\n }\n\n const toolStatus = metadata.toolStatuses?.[toolCallId];\n const mcpApp = extractMcpAppMetadata(\n part,\n metadata.mcpAppMetadataCache,\n );\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText,\n args,\n result,\n isError,\n ...(modelContent !== undefined && { modelContent }),\n ...(mcpApp && { mcp: { app: mcpApp } }),\n ...(part.callProviderMetadata != null\n ? {\n providerMetadata:\n part.callProviderMetadata as PartProviderMetadata,\n }\n : undefined),\n ...getToolApprovalAndInterrupt(part, toolStatus),\n } satisfies ToolCallMessagePart;\n }\n\n if (part.type === \"source-url\") {\n return {\n type: \"source\",\n sourceType: \"url\",\n id: part.sourceId,\n url: part.url,\n ...(part.title != null ? { title: part.title } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata:\n part.providerMetadata as SourceProviderMetadata,\n }\n : undefined),\n } satisfies SourceMessagePart;\n }\n\n if (part.type === \"file\") {\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mediaType,\n ...(part.filename != null && { filename: part.filename }),\n } satisfies FileMessagePart;\n }\n\n if (part.type === \"source-document\") {\n return {\n type: \"source\",\n sourceType: \"document\",\n id: part.sourceId,\n title: part.title,\n mediaType: part.mediaType,\n ...(part.filename != null ? { filename: part.filename } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata:\n part.providerMetadata as SourceProviderMetadata,\n }\n : undefined),\n } satisfies SourceMessagePart;\n }\n\n if (part.type.startsWith(\"data-\")) {\n return {\n type: \"data\",\n name: part.type.substring(5),\n data: (part as any).data,\n } satisfies DataMessagePart;\n }\n\n if (isReasoningFileUIPart(part)) {\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mediaType,\n } satisfies FileMessagePart;\n }\n\n if (isCustomContentUIPart(part)) {\n return {\n type: \"data\",\n name: part.kind,\n data: part.providerMetadata ?? null,\n } satisfies DataMessagePart;\n }\n\n console.warn(`Unsupported message part type: ${part.type}`);\n return null;\n })\n .filter(Boolean) as MessageContent[number][];\n\n const seenToolCallIds = new Set<string>();\n return converted.filter((part) => {\n if (part.type === \"tool-call\" && part.toolCallId != null) {\n if (seenToolCallIds.has(part.toolCallId)) return false;\n seenToolCallIds.add(part.toolCallId);\n }\n return true;\n });\n}\n\nexport const AISDKMessageConverter = unstable_createMessageConverter(\n (message: UIMessage, metadata: AISDKMessageConverterMetadata) => {\n const createdAt = new Date();\n const content = convertParts(message, metadata);\n\n switch (message.role) {\n case \"user\":\n return {\n role: \"user\",\n id: message.id,\n createdAt,\n content,\n attachments: message.parts\n ?.filter((p) => p.type === \"file\")\n .map((part, idx) => {\n const mediaType = part.mediaType ?? \"unknown/unknown\";\n const isImage = mediaType.startsWith(\"image/\");\n return {\n id: idx.toString(),\n type: isImage ? \"image\" : \"file\",\n name: part.filename ?? \"file\",\n content: [\n isImage\n ? {\n type: \"image\",\n image: part.url,\n filename: part.filename!,\n }\n : {\n type: \"file\",\n filename: part.filename!,\n data: part.url,\n mimeType: mediaType,\n },\n ],\n contentType: mediaType,\n status: { type: \"complete\" as const },\n };\n }),\n metadata: toThreadMetadata(message.metadata),\n };\n\n case \"system\":\n case \"assistant\": {\n const timing = metadata.messageTiming?.[message.id];\n const isOptimistic =\n message.role === \"assistant\" &&\n message.id === metadata.optimisticMessageId;\n return {\n role: message.role,\n id: message.id,\n createdAt,\n content,\n metadata: {\n ...toThreadMetadata(message.metadata),\n ...(timing && { timing }),\n ...(isOptimistic && { isOptimistic: true }),\n },\n };\n }\n\n default:\n console.warn(`Unsupported message role: ${message.role}`);\n return [];\n }\n },\n);\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useChatRuntime.d.ts","names":[],"sources":["../../src/runtime/useChatRuntime.ts"],"mappings":";;;;;KAYY,sBAAsB,mBAAmB,YAAY,aAC/D,kBAAkB;EAChB,QAAQ;EACR,qBAAqB;;cAmBZ,iBAAkB,mBAAmB,YAAY,aAAW,OAAA,qBAAA,YAItE,sBAAsB,gBAAmB"}
1
+ {"version":3,"file":"useChatRuntime.d.ts","names":[],"sources":["../../src/runtime/useChatRuntime.ts"],"mappings":";;;;;KAYY,sBAAsB,mBAAmB,YAAY,aAC/D,kBAAkB;EAChB,QAAQ;EACR,qBAAqB;;cAoBZ,iBAAkB,mBAAmB,YAAY,aAAW,OAAA,qBAAA,YAItE,sBAAsB,gBAAmB"}
@@ -20,7 +20,8 @@ const useChatThreadRuntime = (options) => {
20
20
  t1 = {
21
21
  id,
22
22
  isMainThread,
23
- getThreadListItem: t0
23
+ getThreadListItem: t0,
24
+ stopOnClientDestroy: true
24
25
  };
25
26
  $[2] = id;
26
27
  $[3] = isMainThread;
@@ -1 +1 @@
1
- {"version":3,"file":"useChatRuntime.js","names":["c","_c","UIMessage","AssistantCloud","AssistantRuntime","useCloudThreadListAdapter","useRemoteThreadListRuntime","useAui","useAuiState","useChatThread","ChatThreadOptions","UseChatRuntimeOptions","UI_MESSAGE","cloud","onThreadIdChange","threadId","useChatThreadRuntime","options","$","id","_temp","isMainThread","_temp2","aui","t0","threadListItem","source","undefined","t1","getThreadListItem","useChatRuntime","cloudAdapter","runtimeHook","RuntimeHook","adapter","allowNesting","s","s_0","threads","mainThreadId"],"sources":["../../src/runtime/useChatRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport type { UIMessage } from \"@ai-sdk/react\";\nimport type { AssistantCloud } from \"assistant-cloud\";\nimport type { AssistantRuntime } from \"@assistant-ui/core\";\nimport {\n useCloudThreadListAdapter,\n useRemoteThreadListRuntime,\n} from \"@assistant-ui/core/react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport { useChatThread, type ChatThreadOptions } from \"./useChatThread\";\n\nexport type UseChatRuntimeOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n ChatThreadOptions<UI_MESSAGE> & {\n cloud?: AssistantCloud | undefined;\n onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;\n };\n\nconst useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(\n options?: ChatThreadOptions<UI_MESSAGE>,\n): AssistantRuntime => {\n const id = useAuiState((s) => s.threadListItem.id);\n const isMainThread = useAuiState(\n (s) => s.threads.mainThreadId === s.threadListItem.id,\n );\n const aui = useAui();\n return useChatThread(options, {\n id,\n isMainThread,\n getThreadListItem: () =>\n aui.threadListItem.source ? aui.threadListItem : undefined,\n });\n};\n\nexport const useChatRuntime = <UI_MESSAGE extends UIMessage = UIMessage>({\n cloud,\n onThreadIdChange,\n ...options\n}: UseChatRuntimeOptions<UI_MESSAGE> = {}): AssistantRuntime => {\n const cloudAdapter = useCloudThreadListAdapter({ cloud });\n return useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n return useChatThreadRuntime(options);\n },\n adapter: cloudAdapter,\n allowNesting: true,\n onThreadIdChange,\n });\n};\n"],"mappings":";;;;;;AAkBA,MAAMgB,wBAAuBC,YAAA;CAAA,MAAAC,IAAAjB,EAAA,CAAA;CAG3B,MAAAkB,KAAWX,YAAYY,KAA0B;CACjD,MAAAC,eAAqBb,YACnBc,MACF;CACA,MAAAC,MAAYhB,OAAO;CAAE,IAAAiB;CAAA,IAAAN,EAAA,OAAAK,KAAA;EAIAC,WACjBD,IAAGE,eAAeC,SAAUH,IAAGE,iBAA/BE,KAAAA;EAA0DT,EAAA,KAAAK;EAAAL,EAAA,KAAAM;CAAA,OAAAA,KAAAN,EAAA;CAAA,IAAAU;CAAA,IAAAV,EAAA,OAAAC,MAAAD,EAAA,OAAAG,gBAAAH,EAAA,OAAAM,IAAA;EAJhCI,KAAA;GAAAT;GAAAE;GAAAQ,mBAGTL;EAErB;EAACN,EAAA,KAAAC;EAAAD,EAAA,KAAAG;EAAAH,EAAA,KAAAM;EAAAN,EAAA,KAAAU;CAAA,OAAAA,KAAAV,EAAA;CAAA,OALMT,cAAcQ,SAASW,EAK7B;AAAC;AAGJ,MAAaE,kBAA4D,EACvEjB,OACAC,kBACA,GAAGG,YACkC,CAAC,MAAwB;CAC9D,MAAMc,eAAe1B,0BAA0B,EAAEQ,MAAM,CAAC;CACxD,OAAOP,2BAA2B;EAChC0B,aAAa,SAASC,cAAc;GAClC,OAAOjB,qBAAqBC,OAAO;EACrC;EACAiB,SAASH;EACTI,cAAc;EACdrB;CACF,CAAC;AACH;AA9B6B,SAAAM,MAAAgB,GAAA;CAAA,OAGGA,EAACX,eAAeN;AAAG;AAHtB,SAAAG,OAAAe,KAAA;CAAA,OAKlBD,IAACE,QAAQC,iBAAkBH,IAACX,eAAeN;AAAG"}
1
+ {"version":3,"file":"useChatRuntime.js","names":["c","_c","UIMessage","AssistantCloud","AssistantRuntime","useCloudThreadListAdapter","useRemoteThreadListRuntime","useAui","useAuiState","useChatThread","ChatThreadOptions","UseChatRuntimeOptions","UI_MESSAGE","cloud","onThreadIdChange","threadId","useChatThreadRuntime","options","$","id","_temp","isMainThread","_temp2","aui","t0","threadListItem","source","undefined","t1","getThreadListItem","stopOnClientDestroy","useChatRuntime","cloudAdapter","runtimeHook","RuntimeHook","adapter","allowNesting","s","s_0","threads","mainThreadId"],"sources":["../../src/runtime/useChatRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport type { UIMessage } from \"@ai-sdk/react\";\nimport type { AssistantCloud } from \"assistant-cloud\";\nimport type { AssistantRuntime } from \"@assistant-ui/core\";\nimport {\n useCloudThreadListAdapter,\n useRemoteThreadListRuntime,\n} from \"@assistant-ui/core/react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport { useChatThread, type ChatThreadOptions } from \"./useChatThread\";\n\nexport type UseChatRuntimeOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n ChatThreadOptions<UI_MESSAGE> & {\n cloud?: AssistantCloud | undefined;\n onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;\n };\n\nconst useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(\n options?: ChatThreadOptions<UI_MESSAGE>,\n): AssistantRuntime => {\n const id = useAuiState((s) => s.threadListItem.id);\n const isMainThread = useAuiState(\n (s) => s.threads.mainThreadId === s.threadListItem.id,\n );\n const aui = useAui();\n return useChatThread(options, {\n id,\n isMainThread,\n getThreadListItem: () =>\n aui.threadListItem.source ? aui.threadListItem : undefined,\n stopOnClientDestroy: true,\n });\n};\n\nexport const useChatRuntime = <UI_MESSAGE extends UIMessage = UIMessage>({\n cloud,\n onThreadIdChange,\n ...options\n}: UseChatRuntimeOptions<UI_MESSAGE> = {}): AssistantRuntime => {\n const cloudAdapter = useCloudThreadListAdapter({ cloud });\n return useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n return useChatThreadRuntime(options);\n },\n adapter: cloudAdapter,\n allowNesting: true,\n onThreadIdChange,\n });\n};\n"],"mappings":";;;;;;AAkBA,MAAMgB,wBAAuBC,YAAA;CAAA,MAAAC,IAAAjB,EAAA,CAAA;CAG3B,MAAAkB,KAAWX,YAAYY,KAA0B;CACjD,MAAAC,eAAqBb,YACnBc,MACF;CACA,MAAAC,MAAYhB,OAAO;CAAE,IAAAiB;CAAA,IAAAN,EAAA,OAAAK,KAAA;EAIAC,WACjBD,IAAGE,eAAeC,SAAUH,IAAGE,iBAA/BE,KAAAA;EAA0DT,EAAA,KAAAK;EAAAL,EAAA,KAAAM;CAAA,OAAAA,KAAAN,EAAA;CAAA,IAAAU;CAAA,IAAAV,EAAA,OAAAC,MAAAD,EAAA,OAAAG,gBAAAH,EAAA,OAAAM,IAAA;EAJhCI,KAAA;GAAAT;GAAAE;GAAAQ,mBAGTL;GACyCM,qBACvC;EACvB;EAACZ,EAAA,KAAAC;EAAAD,EAAA,KAAAG;EAAAH,EAAA,KAAAM;EAAAN,EAAA,KAAAU;CAAA,OAAAA,KAAAV,EAAA;CAAA,OANMT,cAAcQ,SAASW,EAM7B;AAAC;AAGJ,MAAaG,kBAA4D,EACvElB,OACAC,kBACA,GAAGG,YACkC,CAAC,MAAwB;CAC9D,MAAMe,eAAe3B,0BAA0B,EAAEQ,MAAM,CAAC;CACxD,OAAOP,2BAA2B;EAChC2B,aAAa,SAASC,cAAc;GAClC,OAAOlB,qBAAqBC,OAAO;EACrC;EACAkB,SAASH;EACTI,cAAc;EACdtB;CACF,CAAC;AACH;AA/B6B,SAAAM,MAAAiB,GAAA;CAAA,OAGGA,EAACZ,eAAeN;AAAG;AAHtB,SAAAG,OAAAgB,KAAA;CAAA,OAKlBD,IAACE,QAAQC,iBAAkBH,IAACZ,eAAeN;AAAG"}
@@ -1 +1 @@
1
- {"version":3,"file":"useExternalHistory.d.ts","names":[],"sources":["../../src/runtime/useExternalHistory.ts"],"mappings":";;;cAuBa,8BAA+B,UAC1C,mBAAmB,UAAU,eAAe,iBAC5C,UAAU,wBAAwB,cACjC;cA+BU,qBAAsB,UACjC,YAAY,UAAU,mBACtB,gBAAgB,kCAChB,mBAAmB,UAAU,eAAe,iBAC5C,sBAAsB,qBAAqB,gBAC3C,gBAAgB,UAAU;;EAwUN,gBAAA,sBAAM"}
1
+ {"version":3,"file":"useExternalHistory.d.ts","names":[],"sources":["../../src/runtime/useExternalHistory.ts"],"mappings":";;;cAuBa,8BAA+B,UAC1C,mBAAmB,UAAU,eAAe,iBAC5C,UAAU,wBAAwB,cACjC;cA+BU,qBAAsB,UACjC,YAAY,UAAU,mBACtB,gBAAgB,kCAChB,mBAAmB,UAAU,eAAe,iBAC5C,sBAAsB,qBAAqB,gBAC3C,gBAAgB,UAAU;;EA2UN,gBAAA,sBAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"useExternalHistory.js","names":["AssistantRuntime","ThreadHistoryAdapter","ThreadMessage","MessageFormatAdapter","MessageFormatItem","MessageFormatRepository","ExportedMessageRepository","getExternalStoreMessages","MessageRepository","useAui","useRef","useEffect","useState","RefObject","useCallback","useMemo","toExportedMessageRepository","toThreadMessages","messages","TMessage","survivingIds","Set","survivors","flatMap","m","message","console","warn","parentId","has","add","id","headId","isAwaitingToolApproval","status","type","reason","encodeContent","storageFormatAdapter","item","JSON","stringify","encode","useExternalHistory","runtimeRef","historyAdapter","onSetMessages","loadedRef","itemEpoch","setItemEpoch","aui","optionalThreadListItem","threadListItem","source","hasLoaded","setHasLoaded","historyIds","deferredTelemetryIds","persistedInnerMessages","Map","content","onSetMessagesRef","current","formatAdapter","undefined","withFormat","Error","isLoading","loadHistory","repo","load","length","set","getId","converted","thread","import","tempRepo","getMessages","error","pin","remoteId","getState","subscribe","n","threadState","isRunning","runStartRef","persistTimerRef","ReturnType","setTimeout","persistInFlightRef","Promise","resolve","stepBoundariesRef","wasRunningRef","toolCallCountRef","adapter","unsubscribe","wasRunning","lastMsg","at","role","currentToolCallCount","filter","p","push","Date","now","clearTimeout","persistSettled","initialThreadState","ignoreRunning","latest","boundaries","durationMs","lastAssistant","findLast","tcCount","totalSteps","stepDur","i","Math","round","stepTimestamps","map","endMs","start_ms","end_ms","telemetryOptions","then","lastInnerMessageId","getLastInnerId","msgs","toBatchItems","msg","idx","innerMessages","isTerminal","isAwaitingToolCalls","isReady","update","batchItems","innerId","persisted","get","append","delete","reportTelemetry","catch","deleteMessage","messageId","deleteMessages","bind","messageIndex","findIndex","previousInnerMessages","slice","itemsToDelete","deletion"],"sources":["../../src/runtime/useExternalHistory.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n AssistantRuntime,\n ThreadHistoryAdapter,\n ThreadMessage,\n MessageFormatAdapter,\n MessageFormatItem,\n MessageFormatRepository,\n ExportedMessageRepository,\n} from \"@assistant-ui/core\";\nimport { getExternalStoreMessages } from \"@assistant-ui/core\";\nimport { MessageRepository } from \"@assistant-ui/core/internal\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n useRef,\n useEffect,\n useState,\n type RefObject,\n useCallback,\n useMemo,\n} from \"react\";\n\nexport const toExportedMessageRepository = <TMessage>(\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n messages: MessageFormatRepository<TMessage>,\n): ExportedMessageRepository => {\n const survivingIds = new Set<string>();\n const survivors = messages.messages.flatMap((m) => {\n const message = toThreadMessages([m.message])[0];\n if (!message) {\n console.warn(\"Skipping a stored message that could not be loaded.\");\n return [];\n }\n if (m.parentId && !survivingIds.has(m.parentId)) return [];\n survivingIds.add(message.id);\n return [{ ...m, message }];\n });\n\n return {\n headId:\n messages.headId && survivingIds.has(messages.headId)\n ? messages.headId\n : null,\n messages: survivors,\n };\n};\n\nconst isAwaitingToolApproval = (message: ThreadMessage) =>\n message.status?.type === \"requires-action\" &&\n message.status.reason === \"tool-calls\";\n\nconst encodeContent = <TMessage>(\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n item: MessageFormatItem<TMessage>,\n) => JSON.stringify(storageFormatAdapter.encode(item));\n\nexport const useExternalHistory = <TMessage>(\n runtimeRef: RefObject<AssistantRuntime>,\n historyAdapter: ThreadHistoryAdapter | undefined,\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n onSetMessages: (messages: TMessage[]) => void,\n) => {\n const loadedRef = useRef(false);\n const [itemEpoch, setItemEpoch] = useState(0);\n\n const aui = useAui();\n const optionalThreadListItem = useCallback(\n () => (aui.threadListItem.source ? aui.threadListItem : null),\n [aui],\n );\n\n const [hasLoaded, setHasLoaded] = useState(false);\n\n const historyIds = useRef(new Set<string>());\n const deferredTelemetryIds = useRef(new Set<string>());\n // `content` is a snapshot taken at write time rather than a re-encode of `source`, because a retained message object can be mutated in place by the runtime that produced it.\n const persistedInnerMessages = useRef(\n new Map<string, { source: TMessage; content: string }>(),\n );\n\n const onSetMessagesRef = useRef(onSetMessages);\n useEffect(() => {\n onSetMessagesRef.current = onSetMessages;\n });\n\n const formatAdapter = useMemo(() => {\n if (!historyAdapter) return undefined;\n if (!historyAdapter.withFormat) {\n throw new Error(\n \"useAISDKRuntime: ThreadHistoryAdapter is missing the required `withFormat` method.\",\n );\n }\n return historyAdapter.withFormat<TMessage, any>(storageFormatAdapter);\n }, [historyAdapter, storageFormatAdapter]);\n\n const isLoading = formatAdapter != null && !hasLoaded;\n\n useEffect(() => {\n if (!formatAdapter || loadedRef.current) return undefined;\n\n const loadHistory = async () => {\n try {\n const repo = await formatAdapter.load();\n if (repo && repo.messages.length > 0) {\n for (const m of repo.messages) {\n persistedInnerMessages.current.set(\n storageFormatAdapter.getId(m.message),\n {\n source: m.message,\n content: encodeContent(storageFormatAdapter, m),\n },\n );\n }\n const converted = toExportedMessageRepository(toThreadMessages, repo);\n runtimeRef.current.thread.import(converted);\n\n const tempRepo = new MessageRepository();\n tempRepo.import(converted);\n const messages = tempRepo.getMessages();\n\n onSetMessagesRef.current(\n messages.flatMap(getExternalStoreMessages<TMessage>),\n );\n\n historyIds.current = new Set();\n for (const m of converted.messages) {\n historyIds.current.add(m.message.id);\n if (isAwaitingToolApproval(m.message)) {\n deferredTelemetryIds.current.add(m.message.id);\n }\n }\n }\n } catch (error) {\n console.error(\"Failed to load message history:\", error);\n } finally {\n setHasLoaded(true);\n }\n };\n\n formatAdapter.pin?.();\n\n const remoteId = optionalThreadListItem()?.getState().remoteId;\n if (!remoteId) {\n setHasLoaded(true);\n return aui.subscribe(() => {\n if (optionalThreadListItem()?.getState().remoteId) {\n setItemEpoch((n) => n + 1);\n }\n });\n }\n\n const threadState = runtimeRef.current.thread.getState();\n if (threadState.isRunning || threadState.messages.length > 0) {\n loadedRef.current = true;\n setHasLoaded(true);\n return undefined;\n }\n\n loadedRef.current = true;\n void loadHistory();\n return undefined;\n }, [\n formatAdapter,\n toThreadMessages,\n runtimeRef,\n optionalThreadListItem,\n aui,\n itemEpoch,\n storageFormatAdapter,\n ]);\n\n const runStartRef = useRef<number | null>(null);\n const persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const persistInFlightRef = useRef<Promise<void>>(Promise.resolve());\n const stepBoundariesRef = useRef<number[]>([]);\n const wasRunningRef = useRef(false);\n const toolCallCountRef = useRef(0);\n\n useEffect(() => {\n if (!formatAdapter) return;\n const adapter = formatAdapter;\n\n const unsubscribe = runtimeRef.current.thread.subscribe(() => {\n const threadState = runtimeRef.current.thread.getState();\n const { isRunning } = threadState;\n const wasRunning = wasRunningRef.current;\n wasRunningRef.current = isRunning;\n\n // Track step boundaries by content changes (more reliable than isRunning)\n if (runStartRef.current != null) {\n const lastMsg = threadState.messages.at(-1);\n if (lastMsg?.role === \"assistant\") {\n const currentToolCallCount = lastMsg.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n while (toolCallCountRef.current < currentToolCallCount) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n toolCallCountRef.current++;\n }\n }\n }\n\n if (isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n adapter.pin?.();\n }\n // Cancel any pending persist — isRunning went back to true\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n }\n return;\n }\n\n // Only act on the true→false transition\n if (!wasRunning) return;\n\n // Record step boundary offset (synchronous for accuracy)\n if (runStartRef.current != null) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n }\n\n // Debounce: wait one macrotask so agentic step flickers are absorbed\n if (persistTimerRef.current) clearTimeout(persistTimerRef.current);\n persistTimerRef.current = setTimeout(() => {\n persistSettled(false);\n }, 0);\n });\n\n const initialThreadState = runtimeRef.current.thread.getState();\n wasRunningRef.current = initialThreadState.isRunning;\n if (initialThreadState.isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n adapter.pin?.();\n }\n } else if (initialThreadState.messages.length > 0) {\n persistSettled(true);\n }\n\n function persistSettled(ignoreRunning: boolean) {\n persistTimerRef.current = null;\n const latest = runtimeRef.current.thread.getState();\n if (!ignoreRunning && latest.isRunning) return;\n\n const boundaries = stepBoundariesRef.current;\n const durationMs = boundaries.length > 0 ? boundaries.at(-1) : undefined;\n\n // Fallback: if only 1 boundary but message has multiple steps, distribute evenly\n if (boundaries.length === 1 && durationMs != null) {\n const lastAssistant = latest.messages.findLast(\n (m) => m.role === \"assistant\",\n );\n if (lastAssistant) {\n const tcCount = lastAssistant.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n if (tcCount > 0) {\n const totalSteps = tcCount + 1;\n const stepDur = durationMs / totalSteps;\n boundaries.length = 0;\n for (let i = 0; i < totalSteps; i++) {\n boundaries.push(Math.round((i + 1) * stepDur));\n }\n }\n }\n }\n\n // Build per-step timestamps when there are multiple steps\n const stepTimestamps =\n boundaries.length > 1\n ? boundaries.map((endMs, i) => ({\n start_ms: i === 0 ? 0 : boundaries[i - 1]!,\n end_ms: endMs,\n }))\n : undefined;\n\n runStartRef.current = null;\n stepBoundariesRef.current = [];\n\n const telemetryOptions = {\n ...(durationMs != null ? { durationMs } : undefined),\n ...(stepTimestamps != null ? { stepTimestamps } : undefined),\n };\n\n persistInFlightRef.current = persistInFlightRef.current\n .then(async () => {\n const { messages } = latest;\n let lastInnerMessageId: string | null = null;\n\n const getLastInnerId = (msgs: TMessage[]): string | null =>\n msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)!) : null;\n\n const toBatchItems = (msgs: TMessage[]) =>\n msgs.map((msg, idx) => ({\n parentId:\n idx === 0\n ? lastInnerMessageId\n : storageFormatAdapter.getId(msgs[idx - 1]!),\n message: msg,\n }));\n\n for (const message of messages) {\n const innerMessages = getExternalStoreMessages<TMessage>(message);\n\n const isTerminal =\n message.status === undefined ||\n message.status.type === \"complete\" ||\n message.status.type === \"incomplete\";\n const isAwaitingToolCalls = isAwaitingToolApproval(message);\n // A paused message's later content can only reach storage via update, so it is persisted early only when the adapter supports update.\n const isReady =\n isTerminal ||\n (isAwaitingToolCalls && adapter.update !== undefined);\n\n if (!isReady) {\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n\n if (!historyIds.current.has(message.id)) {\n historyIds.current.add(message.id);\n deferredTelemetryIds.current.add(message.id);\n }\n\n const batchItems = toBatchItems(innerMessages);\n for (const item of batchItems) {\n const innerId = storageFormatAdapter.getId(item.message);\n const persisted = persistedInnerMessages.current.get(innerId);\n if (!persisted) {\n await adapter.append(item);\n persistedInnerMessages.current.set(innerId, {\n source: item.message,\n content: encodeContent(storageFormatAdapter, item),\n });\n } else if (\n persisted.source !== item.message &&\n durationMs !== undefined &&\n adapter.update\n ) {\n const content = encodeContent(storageFormatAdapter, item);\n if (content === persisted.content) {\n persisted.source = item.message;\n } else {\n try {\n await adapter.update(item, innerId);\n persistedInnerMessages.current.set(innerId, {\n source: item.message,\n content,\n });\n } catch {\n // A failed update leaves the stale baseline behind so the next run stop retries it.\n }\n }\n }\n }\n\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n\n if (deferredTelemetryIds.current.has(message.id) && isTerminal) {\n deferredTelemetryIds.current.delete(message.id);\n adapter.reportTelemetry?.(batchItems, telemetryOptions);\n }\n }\n })\n .catch((error) => {\n console.error(\"Failed to persist message history:\", error);\n });\n }\n\n return () => {\n unsubscribe();\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n persistSettled(false);\n }\n };\n }, [formatAdapter, storageFormatAdapter, runtimeRef]);\n\n const deleteMessage = useCallback(\n async (messageId: string) => {\n const deleteMessages = formatAdapter?.delete?.bind(formatAdapter);\n if (!deleteMessages) return;\n\n const messages = runtimeRef.current.thread.getState().messages;\n const messageIndex = messages.findIndex((m) => m.id === messageId);\n if (messageIndex === -1) return;\n\n const previousInnerMessages = messages\n .slice(0, messageIndex)\n .flatMap(getExternalStoreMessages<TMessage>);\n let parentId = previousInnerMessages.at(-1)\n ? storageFormatAdapter.getId(previousInnerMessages.at(-1)!)\n : null;\n const itemsToDelete = getExternalStoreMessages<TMessage>(\n messages[messageIndex]!,\n ).map((message) => {\n const item = { parentId, message };\n parentId = storageFormatAdapter.getId(message);\n return item;\n });\n\n const deletion = persistInFlightRef.current.then(async () => {\n await deleteMessages(itemsToDelete);\n\n historyIds.current.delete(messageId);\n deferredTelemetryIds.current.delete(messageId);\n for (const item of itemsToDelete) {\n persistedInnerMessages.current.delete(\n storageFormatAdapter.getId(item.message),\n );\n }\n });\n\n persistInFlightRef.current = deletion.catch(() => {});\n await deletion;\n },\n [formatAdapter, runtimeRef, storageFormatAdapter],\n );\n\n return { isLoading, deleteMessage };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"useExternalHistory.js","names":["AssistantRuntime","ThreadHistoryAdapter","ThreadMessage","MessageFormatAdapter","MessageFormatItem","MessageFormatRepository","ExportedMessageRepository","getExternalStoreMessages","MessageRepository","useAui","useRef","useEffect","useState","RefObject","useCallback","useMemo","toExportedMessageRepository","toThreadMessages","messages","TMessage","survivingIds","Set","survivors","flatMap","m","message","console","warn","parentId","has","add","id","headId","isAwaitingToolApproval","status","type","reason","encodeContent","storageFormatAdapter","item","JSON","stringify","encode","useExternalHistory","runtimeRef","historyAdapter","onSetMessages","loadedRef","itemEpoch","setItemEpoch","aui","optionalThreadListItem","threadListItem","source","hasLoaded","setHasLoaded","historyIds","deferredTelemetryIds","persistedInnerMessages","Map","content","onSetMessagesRef","current","formatAdapter","undefined","withFormat","Error","isLoading","loadHistory","repo","load","length","set","getId","converted","thread","import","tempRepo","getMessages","error","pin","remoteId","getState","subscribe","n","threadState","isRunning","runStartRef","persistTimerRef","ReturnType","setTimeout","persistInFlightRef","Promise","resolve","stepBoundariesRef","wasRunningRef","toolCallCountRef","adapter","unsubscribe","wasRunning","lastMsg","at","role","currentToolCallCount","filter","p","push","Date","now","clearTimeout","persistSettled","initialThreadState","ignoreRunning","latest","boundaries","durationMs","lastAssistant","findLast","tcCount","totalSteps","stepDur","i","Math","round","stepTimestamps","map","endMs","start_ms","end_ms","telemetryOptions","then","lastInnerMessageId","getLastInnerId","msgs","toBatchItems","msg","idx","innerMessages","isTerminal","isAwaitingToolCalls","isReady","update","batchItems","innerId","persisted","get","append","delete","reportTelemetry","catch","deleteMessage","messageId","deleteMessages","bind","messageIndex","findIndex","previousInnerMessages","slice","itemsToDelete","deletion"],"sources":["../../src/runtime/useExternalHistory.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n AssistantRuntime,\n ThreadHistoryAdapter,\n ThreadMessage,\n MessageFormatAdapter,\n MessageFormatItem,\n MessageFormatRepository,\n ExportedMessageRepository,\n} from \"@assistant-ui/core\";\nimport { getExternalStoreMessages } from \"@assistant-ui/core\";\nimport { MessageRepository } from \"@assistant-ui/core/internal\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n useRef,\n useEffect,\n useState,\n type RefObject,\n useCallback,\n useMemo,\n} from \"react\";\n\nexport const toExportedMessageRepository = <TMessage>(\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n messages: MessageFormatRepository<TMessage>,\n): ExportedMessageRepository => {\n const survivingIds = new Set<string>();\n const survivors = messages.messages.flatMap((m) => {\n const message = toThreadMessages([m.message])[0];\n if (!message) {\n console.warn(\"Skipping a stored message that could not be loaded.\");\n return [];\n }\n if (m.parentId && !survivingIds.has(m.parentId)) return [];\n survivingIds.add(message.id);\n return [{ ...m, message }];\n });\n\n return {\n headId:\n messages.headId && survivingIds.has(messages.headId)\n ? messages.headId\n : null,\n messages: survivors,\n };\n};\n\nconst isAwaitingToolApproval = (message: ThreadMessage) =>\n message.status?.type === \"requires-action\" &&\n message.status.reason === \"tool-calls\";\n\nconst encodeContent = <TMessage>(\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n item: MessageFormatItem<TMessage>,\n) => JSON.stringify(storageFormatAdapter.encode(item));\n\nexport const useExternalHistory = <TMessage>(\n runtimeRef: RefObject<AssistantRuntime>,\n historyAdapter: ThreadHistoryAdapter | undefined,\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n onSetMessages: (messages: TMessage[]) => void,\n) => {\n const loadedRef = useRef(false);\n const [itemEpoch, setItemEpoch] = useState(0);\n\n const aui = useAui();\n const optionalThreadListItem = useCallback(\n () => (aui.threadListItem.source ? aui.threadListItem : null),\n [aui],\n );\n\n const [hasLoaded, setHasLoaded] = useState(false);\n\n const historyIds = useRef(new Set<string>());\n const deferredTelemetryIds = useRef(new Set<string>());\n // `content` is a snapshot taken at write time rather than a re-encode of `source`, because a retained message object can be mutated in place by the runtime that produced it.\n const persistedInnerMessages = useRef(\n new Map<string, { source: TMessage; content: string }>(),\n );\n\n const onSetMessagesRef = useRef(onSetMessages);\n useEffect(() => {\n onSetMessagesRef.current = onSetMessages;\n });\n\n const formatAdapter = useMemo(() => {\n if (!historyAdapter) return undefined;\n if (!historyAdapter.withFormat) {\n throw new Error(\n \"useAISDKRuntime: ThreadHistoryAdapter is missing the required `withFormat` method.\",\n );\n }\n return historyAdapter.withFormat<TMessage, any>(storageFormatAdapter);\n }, [historyAdapter, storageFormatAdapter]);\n\n const isLoading = formatAdapter != null && !hasLoaded;\n\n useEffect(() => {\n if (!formatAdapter || loadedRef.current) return undefined;\n\n const loadHistory = async () => {\n try {\n const repo = await formatAdapter.load();\n if (repo && repo.messages.length > 0) {\n for (const m of repo.messages) {\n persistedInnerMessages.current.set(\n storageFormatAdapter.getId(m.message),\n {\n source: m.message,\n content: encodeContent(storageFormatAdapter, m),\n },\n );\n }\n const converted = toExportedMessageRepository(toThreadMessages, repo);\n runtimeRef.current.thread.import(converted);\n\n const tempRepo = new MessageRepository();\n tempRepo.import(converted);\n const messages = tempRepo.getMessages();\n\n onSetMessagesRef.current(\n messages.flatMap(getExternalStoreMessages<TMessage>),\n );\n\n historyIds.current = new Set();\n for (const m of converted.messages) {\n historyIds.current.add(m.message.id);\n if (isAwaitingToolApproval(m.message)) {\n deferredTelemetryIds.current.add(m.message.id);\n }\n }\n }\n } catch (error) {\n console.error(\"Failed to load message history:\", error);\n } finally {\n setHasLoaded(true);\n }\n };\n\n formatAdapter.pin?.();\n\n const remoteId = optionalThreadListItem()?.getState().remoteId;\n if (!remoteId) {\n // History loads asynchronously against the thread list item; without a\n // remote id there is nothing to await, so the flag settles here.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setHasLoaded(true);\n return aui.subscribe(() => {\n if (optionalThreadListItem()?.getState().remoteId) {\n setItemEpoch((n) => n + 1);\n }\n });\n }\n\n const threadState = runtimeRef.current.thread.getState();\n if (threadState.isRunning || threadState.messages.length > 0) {\n loadedRef.current = true;\n setHasLoaded(true);\n return undefined;\n }\n\n loadedRef.current = true;\n void loadHistory();\n return undefined;\n }, [\n formatAdapter,\n toThreadMessages,\n runtimeRef,\n optionalThreadListItem,\n aui,\n itemEpoch,\n storageFormatAdapter,\n ]);\n\n const runStartRef = useRef<number | null>(null);\n const persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const persistInFlightRef = useRef<Promise<void>>(Promise.resolve());\n const stepBoundariesRef = useRef<number[]>([]);\n const wasRunningRef = useRef(false);\n const toolCallCountRef = useRef(0);\n\n useEffect(() => {\n if (!formatAdapter) return;\n const adapter = formatAdapter;\n\n const unsubscribe = runtimeRef.current.thread.subscribe(() => {\n const threadState = runtimeRef.current.thread.getState();\n const { isRunning } = threadState;\n const wasRunning = wasRunningRef.current;\n wasRunningRef.current = isRunning;\n\n // Track step boundaries by content changes (more reliable than isRunning)\n if (runStartRef.current != null) {\n const lastMsg = threadState.messages.at(-1);\n if (lastMsg?.role === \"assistant\") {\n const currentToolCallCount = lastMsg.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n while (toolCallCountRef.current < currentToolCallCount) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n toolCallCountRef.current++;\n }\n }\n }\n\n if (isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n adapter.pin?.();\n }\n // Cancel any pending persist — isRunning went back to true\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n }\n return;\n }\n\n // Only act on the true→false transition\n if (!wasRunning) return;\n\n // Record step boundary offset (synchronous for accuracy)\n if (runStartRef.current != null) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n }\n\n // Debounce: wait one macrotask so agentic step flickers are absorbed\n if (persistTimerRef.current) clearTimeout(persistTimerRef.current);\n persistTimerRef.current = setTimeout(() => {\n persistSettled(false);\n }, 0);\n });\n\n const initialThreadState = runtimeRef.current.thread.getState();\n wasRunningRef.current = initialThreadState.isRunning;\n if (initialThreadState.isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n adapter.pin?.();\n }\n } else if (initialThreadState.messages.length > 0) {\n persistSettled(true);\n }\n\n function persistSettled(ignoreRunning: boolean) {\n persistTimerRef.current = null;\n const latest = runtimeRef.current.thread.getState();\n if (!ignoreRunning && latest.isRunning) return;\n\n const boundaries = stepBoundariesRef.current;\n const durationMs = boundaries.length > 0 ? boundaries.at(-1) : undefined;\n\n // Fallback: if only 1 boundary but message has multiple steps, distribute evenly\n if (boundaries.length === 1 && durationMs != null) {\n const lastAssistant = latest.messages.findLast(\n (m) => m.role === \"assistant\",\n );\n if (lastAssistant) {\n const tcCount = lastAssistant.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n if (tcCount > 0) {\n const totalSteps = tcCount + 1;\n const stepDur = durationMs / totalSteps;\n boundaries.length = 0;\n for (let i = 0; i < totalSteps; i++) {\n boundaries.push(Math.round((i + 1) * stepDur));\n }\n }\n }\n }\n\n // Build per-step timestamps when there are multiple steps\n const stepTimestamps =\n boundaries.length > 1\n ? boundaries.map((endMs, i) => ({\n start_ms: i === 0 ? 0 : boundaries[i - 1]!,\n end_ms: endMs,\n }))\n : undefined;\n\n runStartRef.current = null;\n stepBoundariesRef.current = [];\n\n const telemetryOptions = {\n ...(durationMs != null ? { durationMs } : undefined),\n ...(stepTimestamps != null ? { stepTimestamps } : undefined),\n };\n\n persistInFlightRef.current = persistInFlightRef.current\n .then(async () => {\n const { messages } = latest;\n let lastInnerMessageId: string | null = null;\n\n const getLastInnerId = (msgs: TMessage[]): string | null =>\n msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)!) : null;\n\n const toBatchItems = (msgs: TMessage[]) =>\n msgs.map((msg, idx) => ({\n parentId:\n idx === 0\n ? lastInnerMessageId\n : storageFormatAdapter.getId(msgs[idx - 1]!),\n message: msg,\n }));\n\n for (const message of messages) {\n const innerMessages = getExternalStoreMessages<TMessage>(message);\n\n const isTerminal =\n message.status === undefined ||\n message.status.type === \"complete\" ||\n message.status.type === \"incomplete\";\n const isAwaitingToolCalls = isAwaitingToolApproval(message);\n // A paused message's later content can only reach storage via update, so it is persisted early only when the adapter supports update.\n const isReady =\n isTerminal ||\n (isAwaitingToolCalls && adapter.update !== undefined);\n\n if (!isReady) {\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n\n if (!historyIds.current.has(message.id)) {\n historyIds.current.add(message.id);\n deferredTelemetryIds.current.add(message.id);\n }\n\n const batchItems = toBatchItems(innerMessages);\n for (const item of batchItems) {\n const innerId = storageFormatAdapter.getId(item.message);\n const persisted = persistedInnerMessages.current.get(innerId);\n if (!persisted) {\n await adapter.append(item);\n persistedInnerMessages.current.set(innerId, {\n source: item.message,\n content: encodeContent(storageFormatAdapter, item),\n });\n } else if (\n persisted.source !== item.message &&\n durationMs !== undefined &&\n adapter.update\n ) {\n const content = encodeContent(storageFormatAdapter, item);\n if (content === persisted.content) {\n persisted.source = item.message;\n } else {\n try {\n await adapter.update(item, innerId);\n persistedInnerMessages.current.set(innerId, {\n source: item.message,\n content,\n });\n } catch {\n // A failed update leaves the stale baseline behind so the next run stop retries it.\n }\n }\n }\n }\n\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n\n if (deferredTelemetryIds.current.has(message.id) && isTerminal) {\n deferredTelemetryIds.current.delete(message.id);\n adapter.reportTelemetry?.(batchItems, telemetryOptions);\n }\n }\n })\n .catch((error) => {\n console.error(\"Failed to persist message history:\", error);\n });\n }\n\n return () => {\n unsubscribe();\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n persistSettled(false);\n }\n };\n }, [formatAdapter, storageFormatAdapter, runtimeRef]);\n\n const deleteMessage = useCallback(\n async (messageId: string) => {\n const deleteMessages = formatAdapter?.delete?.bind(formatAdapter);\n if (!deleteMessages) return;\n\n const messages = runtimeRef.current.thread.getState().messages;\n const messageIndex = messages.findIndex((m) => m.id === messageId);\n if (messageIndex === -1) return;\n\n const previousInnerMessages = messages\n .slice(0, messageIndex)\n .flatMap(getExternalStoreMessages<TMessage>);\n let parentId = previousInnerMessages.at(-1)\n ? storageFormatAdapter.getId(previousInnerMessages.at(-1)!)\n : null;\n const itemsToDelete = getExternalStoreMessages<TMessage>(\n messages[messageIndex]!,\n ).map((message) => {\n const item = { parentId, message };\n parentId = storageFormatAdapter.getId(message);\n return item;\n });\n\n const deletion = persistInFlightRef.current.then(async () => {\n await deleteMessages(itemsToDelete);\n\n historyIds.current.delete(messageId);\n deferredTelemetryIds.current.delete(messageId);\n for (const item of itemsToDelete) {\n persistedInnerMessages.current.delete(\n storageFormatAdapter.getId(item.message),\n );\n }\n });\n\n persistInFlightRef.current = deletion.catch(() => {});\n await deletion;\n },\n [formatAdapter, runtimeRef, storageFormatAdapter],\n );\n\n return { isLoading, deleteMessage };\n};\n"],"mappings":"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"}
@@ -117,7 +117,8 @@ var AISDKToolkit = class {
117
117
  async #mcpTools() {
118
118
  const toolSets = await Promise.all(Object.entries(this.#toolkit).filter((entry) => isMcpToolkitTool(entry[1])).map(async ([name, tool]) => {
119
119
  const startedAt = Date.now();
120
- const client = await this.#mcpClient(name, tool.server, startedAt).catch((error) => {
120
+ const clientPromise = this.#mcpClient(name, tool.server, startedAt);
121
+ const client = await clientPromise.catch((error) => {
121
122
  if (error instanceof MCPConnectionTimeoutError) throw error;
122
123
  throw toMcpToolkitError(name, "connect", error);
123
124
  });
@@ -134,14 +135,16 @@ var AISDKToolkit = class {
134
135
  ];
135
136
  } catch (error) {
136
137
  if (error instanceof MCPConnectionTimeoutError) {
137
- this.#mcpClients.delete(name);
138
- client.close().catch(() => {});
138
+ if (this.#mcpClients.get(name) === clientPromise) {
139
+ this.#mcpClients.delete(name);
140
+ client.close().catch(() => {});
141
+ }
139
142
  throw error;
140
143
  }
141
144
  throw toMcpToolkitError(name, "list tools", error);
142
145
  }
143
146
  }));
144
- const tools = {};
147
+ const tools = Object.create(null);
145
148
  const toolSources = /* @__PURE__ */ new Map();
146
149
  for (const [serverName, mcpTool, toolSet] of toolSets) for (const [toolName, tool] of Object.entries(toolSet)) {
147
150
  if (isDisabledMcpTool(mcpTool.tools?.[toolName])) continue;
@@ -1 +1 @@
1
- {"version":3,"file":"generativeTools.js","names":["jsonSchema","ToolSet","MCPClient","MCPClientConfig","createMCPClient","Experimental_StdioMCPTransport","toJSONSchema","Tool","McpServerConfig","ToolModelOutputFunction","McpToolkitToolConfig","Toolkit","ToolkitDefinition","frontendTools","FrontendTools","toAISDKContent","toAISDKDefaultOutput","unwrapModelContentEnvelope","ModelContentEnvelope","EMPTY_SCHEMA","type","const","properties","humanNotSupported","Error","neverAbort","AbortController","signal","MCPConnectionTimeoutPhase","MCPConnectionTimeoutError","createMcpConnectionTimeoutError","name","phase","timeoutMs","withMcpConnectionTimeout","promise","Promise","T","options","config","startedAt","connectionTimeout","undefined","remainingMs","Date","now","timeoutError","timeout","ReturnType","setTimeout","race","_","reject","clearTimeout","parametersToInputSchema","parameters","GenerativeToolsOptions","toolkit","AISDKToolkitOptions","AISDKToolkitToolsOptions","frontend","generativeTools","assertNoMcpToolkitTools","toProviderToolSet","toServerToolSet","AISDKToolkit","mcpClients","Map","constructor","tools","frontendToolSet","mcpToolSet","mcpTools","providerToolSet","serverToolSet","assertNoMcpToolNameCollisions","source","close","clientEntries","entries","clientNames","map","clear","clientResults","allSettled","clientPromise","clients","flatMap","result","index","status","value","closeResults","client","errors","toMcpToolkitError","reason","length","AggregateError","#mcpTools","McpToolSet","toolSets","all","Object","filter","entry","McpToolkitTool","isMcpToolkitTool","tool","mcpClient","server","catch","error","delete","toolSources","serverName","mcpTool","toolSet","toolName","isDisabledMcpTool","exposedName","prefix","existingServerName","get","set","sources","#mcpClient","existing","createPromise","toMCPClientConfig","next","then","transport","command","args","env","cwd","url","headers","redirect","ToolkitTool","Record","mcp","prototype","hasOwnProperty","call","disabled","getErrorMessage","message","String","entryName","action","cause","mcpToolName","find","AISDKToModelOutputOptions","Omit","Parameters","TArgs","TResult","output","toAISDKToModelOutput","toModelOutput","modelContent","parts","fromEntries","t","execute","description","inputSchema","providerOptions","callOptions","toolCallId","abortSignal","AbortSignal","human","ProviderToolkitTool","isProviderToolkitTool","id","providerId","supportsDeferredResults","Extract"],"sources":["../../src/tools/generativeTools.ts"],"sourcesContent":["import { jsonSchema, type ToolSet } from \"ai\";\nimport type { MCPClient, MCPClientConfig } from \"@ai-sdk/mcp\";\nimport { createMCPClient } from \"@ai-sdk/mcp\";\nimport { Experimental_StdioMCPTransport } from \"#mcp-stdio\";\nimport {\n toJSONSchema,\n type Tool,\n type McpServerConfig,\n type ToolModelOutputFunction,\n} from \"assistant-stream\";\nimport type {\n McpToolkitToolConfig,\n Toolkit,\n ToolkitDefinition,\n} from \"@assistant-ui/core/react\";\nimport { frontendTools, type FrontendTools } from \"./frontendTools\";\nimport {\n toAISDKContent,\n toAISDKDefaultOutput,\n} from \"../converters/toolOutputConversion\";\nimport {\n unwrapModelContentEnvelope,\n type ModelContentEnvelope,\n} from \"../converters/modelContentEnvelope\";\n\nconst EMPTY_SCHEMA = { type: \"object\" as const, properties: {} };\n\nconst humanNotSupported = (): never => {\n throw new Error(\n \"`human()` is not available during server-side tool execution.\",\n );\n};\n\n// AI SDK leaves `abortSignal` optional; assistant-ui's execute requires one.\nconst neverAbort = new AbortController().signal;\n\ntype MCPConnectionTimeoutPhase = \"connecting\" | \"listing tools\";\n\nclass MCPConnectionTimeoutError extends Error {}\n\nconst createMcpConnectionTimeoutError = (\n name: string,\n phase: MCPConnectionTimeoutPhase,\n timeoutMs: number,\n) =>\n new MCPConnectionTimeoutError(\n `MCP toolkit entry \"${name}\" timed out while ${phase} after ${timeoutMs}ms.`,\n );\n\nconst withMcpConnectionTimeout = async <T>(\n promise: Promise<T>,\n options: {\n name: string;\n config: McpServerConfig;\n phase: MCPConnectionTimeoutPhase;\n startedAt: number;\n },\n): Promise<T> => {\n const timeoutMs = options.config.connectionTimeout;\n if (timeoutMs === undefined) return await promise;\n const remainingMs = timeoutMs - (Date.now() - options.startedAt);\n const timeoutError = () =>\n createMcpConnectionTimeoutError(options.name, options.phase, timeoutMs);\n if (remainingMs <= 0) throw timeoutError();\n\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => reject(timeoutError()), remainingMs);\n }),\n ]);\n } finally {\n if (timeout !== undefined) clearTimeout(timeout);\n }\n};\n\nconst parametersToInputSchema = (parameters: Tool[\"parameters\"] | undefined) =>\n jsonSchema(parameters ? toJSONSchema(parameters) : EMPTY_SCHEMA);\n\n/**\n * @deprecated Options for the deprecated {@link generativeTools}. Use\n * {@link AISDKToolkit} with {@link AISDKToolkitOptions} /\n * {@link AISDKToolkitToolsOptions} instead.\n */\nexport interface GenerativeToolsOptions {\n /**\n * The server build of a generative toolkit (schema + server `execute`). Typed\n * as the canonical {@link Toolkit} so callers don't need to cast; the server\n * build carries `execute`, recovered internally as {@link ToolkitDefinition}.\n */\n toolkit: Toolkit;\n /**\n * Tools uploaded by the frontend (the request body's `tools`). Merged in\n * alongside the `toolkit`; a server `execute` from `toolkit` takes precedence\n * over an uploaded entry of the same name.\n */\n frontendTools?: FrontendTools;\n}\n\nexport type AISDKToolkitOptions = {\n toolkit: Toolkit;\n};\n\nexport type AISDKToolkitToolsOptions = {\n /**\n * Tools uploaded by the frontend request body.\n */\n frontend?: FrontendTools;\n};\n\n/**\n * Builds an AI SDK `ToolSet` for server-side use with `streamText` /\n * `generateText` from a generative `toolkit` and the frontend-uploaded tools.\n *\n * Each toolkit tool's `execute` runs on the server. Pair this with the\n * `\"use generative\"` compiler: import the toolkit in a server route (where it\n * resolves to the server build — schema + `execute`, with `render` stripped) and\n * pass it here. Tools without an `execute` are still exposed to the model but\n * left for the client to fulfill. `frontendTools` lets the client contribute\n * tools that aren't in the static toolkit.\n *\n * @deprecated Use {@link AISDKToolkit} instead:\n * `new AISDKToolkit({ toolkit }).tools({ frontend })`. It is a strict superset\n * (it also opens MCP server connections), so it replaces `generativeTools`\n * everywhere. The `frontendTools` option is named `frontend` on `.tools()`, and\n * `.tools()` is async. `generativeTools` will be removed in a future version.\n *\n * @example\n * ```ts\n * // Define once at module scope so any MCP connections pool across requests.\n * const aiToolkit = new AISDKToolkit({ toolkit: docsToolkit });\n *\n * // In your route handler:\n * const { tools } = await req.json();\n * streamText({\n * model,\n * messages,\n * tools: await aiToolkit.tools({ frontend: tools }),\n * });\n * ```\n */\nexport const generativeTools = (options: GenerativeToolsOptions): ToolSet => {\n assertNoMcpToolkitTools(options.toolkit);\n return {\n ...(options.frontendTools ? frontendTools(options.frontendTools) : {}),\n // `toolkit` last so its server-side `execute` wins over an uploaded entry of\n // the same name. The cast recovers the declaration shape — the server build\n // carries `execute`, which the canonical `Toolkit` type erases.\n ...toProviderToolSet(options.toolkit),\n ...toServerToolSet(options.toolkit as ToolkitDefinition),\n };\n};\n\nexport class AISDKToolkit {\n readonly #toolkit: Toolkit;\n readonly #mcpClients = new Map<string, Promise<MCPClient>>();\n\n constructor(options: AISDKToolkitOptions) {\n this.#toolkit = options.toolkit;\n }\n\n async tools(options: AISDKToolkitToolsOptions = {}): Promise<ToolSet> {\n const frontendToolSet = options.frontend\n ? frontendTools(options.frontend)\n : {};\n const mcpToolSet = await this.#mcpTools();\n const providerToolSet = toProviderToolSet(this.#toolkit);\n const serverToolSet = toServerToolSet(this.#toolkit as ToolkitDefinition);\n\n assertNoMcpToolNameCollisions(mcpToolSet, [\n { source: \"frontend\", tools: frontendToolSet },\n { source: \"provider\", tools: providerToolSet },\n { source: \"toolkit\", tools: serverToolSet },\n ]);\n\n return {\n ...frontendToolSet,\n ...mcpToolSet.tools,\n ...providerToolSet,\n ...serverToolSet,\n };\n }\n\n async close(): Promise<void> {\n const clientEntries = [...this.#mcpClients.entries()];\n const clientNames = clientEntries.map(([name]) => name);\n this.#mcpClients.clear();\n const clientResults = await Promise.allSettled(\n clientEntries.map(([, clientPromise]) => clientPromise),\n );\n const clients = clientResults.flatMap((result, index) =>\n result.status === \"fulfilled\"\n ? [[clientNames[index]!, result.value] as const]\n : [],\n );\n const closeResults = await Promise.allSettled(\n clients.map(([, client]) => client.close()),\n );\n const errors = [\n ...clientResults.flatMap((result, index) =>\n result.status === \"rejected\"\n ? [toMcpToolkitError(clientNames[index]!, \"connect\", result.reason)]\n : [],\n ),\n ...closeResults.flatMap((result, index) =>\n result.status === \"rejected\"\n ? [toMcpToolkitError(clients[index]![0], \"close\", result.reason)]\n : [],\n ),\n ];\n if (errors.length === 1) throw errors[0];\n if (errors.length > 1) {\n throw new AggregateError(\n errors,\n \"Failed to close one or more MCP clients\",\n );\n }\n }\n\n async #mcpTools(): Promise<McpToolSet> {\n const toolSets = await Promise.all(\n Object.entries(this.#toolkit)\n .filter((entry): entry is [string, McpToolkitTool] =>\n isMcpToolkitTool(entry[1]),\n )\n .map(async ([name, tool]) => {\n const startedAt = Date.now();\n const client = await this.#mcpClient(\n name,\n tool.server,\n startedAt,\n ).catch((error: unknown) => {\n if (error instanceof MCPConnectionTimeoutError) throw error;\n throw toMcpToolkitError(name, \"connect\", error);\n });\n try {\n const tools = await withMcpConnectionTimeout(client.tools(), {\n name,\n config: tool.server,\n phase: \"listing tools\",\n startedAt,\n });\n return [name, tool, tools] as const;\n } catch (error) {\n if (error instanceof MCPConnectionTimeoutError) {\n this.#mcpClients.delete(name);\n void client.close().catch(() => {});\n throw error;\n }\n throw toMcpToolkitError(name, \"list tools\", error);\n }\n }),\n );\n\n const tools: ToolSet = {};\n const toolSources = new Map<string, string>();\n for (const [serverName, mcpTool, toolSet] of toolSets) {\n for (const [toolName, tool] of Object.entries(toolSet)) {\n if (isDisabledMcpTool(mcpTool.tools?.[toolName])) continue;\n const exposedName = `${mcpTool.prefix ?? \"\"}${toolName}`;\n const existingServerName = toolSources.get(exposedName);\n if (existingServerName) {\n throw new Error(\n `MCP tool name collision: \"${exposedName}\" is exposed by both \"${existingServerName}\" and \"${serverName}\". Rename one of the toolkit entries or expose distinct MCP tool names.`,\n );\n }\n toolSources.set(exposedName, serverName);\n tools[exposedName] = tool as ToolSet[string];\n }\n }\n return { tools, sources: toolSources };\n }\n\n #mcpClient(\n name: string,\n config: McpServerConfig,\n startedAt: number,\n ): Promise<MCPClient> {\n const existing = this.#mcpClients.get(name);\n if (existing) return existing;\n const createPromise = createMCPClient(toMCPClientConfig(config));\n let next: Promise<MCPClient>;\n next = withMcpConnectionTimeout(createPromise, {\n name,\n config,\n phase: \"connecting\",\n startedAt,\n }).catch((error) => {\n if (this.#mcpClients.get(name) === next) {\n this.#mcpClients.delete(name);\n }\n if (error instanceof MCPConnectionTimeoutError) {\n void createPromise.then((client) => client.close()).catch(() => {});\n }\n throw error;\n });\n this.#mcpClients.set(name, next);\n return next;\n }\n}\n\nconst toMCPClientConfig = (config: McpServerConfig): MCPClientConfig => {\n if (config.type === \"stdio\") {\n return {\n transport: new Experimental_StdioMCPTransport({\n command: config.command,\n ...(config.args && { args: [...config.args] }),\n ...(config.env && { env: config.env }),\n ...(config.cwd && { cwd: config.cwd }),\n }),\n };\n }\n\n return {\n transport: {\n type: config.type,\n url: config.url,\n ...(config.headers && { headers: config.headers }),\n ...(config.redirect && { redirect: config.redirect }),\n },\n };\n};\n\ntype ToolkitTool = Toolkit[string];\n\ntype McpToolkitTool = ToolkitTool & {\n type: \"mcp\";\n server: McpServerConfig;\n prefix?: string | undefined;\n tools?: Record<string, McpToolkitToolConfig> | undefined;\n};\n\ntype McpToolSet = {\n tools: ToolSet;\n sources: Map<string, string>;\n};\n\nconst assertNoMcpToolNameCollisions = (\n mcp: McpToolSet,\n toolSets: readonly { source: string; tools: ToolSet }[],\n): void => {\n for (const [toolName, serverName] of mcp.sources) {\n for (const { source, tools } of toolSets) {\n if (!Object.prototype.hasOwnProperty.call(tools, toolName)) continue;\n throw new Error(\n `MCP tool \"${toolName}\" from \"${serverName}\" conflicts with ${source} tool \"${toolName}\". Rename one of the tools so each model-visible tool name is unique.`,\n );\n }\n }\n};\n\nconst isMcpToolkitTool = (tool: ToolkitTool): tool is McpToolkitTool =>\n tool.type === \"mcp\" && !tool.disabled;\n\nconst getErrorMessage = (error: unknown): string =>\n error instanceof Error ? error.message || error.name : String(error);\n\nconst toMcpToolkitError = (\n entryName: string,\n action: \"connect\" | \"list tools\" | \"close\",\n error: unknown,\n): Error => {\n return new Error(\n `MCP toolkit entry \"${entryName}\" failed to ${action}: ${getErrorMessage(error)}`,\n { cause: error },\n );\n};\n\nconst isDisabledMcpTool = (config: McpToolkitToolConfig | undefined): boolean =>\n config?.disabled === true;\n\nconst assertNoMcpToolkitTools = (toolkit: Toolkit): void => {\n const mcpToolName = Object.entries(toolkit).find(([, tool]) =>\n isMcpToolkitTool(tool),\n )?.[0];\n if (!mcpToolName) return;\n\n throw new Error(\n `MCP toolkit entry \"${mcpToolName}\" requires AISDKToolkit. Use new AISDKToolkit({ toolkit }).tools(...) instead of generativeTools(...).`,\n );\n};\n\ntype AISDKToModelOutputOptions<TArgs, TResult> = Omit<\n Parameters<ToolModelOutputFunction<TArgs, TResult>>[0],\n \"output\"\n> & {\n output: TResult | ModelContentEnvelope<TResult>;\n};\n\nconst toAISDKToModelOutput =\n <TArgs, TResult>(toModelOutput?: ToolModelOutputFunction<TArgs, TResult>) =>\n async (options: AISDKToModelOutputOptions<TArgs, TResult>) => {\n const { result, modelContent } = unwrapModelContentEnvelope(options.output);\n\n if (modelContent !== undefined) {\n return toAISDKContent(modelContent);\n }\n\n if (!toModelOutput) {\n return toAISDKDefaultOutput(result);\n }\n\n const parts = await toModelOutput({\n ...options,\n output: result,\n });\n return toAISDKContent(parts);\n };\n\nconst toServerToolSet = (toolkit: ToolkitDefinition): ToolSet =>\n Object.fromEntries(\n Object.entries(toolkit)\n .filter(\n ([, t]) => t.type !== \"mcp\" && t.type !== \"provider\" && !t.disabled,\n )\n .map(([name, t]) => {\n const execute = t.execute;\n return [\n name,\n {\n ...(t.description !== undefined && { description: t.description }),\n inputSchema: parametersToInputSchema(t.parameters),\n toModelOutput: toAISDKToModelOutput(t.toModelOutput),\n ...(t.providerOptions && { providerOptions: t.providerOptions }),\n ...(execute && {\n execute: (\n args: unknown,\n callOptions: { toolCallId: string; abortSignal?: AbortSignal },\n ) =>\n execute(args as never, {\n toolCallId: callOptions.toolCallId,\n abortSignal: callOptions.abortSignal ?? neverAbort,\n human: humanNotSupported,\n }),\n }),\n },\n ];\n }),\n ) as ToolSet;\n\nconst toProviderToolSet = (toolkit: Toolkit): ToolSet =>\n Object.fromEntries(\n Object.entries(toolkit)\n .filter((entry): entry is [string, ProviderToolkitTool] =>\n isProviderToolkitTool(entry[1]),\n )\n .map(([name, t]) => [\n name,\n {\n type: \"provider\",\n id: t.providerId,\n args: t.args,\n ...(t.parameters && {\n inputSchema: parametersToInputSchema(t.parameters),\n }),\n ...(t.providerOptions && { providerOptions: t.providerOptions }),\n ...(t.supportsDeferredResults !== undefined && {\n supportsDeferredResults: t.supportsDeferredResults,\n }),\n },\n ]),\n ) as ToolSet;\n\ntype ProviderToolkitTool = Extract<Toolkit[string], { type: \"provider\" }>;\n\nconst isProviderToolkitTool = (\n tool: Toolkit[string],\n): tool is ProviderToolkitTool => tool.type === \"provider\" && !tool.disabled;\n"],"mappings":"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"}
1
+ {"version":3,"file":"generativeTools.js","names":["jsonSchema","ToolSet","MCPClient","MCPClientConfig","createMCPClient","Experimental_StdioMCPTransport","toJSONSchema","Tool","McpServerConfig","ToolModelOutputFunction","McpToolkitToolConfig","Toolkit","ToolkitDefinition","frontendTools","FrontendTools","toAISDKContent","toAISDKDefaultOutput","unwrapModelContentEnvelope","ModelContentEnvelope","EMPTY_SCHEMA","type","const","properties","humanNotSupported","Error","neverAbort","AbortController","signal","MCPConnectionTimeoutPhase","MCPConnectionTimeoutError","createMcpConnectionTimeoutError","name","phase","timeoutMs","withMcpConnectionTimeout","promise","Promise","T","options","config","startedAt","connectionTimeout","undefined","remainingMs","Date","now","timeoutError","timeout","ReturnType","setTimeout","race","_","reject","clearTimeout","parametersToInputSchema","parameters","GenerativeToolsOptions","toolkit","AISDKToolkitOptions","AISDKToolkitToolsOptions","frontend","generativeTools","assertNoMcpToolkitTools","toProviderToolSet","toServerToolSet","AISDKToolkit","mcpClients","Map","constructor","tools","frontendToolSet","mcpToolSet","mcpTools","providerToolSet","serverToolSet","assertNoMcpToolNameCollisions","source","close","clientEntries","entries","clientNames","map","clear","clientResults","allSettled","clientPromise","clients","flatMap","result","index","status","value","closeResults","client","errors","toMcpToolkitError","reason","length","AggregateError","#mcpTools","McpToolSet","toolSets","all","Object","filter","entry","McpToolkitTool","isMcpToolkitTool","tool","mcpClient","server","catch","error","get","delete","create","toolSources","serverName","mcpTool","toolSet","toolName","isDisabledMcpTool","exposedName","prefix","existingServerName","set","sources","#mcpClient","existing","createPromise","toMCPClientConfig","next","then","transport","command","args","env","cwd","url","headers","redirect","ToolkitTool","Record","mcp","prototype","hasOwnProperty","call","disabled","getErrorMessage","message","String","entryName","action","cause","mcpToolName","find","AISDKToModelOutputOptions","Omit","Parameters","TArgs","TResult","output","toAISDKToModelOutput","toModelOutput","modelContent","parts","fromEntries","t","execute","description","inputSchema","providerOptions","callOptions","toolCallId","abortSignal","AbortSignal","human","ProviderToolkitTool","isProviderToolkitTool","id","providerId","supportsDeferredResults","Extract"],"sources":["../../src/tools/generativeTools.ts"],"sourcesContent":["import { jsonSchema, type ToolSet } from \"ai\";\nimport type { MCPClient, MCPClientConfig } from \"@ai-sdk/mcp\";\nimport { createMCPClient } from \"@ai-sdk/mcp\";\nimport { Experimental_StdioMCPTransport } from \"#mcp-stdio\";\nimport {\n toJSONSchema,\n type Tool,\n type McpServerConfig,\n type ToolModelOutputFunction,\n} from \"assistant-stream\";\nimport type {\n McpToolkitToolConfig,\n Toolkit,\n ToolkitDefinition,\n} from \"@assistant-ui/core/react\";\nimport { frontendTools, type FrontendTools } from \"./frontendTools\";\nimport {\n toAISDKContent,\n toAISDKDefaultOutput,\n} from \"../converters/toolOutputConversion\";\nimport {\n unwrapModelContentEnvelope,\n type ModelContentEnvelope,\n} from \"../converters/modelContentEnvelope\";\n\nconst EMPTY_SCHEMA = { type: \"object\" as const, properties: {} };\n\nconst humanNotSupported = (): never => {\n throw new Error(\n \"`human()` is not available during server-side tool execution.\",\n );\n};\n\n// AI SDK leaves `abortSignal` optional; assistant-ui's execute requires one.\nconst neverAbort = new AbortController().signal;\n\ntype MCPConnectionTimeoutPhase = \"connecting\" | \"listing tools\";\n\nclass MCPConnectionTimeoutError extends Error {}\n\nconst createMcpConnectionTimeoutError = (\n name: string,\n phase: MCPConnectionTimeoutPhase,\n timeoutMs: number,\n) =>\n new MCPConnectionTimeoutError(\n `MCP toolkit entry \"${name}\" timed out while ${phase} after ${timeoutMs}ms.`,\n );\n\nconst withMcpConnectionTimeout = async <T>(\n promise: Promise<T>,\n options: {\n name: string;\n config: McpServerConfig;\n phase: MCPConnectionTimeoutPhase;\n startedAt: number;\n },\n): Promise<T> => {\n const timeoutMs = options.config.connectionTimeout;\n if (timeoutMs === undefined) return await promise;\n const remainingMs = timeoutMs - (Date.now() - options.startedAt);\n const timeoutError = () =>\n createMcpConnectionTimeoutError(options.name, options.phase, timeoutMs);\n if (remainingMs <= 0) throw timeoutError();\n\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => reject(timeoutError()), remainingMs);\n }),\n ]);\n } finally {\n if (timeout !== undefined) clearTimeout(timeout);\n }\n};\n\nconst parametersToInputSchema = (parameters: Tool[\"parameters\"] | undefined) =>\n jsonSchema(parameters ? toJSONSchema(parameters) : EMPTY_SCHEMA);\n\n/**\n * @deprecated Options for the deprecated {@link generativeTools}. Use\n * {@link AISDKToolkit} with {@link AISDKToolkitOptions} /\n * {@link AISDKToolkitToolsOptions} instead.\n */\nexport interface GenerativeToolsOptions {\n /**\n * The server build of a generative toolkit (schema + server `execute`). Typed\n * as the canonical {@link Toolkit} so callers don't need to cast; the server\n * build carries `execute`, recovered internally as {@link ToolkitDefinition}.\n */\n toolkit: Toolkit;\n /**\n * Tools uploaded by the frontend (the request body's `tools`). Merged in\n * alongside the `toolkit`; a server `execute` from `toolkit` takes precedence\n * over an uploaded entry of the same name.\n */\n frontendTools?: FrontendTools;\n}\n\nexport type AISDKToolkitOptions = {\n toolkit: Toolkit;\n};\n\nexport type AISDKToolkitToolsOptions = {\n /**\n * Tools uploaded by the frontend request body.\n */\n frontend?: FrontendTools;\n};\n\n/**\n * Builds an AI SDK `ToolSet` for server-side use with `streamText` /\n * `generateText` from a generative `toolkit` and the frontend-uploaded tools.\n *\n * Each toolkit tool's `execute` runs on the server. Pair this with the\n * `\"use generative\"` compiler: import the toolkit in a server route (where it\n * resolves to the server build — schema + `execute`, with `render` stripped) and\n * pass it here. Tools without an `execute` are still exposed to the model but\n * left for the client to fulfill. `frontendTools` lets the client contribute\n * tools that aren't in the static toolkit.\n *\n * @deprecated Use {@link AISDKToolkit} instead:\n * `new AISDKToolkit({ toolkit }).tools({ frontend })`. It is a strict superset\n * (it also opens MCP server connections), so it replaces `generativeTools`\n * everywhere. The `frontendTools` option is named `frontend` on `.tools()`, and\n * `.tools()` is async. `generativeTools` will be removed in a future version.\n *\n * @example\n * ```ts\n * // Define once at module scope so any MCP connections pool across requests.\n * const aiToolkit = new AISDKToolkit({ toolkit: docsToolkit });\n *\n * // In your route handler:\n * const { tools } = await req.json();\n * streamText({\n * model,\n * messages,\n * tools: await aiToolkit.tools({ frontend: tools }),\n * });\n * ```\n */\nexport const generativeTools = (options: GenerativeToolsOptions): ToolSet => {\n assertNoMcpToolkitTools(options.toolkit);\n return {\n ...(options.frontendTools ? frontendTools(options.frontendTools) : {}),\n // `toolkit` last so its server-side `execute` wins over an uploaded entry of\n // the same name. The cast recovers the declaration shape — the server build\n // carries `execute`, which the canonical `Toolkit` type erases.\n ...toProviderToolSet(options.toolkit),\n ...toServerToolSet(options.toolkit as ToolkitDefinition),\n };\n};\n\nexport class AISDKToolkit {\n readonly #toolkit: Toolkit;\n readonly #mcpClients = new Map<string, Promise<MCPClient>>();\n\n constructor(options: AISDKToolkitOptions) {\n this.#toolkit = options.toolkit;\n }\n\n async tools(options: AISDKToolkitToolsOptions = {}): Promise<ToolSet> {\n const frontendToolSet = options.frontend\n ? frontendTools(options.frontend)\n : {};\n const mcpToolSet = await this.#mcpTools();\n const providerToolSet = toProviderToolSet(this.#toolkit);\n const serverToolSet = toServerToolSet(this.#toolkit as ToolkitDefinition);\n\n assertNoMcpToolNameCollisions(mcpToolSet, [\n { source: \"frontend\", tools: frontendToolSet },\n { source: \"provider\", tools: providerToolSet },\n { source: \"toolkit\", tools: serverToolSet },\n ]);\n\n return {\n ...frontendToolSet,\n ...mcpToolSet.tools,\n ...providerToolSet,\n ...serverToolSet,\n };\n }\n\n async close(): Promise<void> {\n const clientEntries = [...this.#mcpClients.entries()];\n const clientNames = clientEntries.map(([name]) => name);\n this.#mcpClients.clear();\n const clientResults = await Promise.allSettled(\n clientEntries.map(([, clientPromise]) => clientPromise),\n );\n const clients = clientResults.flatMap((result, index) =>\n result.status === \"fulfilled\"\n ? [[clientNames[index]!, result.value] as const]\n : [],\n );\n const closeResults = await Promise.allSettled(\n clients.map(([, client]) => client.close()),\n );\n const errors = [\n ...clientResults.flatMap((result, index) =>\n result.status === \"rejected\"\n ? [toMcpToolkitError(clientNames[index]!, \"connect\", result.reason)]\n : [],\n ),\n ...closeResults.flatMap((result, index) =>\n result.status === \"rejected\"\n ? [toMcpToolkitError(clients[index]![0], \"close\", result.reason)]\n : [],\n ),\n ];\n if (errors.length === 1) throw errors[0];\n if (errors.length > 1) {\n throw new AggregateError(\n errors,\n \"Failed to close one or more MCP clients\",\n );\n }\n }\n\n async #mcpTools(): Promise<McpToolSet> {\n const toolSets = await Promise.all(\n Object.entries(this.#toolkit)\n .filter((entry): entry is [string, McpToolkitTool] =>\n isMcpToolkitTool(entry[1]),\n )\n .map(async ([name, tool]) => {\n const startedAt = Date.now();\n const clientPromise = this.#mcpClient(name, tool.server, startedAt);\n const client = await clientPromise.catch((error: unknown) => {\n if (error instanceof MCPConnectionTimeoutError) throw error;\n throw toMcpToolkitError(name, \"connect\", error);\n });\n try {\n const tools = await withMcpConnectionTimeout(client.tools(), {\n name,\n config: tool.server,\n phase: \"listing tools\",\n startedAt,\n });\n return [name, tool, tools] as const;\n } catch (error) {\n if (error instanceof MCPConnectionTimeoutError) {\n if (this.#mcpClients.get(name) === clientPromise) {\n this.#mcpClients.delete(name);\n void client.close().catch(() => {});\n }\n throw error;\n }\n throw toMcpToolkitError(name, \"list tools\", error);\n }\n }),\n );\n\n const tools = Object.create(null) as ToolSet;\n const toolSources = new Map<string, string>();\n for (const [serverName, mcpTool, toolSet] of toolSets) {\n for (const [toolName, tool] of Object.entries(toolSet)) {\n if (isDisabledMcpTool(mcpTool.tools?.[toolName])) continue;\n const exposedName = `${mcpTool.prefix ?? \"\"}${toolName}`;\n const existingServerName = toolSources.get(exposedName);\n if (existingServerName) {\n throw new Error(\n `MCP tool name collision: \"${exposedName}\" is exposed by both \"${existingServerName}\" and \"${serverName}\". Rename one of the toolkit entries or expose distinct MCP tool names.`,\n );\n }\n toolSources.set(exposedName, serverName);\n tools[exposedName] = tool as ToolSet[string];\n }\n }\n return { tools, sources: toolSources };\n }\n\n #mcpClient(\n name: string,\n config: McpServerConfig,\n startedAt: number,\n ): Promise<MCPClient> {\n const existing = this.#mcpClients.get(name);\n if (existing) return existing;\n const createPromise = createMCPClient(toMCPClientConfig(config));\n let next: Promise<MCPClient>;\n next = withMcpConnectionTimeout(createPromise, {\n name,\n config,\n phase: \"connecting\",\n startedAt,\n }).catch((error) => {\n if (this.#mcpClients.get(name) === next) {\n this.#mcpClients.delete(name);\n }\n if (error instanceof MCPConnectionTimeoutError) {\n void createPromise.then((client) => client.close()).catch(() => {});\n }\n throw error;\n });\n this.#mcpClients.set(name, next);\n return next;\n }\n}\n\nconst toMCPClientConfig = (config: McpServerConfig): MCPClientConfig => {\n if (config.type === \"stdio\") {\n return {\n transport: new Experimental_StdioMCPTransport({\n command: config.command,\n ...(config.args && { args: [...config.args] }),\n ...(config.env && { env: config.env }),\n ...(config.cwd && { cwd: config.cwd }),\n }),\n };\n }\n\n return {\n transport: {\n type: config.type,\n url: config.url,\n ...(config.headers && { headers: config.headers }),\n ...(config.redirect && { redirect: config.redirect }),\n },\n };\n};\n\ntype ToolkitTool = Toolkit[string];\n\ntype McpToolkitTool = ToolkitTool & {\n type: \"mcp\";\n server: McpServerConfig;\n prefix?: string | undefined;\n tools?: Record<string, McpToolkitToolConfig> | undefined;\n};\n\ntype McpToolSet = {\n tools: ToolSet;\n sources: Map<string, string>;\n};\n\nconst assertNoMcpToolNameCollisions = (\n mcp: McpToolSet,\n toolSets: readonly { source: string; tools: ToolSet }[],\n): void => {\n for (const [toolName, serverName] of mcp.sources) {\n for (const { source, tools } of toolSets) {\n if (!Object.prototype.hasOwnProperty.call(tools, toolName)) continue;\n throw new Error(\n `MCP tool \"${toolName}\" from \"${serverName}\" conflicts with ${source} tool \"${toolName}\". Rename one of the tools so each model-visible tool name is unique.`,\n );\n }\n }\n};\n\nconst isMcpToolkitTool = (tool: ToolkitTool): tool is McpToolkitTool =>\n tool.type === \"mcp\" && !tool.disabled;\n\nconst getErrorMessage = (error: unknown): string =>\n error instanceof Error ? error.message || error.name : String(error);\n\nconst toMcpToolkitError = (\n entryName: string,\n action: \"connect\" | \"list tools\" | \"close\",\n error: unknown,\n): Error => {\n return new Error(\n `MCP toolkit entry \"${entryName}\" failed to ${action}: ${getErrorMessage(error)}`,\n { cause: error },\n );\n};\n\nconst isDisabledMcpTool = (config: McpToolkitToolConfig | undefined): boolean =>\n config?.disabled === true;\n\nconst assertNoMcpToolkitTools = (toolkit: Toolkit): void => {\n const mcpToolName = Object.entries(toolkit).find(([, tool]) =>\n isMcpToolkitTool(tool),\n )?.[0];\n if (!mcpToolName) return;\n\n throw new Error(\n `MCP toolkit entry \"${mcpToolName}\" requires AISDKToolkit. Use new AISDKToolkit({ toolkit }).tools(...) instead of generativeTools(...).`,\n );\n};\n\ntype AISDKToModelOutputOptions<TArgs, TResult> = Omit<\n Parameters<ToolModelOutputFunction<TArgs, TResult>>[0],\n \"output\"\n> & {\n output: TResult | ModelContentEnvelope<TResult>;\n};\n\nconst toAISDKToModelOutput =\n <TArgs, TResult>(toModelOutput?: ToolModelOutputFunction<TArgs, TResult>) =>\n async (options: AISDKToModelOutputOptions<TArgs, TResult>) => {\n const { result, modelContent } = unwrapModelContentEnvelope(options.output);\n\n if (modelContent !== undefined) {\n return toAISDKContent(modelContent);\n }\n\n if (!toModelOutput) {\n return toAISDKDefaultOutput(result);\n }\n\n const parts = await toModelOutput({\n ...options,\n output: result,\n });\n return toAISDKContent(parts);\n };\n\nconst toServerToolSet = (toolkit: ToolkitDefinition): ToolSet =>\n Object.fromEntries(\n Object.entries(toolkit)\n .filter(\n ([, t]) => t.type !== \"mcp\" && t.type !== \"provider\" && !t.disabled,\n )\n .map(([name, t]) => {\n const execute = t.execute;\n return [\n name,\n {\n ...(t.description !== undefined && { description: t.description }),\n inputSchema: parametersToInputSchema(t.parameters),\n toModelOutput: toAISDKToModelOutput(t.toModelOutput),\n ...(t.providerOptions && { providerOptions: t.providerOptions }),\n ...(execute && {\n execute: (\n args: unknown,\n callOptions: { toolCallId: string; abortSignal?: AbortSignal },\n ) =>\n execute(args as never, {\n toolCallId: callOptions.toolCallId,\n abortSignal: callOptions.abortSignal ?? neverAbort,\n human: humanNotSupported,\n }),\n }),\n },\n ];\n }),\n ) as ToolSet;\n\nconst toProviderToolSet = (toolkit: Toolkit): ToolSet =>\n Object.fromEntries(\n Object.entries(toolkit)\n .filter((entry): entry is [string, ProviderToolkitTool] =>\n isProviderToolkitTool(entry[1]),\n )\n .map(([name, t]) => [\n name,\n {\n type: \"provider\",\n id: t.providerId,\n args: t.args,\n ...(t.parameters && {\n inputSchema: parametersToInputSchema(t.parameters),\n }),\n ...(t.providerOptions && { providerOptions: t.providerOptions }),\n ...(t.supportsDeferredResults !== undefined && {\n supportsDeferredResults: t.supportsDeferredResults,\n }),\n },\n ]),\n ) as ToolSet;\n\ntype ProviderToolkitTool = Extract<Toolkit[string], { type: \"provider\" }>;\n\nconst isProviderToolkitTool = (\n tool: Toolkit[string],\n): tool is ProviderToolkitTool => tool.type === \"provider\" && !tool.disabled;\n"],"mappings":"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"}
package/dist/usage.d.ts CHANGED
@@ -12,6 +12,14 @@ interface TokenUsageExtractableMessage {
12
12
  metadata?: unknown;
13
13
  }
14
14
  declare function getThreadMessageTokenUsage(message: TokenUsageExtractableMessage | undefined): ThreadTokenUsage | undefined;
15
+ /**
16
+ * Reads token usage from the newest assistant message that reports any.
17
+ *
18
+ * A route attaches usage through the AI SDK's `messageMetadata` option. Because
19
+ * a thread message carries a fixed metadata shape, the converter moves every
20
+ * other key the route returns into `metadata.custom`, which is where this hook
21
+ * looks.
22
+ */
15
23
  declare function useThreadTokenUsage(): ThreadTokenUsage | undefined;
16
24
  //#endregion
17
25
  export { ThreadTokenUsage, TokenUsageExtractableMessage, getThreadMessageTokenUsage, useThreadTokenUsage };
@@ -1 +1 @@
1
- {"version":3,"file":"usage.d.ts","names":[],"sources":["../src/usage.ts"],"mappings":";KAIY;EACV;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA;;iBAuHc,2BACd,SAAS,2CACR;iBA8Ba,uBAAuB"}
1
+ {"version":3,"file":"usage.d.ts","names":[],"sources":["../src/usage.ts"],"mappings":";KAIY;EACV;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA;;iBAuHc,2BACd,SAAS,2CACR;;;;;;;;;iBAsCa,uBAAuB"}
package/dist/usage.js CHANGED
@@ -105,6 +105,14 @@ function findLatestMessageWithUsage(messages) {
105
105
  if (getThreadMessageTokenUsage(message)) return message;
106
106
  }
107
107
  }
108
+ /**
109
+ * Reads token usage from the newest assistant message that reports any.
110
+ *
111
+ * A route attaches usage through the AI SDK's `messageMetadata` option. Because
112
+ * a thread message carries a fixed metadata shape, the converter moves every
113
+ * other key the route returns into `metadata.custom`, which is where this hook
114
+ * looks.
115
+ */
108
116
  function useThreadTokenUsage() {
109
117
  const $ = c(2);
110
118
  const msg = useAuiState(_temp);
package/dist/usage.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"usage.js","names":["useMemo","useAuiState","ThreadTokenUsage","totalTokens","inputTokens","outputTokens","reasoningTokens","cachedInputTokens","TokenUsageExtractableMessage","role","metadata","UsageRecord","Record","USAGE_KEYS","const","asRecord","value","Array","isArray","undefined","asPositiveTokenCount","Number","isFinite","computeTotalTokens","usage","normalizeUsage","record","result","hasFields","key","count","outputTokenDetails","inputTokenDetails","cacheReadTokens","withComputedTotal","usageFromSteps","steps","sums","present","stepsWithUsage","stepsWithComputableTotal","step","stepTotal","getThreadMessageTokenUsage","message","topLevelUsage","customUsage","custom","findLatestMessageWithUsage","messages","idx","length","useThreadTokenUsage","$","_c","msg","_temp","t0","s","thread"],"sources":["../src/usage.ts"],"sourcesContent":["/// <reference types=\"@assistant-ui/core/react\" />\nimport { useMemo } from \"react\";\nimport { useAuiState } from \"@assistant-ui/store\";\n\nexport type ThreadTokenUsage = {\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n reasoningTokens?: number;\n cachedInputTokens?: number;\n};\n\nexport interface TokenUsageExtractableMessage {\n role?: string;\n metadata?: unknown;\n}\n\ntype UsageRecord = Record<string, unknown>;\n\nconst USAGE_KEYS = [\n \"inputTokens\",\n \"outputTokens\",\n \"reasoningTokens\",\n \"cachedInputTokens\",\n \"totalTokens\",\n] as const satisfies (keyof ThreadTokenUsage)[];\n\nfunction asRecord(value: unknown): UsageRecord | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value))\n return undefined;\n return value as UsageRecord;\n}\n\nfunction asPositiveTokenCount(value: unknown): number | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n return undefined;\n }\n return value;\n}\n\nfunction computeTotalTokens(usage: ThreadTokenUsage): number | undefined {\n if (usage.totalTokens !== undefined) return usage.totalTokens;\n if (usage.inputTokens !== undefined && usage.outputTokens !== undefined) {\n return usage.inputTokens + usage.outputTokens;\n }\n return undefined;\n}\n\nfunction normalizeUsage(value: unknown): ThreadTokenUsage | undefined {\n const record = asRecord(value);\n if (!record) return undefined;\n\n const result: ThreadTokenUsage = {};\n let hasFields = false;\n for (const key of USAGE_KEYS) {\n const count = asPositiveTokenCount(record[key]);\n if (count !== undefined) {\n result[key] = count;\n hasFields = true;\n }\n }\n // AI SDK v7 moved these under token detail objects; v6 kept them top-level.\n if (result.reasoningTokens === undefined) {\n const count = asPositiveTokenCount(\n asRecord(record.outputTokenDetails)?.reasoningTokens,\n );\n if (count !== undefined) {\n result.reasoningTokens = count;\n hasFields = true;\n }\n }\n if (result.cachedInputTokens === undefined) {\n const count = asPositiveTokenCount(\n asRecord(record.inputTokenDetails)?.cacheReadTokens,\n );\n if (count !== undefined) {\n result.cachedInputTokens = count;\n hasFields = true;\n }\n }\n return hasFields ? result : undefined;\n}\n\nfunction withComputedTotal(\n usage: ThreadTokenUsage,\n): ThreadTokenUsage | undefined {\n const totalTokens = computeTotalTokens(usage);\n return { ...usage, ...(totalTokens !== undefined && { totalTokens }) };\n}\n\nfunction usageFromSteps(value: unknown): ThreadTokenUsage | undefined {\n const steps = Array.isArray(value) ? value : [];\n\n const sums: Record<string, number> = {};\n const present: Record<string, boolean> = {};\n let stepsWithUsage = 0;\n let stepsWithComputableTotal = 0;\n\n for (const step of steps) {\n const usage = normalizeUsage(asRecord(step)?.usage);\n if (!usage) continue;\n stepsWithUsage++;\n\n const stepTotal = computeTotalTokens(usage);\n if (stepTotal !== undefined) {\n sums.totalTokens = (sums.totalTokens ?? 0) + stepTotal;\n stepsWithComputableTotal++;\n }\n\n for (const key of USAGE_KEYS) {\n if (key === \"totalTokens\") continue;\n if (usage[key] !== undefined) {\n sums[key] = (sums[key] ?? 0) + usage[key];\n present[key] = true;\n }\n }\n }\n\n if (stepsWithUsage === 0) return undefined;\n\n const result: ThreadTokenUsage = {};\n if (stepsWithComputableTotal === stepsWithUsage) {\n result.totalTokens = sums.totalTokens!;\n }\n for (const key of USAGE_KEYS) {\n if (key === \"totalTokens\") continue;\n if (present[key]) {\n result[key] = sums[key]!;\n }\n }\n return result;\n}\n\nexport function getThreadMessageTokenUsage(\n message: TokenUsageExtractableMessage | undefined,\n): ThreadTokenUsage | undefined {\n if (!message || message.role !== \"assistant\") return undefined;\n\n const metadata = asRecord(message.metadata);\n if (!metadata) return undefined;\n\n const topLevelUsage = normalizeUsage(metadata.usage);\n if (topLevelUsage) return withComputedTotal(topLevelUsage);\n\n const customUsage = normalizeUsage(asRecord(metadata.custom)?.usage);\n if (customUsage) return withComputedTotal(customUsage);\n\n return usageFromSteps(metadata.steps);\n}\n\nfunction findLatestMessageWithUsage(\n messages: readonly TokenUsageExtractableMessage[] | undefined,\n): TokenUsageExtractableMessage | undefined {\n if (!messages) return undefined;\n\n for (let idx = messages.length - 1; idx >= 0; idx -= 1) {\n const message = messages[idx];\n if (getThreadMessageTokenUsage(message)) {\n return message;\n }\n }\n\n return undefined;\n}\n\nexport function useThreadTokenUsage(): ThreadTokenUsage | undefined {\n const msg = useAuiState((s) => findLatestMessageWithUsage(s.thread.messages));\n return useMemo(() => getThreadMessageTokenUsage(msg), [msg]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"usage.js","names":["useMemo","useAuiState","ThreadTokenUsage","totalTokens","inputTokens","outputTokens","reasoningTokens","cachedInputTokens","TokenUsageExtractableMessage","role","metadata","UsageRecord","Record","USAGE_KEYS","const","asRecord","value","Array","isArray","undefined","asPositiveTokenCount","Number","isFinite","computeTotalTokens","usage","normalizeUsage","record","result","hasFields","key","count","outputTokenDetails","inputTokenDetails","cacheReadTokens","withComputedTotal","usageFromSteps","steps","sums","present","stepsWithUsage","stepsWithComputableTotal","step","stepTotal","getThreadMessageTokenUsage","message","topLevelUsage","customUsage","custom","findLatestMessageWithUsage","messages","idx","length","useThreadTokenUsage","$","_c","msg","_temp","t0","s","thread"],"sources":["../src/usage.ts"],"sourcesContent":["/// <reference types=\"@assistant-ui/core/react\" />\nimport { useMemo } from \"react\";\nimport { useAuiState } from \"@assistant-ui/store\";\n\nexport type ThreadTokenUsage = {\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n reasoningTokens?: number;\n cachedInputTokens?: number;\n};\n\nexport interface TokenUsageExtractableMessage {\n role?: string;\n metadata?: unknown;\n}\n\ntype UsageRecord = Record<string, unknown>;\n\nconst USAGE_KEYS = [\n \"inputTokens\",\n \"outputTokens\",\n \"reasoningTokens\",\n \"cachedInputTokens\",\n \"totalTokens\",\n] as const satisfies (keyof ThreadTokenUsage)[];\n\nfunction asRecord(value: unknown): UsageRecord | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value))\n return undefined;\n return value as UsageRecord;\n}\n\nfunction asPositiveTokenCount(value: unknown): number | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n return undefined;\n }\n return value;\n}\n\nfunction computeTotalTokens(usage: ThreadTokenUsage): number | undefined {\n if (usage.totalTokens !== undefined) return usage.totalTokens;\n if (usage.inputTokens !== undefined && usage.outputTokens !== undefined) {\n return usage.inputTokens + usage.outputTokens;\n }\n return undefined;\n}\n\nfunction normalizeUsage(value: unknown): ThreadTokenUsage | undefined {\n const record = asRecord(value);\n if (!record) return undefined;\n\n const result: ThreadTokenUsage = {};\n let hasFields = false;\n for (const key of USAGE_KEYS) {\n const count = asPositiveTokenCount(record[key]);\n if (count !== undefined) {\n result[key] = count;\n hasFields = true;\n }\n }\n // AI SDK v7 moved these under token detail objects; v6 kept them top-level.\n if (result.reasoningTokens === undefined) {\n const count = asPositiveTokenCount(\n asRecord(record.outputTokenDetails)?.reasoningTokens,\n );\n if (count !== undefined) {\n result.reasoningTokens = count;\n hasFields = true;\n }\n }\n if (result.cachedInputTokens === undefined) {\n const count = asPositiveTokenCount(\n asRecord(record.inputTokenDetails)?.cacheReadTokens,\n );\n if (count !== undefined) {\n result.cachedInputTokens = count;\n hasFields = true;\n }\n }\n return hasFields ? result : undefined;\n}\n\nfunction withComputedTotal(\n usage: ThreadTokenUsage,\n): ThreadTokenUsage | undefined {\n const totalTokens = computeTotalTokens(usage);\n return { ...usage, ...(totalTokens !== undefined && { totalTokens }) };\n}\n\nfunction usageFromSteps(value: unknown): ThreadTokenUsage | undefined {\n const steps = Array.isArray(value) ? value : [];\n\n const sums: Record<string, number> = {};\n const present: Record<string, boolean> = {};\n let stepsWithUsage = 0;\n let stepsWithComputableTotal = 0;\n\n for (const step of steps) {\n const usage = normalizeUsage(asRecord(step)?.usage);\n if (!usage) continue;\n stepsWithUsage++;\n\n const stepTotal = computeTotalTokens(usage);\n if (stepTotal !== undefined) {\n sums.totalTokens = (sums.totalTokens ?? 0) + stepTotal;\n stepsWithComputableTotal++;\n }\n\n for (const key of USAGE_KEYS) {\n if (key === \"totalTokens\") continue;\n if (usage[key] !== undefined) {\n sums[key] = (sums[key] ?? 0) + usage[key];\n present[key] = true;\n }\n }\n }\n\n if (stepsWithUsage === 0) return undefined;\n\n const result: ThreadTokenUsage = {};\n if (stepsWithComputableTotal === stepsWithUsage) {\n result.totalTokens = sums.totalTokens!;\n }\n for (const key of USAGE_KEYS) {\n if (key === \"totalTokens\") continue;\n if (present[key]) {\n result[key] = sums[key]!;\n }\n }\n return result;\n}\n\nexport function getThreadMessageTokenUsage(\n message: TokenUsageExtractableMessage | undefined,\n): ThreadTokenUsage | undefined {\n if (!message || message.role !== \"assistant\") return undefined;\n\n const metadata = asRecord(message.metadata);\n if (!metadata) return undefined;\n\n const topLevelUsage = normalizeUsage(metadata.usage);\n if (topLevelUsage) return withComputedTotal(topLevelUsage);\n\n const customUsage = normalizeUsage(asRecord(metadata.custom)?.usage);\n if (customUsage) return withComputedTotal(customUsage);\n\n return usageFromSteps(metadata.steps);\n}\n\nfunction findLatestMessageWithUsage(\n messages: readonly TokenUsageExtractableMessage[] | undefined,\n): TokenUsageExtractableMessage | undefined {\n if (!messages) return undefined;\n\n for (let idx = messages.length - 1; idx >= 0; idx -= 1) {\n const message = messages[idx];\n if (getThreadMessageTokenUsage(message)) {\n return message;\n }\n }\n\n return undefined;\n}\n\n/**\n * Reads token usage from the newest assistant message that reports any.\n *\n * A route attaches usage through the AI SDK's `messageMetadata` option. Because\n * a thread message carries a fixed metadata shape, the converter moves every\n * other key the route returns into `metadata.custom`, which is where this hook\n * looks.\n */\nexport function useThreadTokenUsage(): ThreadTokenUsage | undefined {\n const msg = useAuiState((s) => findLatestMessageWithUsage(s.thread.messages));\n return useMemo(() => getThreadMessageTokenUsage(msg), [msg]);\n}\n"],"mappings":";;;;AAmBA,MAAMa,aAAa;CACjB;CACA;CACA;CACA;CACA;AAAa;AAGf,SAASE,SAASC,OAAyC;CACzD,IAAI,CAACA,SAAS,OAAOA,UAAU,YAAYC,MAAMC,QAAQF,KAAK,GAC5D,OAAOG,KAAAA;CACT,OAAOH;AACT;AAEA,SAASI,qBAAqBJ,OAAoC;CAChE,IAAI,OAAOA,UAAU,YAAY,CAACK,OAAOC,SAASN,KAAK,KAAKA,QAAQ,GAClE;CAEF,OAAOA;AACT;AAEA,SAASO,mBAAmBC,OAA6C;CACvE,IAAIA,MAAMrB,gBAAgBgB,KAAAA,GAAW,OAAOK,MAAMrB;CAClD,IAAIqB,MAAMpB,gBAAgBe,KAAAA,KAAaK,MAAMnB,iBAAiBc,KAAAA,GAC5D,OAAOK,MAAMpB,cAAcoB,MAAMnB;AAGrC;AAEA,SAASoB,eAAeT,OAA8C;CACpE,MAAMU,SAASX,SAASC,KAAK;CAC7B,IAAI,CAACU,QAAQ,OAAOP,KAAAA;CAEpB,MAAMQ,SAA2B,CAAC;CAClC,IAAIC,YAAY;CAChB,KAAK,MAAMC,OAAOhB,YAAY;EAC5B,MAAMiB,QAAQV,qBAAqBM,OAAOG,IAAI;EAC9C,IAAIC,UAAUX,KAAAA,GAAW;GACvBQ,OAAOE,OAAOC;GACdF,YAAY;EACd;CACF;CAEA,IAAID,OAAOrB,oBAAoBa,KAAAA,GAAW;EACxC,MAAMW,QAAQV,qBACZL,SAASW,OAAOK,kBAAkB,CAAC,EAAEzB,eACvC;EACA,IAAIwB,UAAUX,KAAAA,GAAW;GACvBQ,OAAOrB,kBAAkBwB;GACzBF,YAAY;EACd;CACF;CACA,IAAID,OAAOpB,sBAAsBY,KAAAA,GAAW;EAC1C,MAAMW,QAAQV,qBACZL,SAASW,OAAOM,iBAAiB,CAAC,EAAEC,eACtC;EACA,IAAIH,UAAUX,KAAAA,GAAW;GACvBQ,OAAOpB,oBAAoBuB;GAC3BF,YAAY;EACd;CACF;CACA,OAAOA,YAAYD,SAASR,KAAAA;AAC9B;AAEA,SAASe,kBACPV,OAC8B;CAC9B,MAAMrB,cAAcoB,mBAAmBC,KAAK;CAC5C,OAAO;EAAE,GAAGA;EAAO,GAAIrB,gBAAgBgB,KAAAA,KAAa,EAAEhB,YAAY;CAAG;AACvE;AAEA,SAASgC,eAAenB,OAA8C;CACpE,MAAMoB,QAAQnB,MAAMC,QAAQF,KAAK,IAAIA,QAAQ,CAAA;CAE7C,MAAMqB,OAA+B,CAAC;CACtC,MAAMC,UAAmC,CAAC;CAC1C,IAAIC,iBAAiB;CACrB,IAAIC,2BAA2B;CAE/B,KAAK,MAAMC,QAAQL,OAAO;EACxB,MAAMZ,QAAQC,eAAeV,SAAS0B,IAAI,CAAC,EAAEjB,KAAK;EAClD,IAAI,CAACA,OAAO;EACZe;EAEA,MAAMG,YAAYnB,mBAAmBC,KAAK;EAC1C,IAAIkB,cAAcvB,KAAAA,GAAW;GAC3BkB,KAAKlC,eAAekC,KAAKlC,eAAe,KAAKuC;GAC7CF;EACF;EAEA,KAAK,MAAMX,OAAOhB,YAAY;GAC5B,IAAIgB,QAAQ,eAAe;GAC3B,IAAIL,MAAMK,SAASV,KAAAA,GAAW;IAC5BkB,KAAKR,QAAQQ,KAAKR,QAAQ,KAAKL,MAAMK;IACrCS,QAAQT,OAAO;GACjB;EACF;CACF;CAEA,IAAIU,mBAAmB,GAAG,OAAOpB,KAAAA;CAEjC,MAAMQ,SAA2B,CAAC;CAClC,IAAIa,6BAA6BD,gBAC/BZ,OAAOxB,cAAckC,KAAKlC;CAE5B,KAAK,MAAM0B,OAAOhB,YAAY;EAC5B,IAAIgB,QAAQ,eAAe;EAC3B,IAAIS,QAAQT,MACVF,OAAOE,OAAOQ,KAAKR;CAEvB;CACA,OAAOF;AACT;AAEA,SAAgBgB,2BACdC,SAC8B;CAC9B,IAAI,CAACA,WAAWA,QAAQnC,SAAS,aAAa,OAAOU,KAAAA;CAErD,MAAMT,WAAWK,SAAS6B,QAAQlC,QAAQ;CAC1C,IAAI,CAACA,UAAU,OAAOS,KAAAA;CAEtB,MAAM0B,gBAAgBpB,eAAef,SAASc,KAAK;CACnD,IAAIqB,eAAe,OAAOX,kBAAkBW,aAAa;CAEzD,MAAMC,cAAcrB,eAAeV,SAASL,SAASqC,MAAM,CAAC,EAAEvB,KAAK;CACnE,IAAIsB,aAAa,OAAOZ,kBAAkBY,WAAW;CAErD,OAAOX,eAAezB,SAAS0B,KAAK;AACtC;AAEA,SAASY,2BACPC,UAC0C;CAC1C,IAAI,CAACA,UAAU,OAAO9B,KAAAA;CAEtB,KAAK,IAAI+B,MAAMD,SAASE,SAAS,GAAGD,OAAO,GAAGA,OAAO,GAAG;EACtD,MAAMN,UAAUK,SAASC;EACzB,IAAIP,2BAA2BC,OAAO,GACpC,OAAOA;CAEX;AAGF;;;;;;;;;AAUA,SAAOQ,sBAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CACL,MAAAC,MAAYtD,YAAYuD,KAAoD;CAAE,IAAAC;CAAA,IAAAJ,EAAA,OAAAE,KAAA;EACzDE,KAAAd,2BAA2BY,GAAG;EAACF,EAAA,KAAAE;EAAAF,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAAA,OAA/BI;AAA+B;AAF/C,SAAAD,MAAAE,GAAA;CAAA,OAC0BV,2BAA2BU,EAACC,OAAOV,QAAS;AAAC"}