@assistant-ui/react-ag-ui 0.0.50 → 0.0.52

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":"conversions.js","names":[],"sources":["../../../src/runtime/adapter/conversions.ts"],"sourcesContent":["\"use client\";\n\nimport type { InputContent } from \"@ag-ui/client\";\nimport type {\n ThreadMessageLike as CoreThreadMessageLike,\n ToolCallMessagePartMcpMetadata,\n ToolModelContentPart,\n} from \"@assistant-ui/core\";\nimport {\n getAutoStatus,\n httpUrlPattern,\n parseDataUrl,\n} from \"@assistant-ui/core/internal\";\nimport { type Tool, toToolsJSONSchema } from \"assistant-stream\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport {\n AG_UI_METADATA_NAMESPACE,\n A2UI_SURFACE_ACTIVITY_TYPE,\n type AgUiCustomMetadata,\n} from \"./run-aggregator\";\nimport {\n applyA2uiOperations,\n convertSurfaceToUISpec,\n type A2uiState,\n type A2uiSurfaceState,\n} from \"@assistant-ui/react-generative-ui/a2ui\";\nimport type { AgUiInterrupt } from \"../types\";\nimport {\n parseMcpToolCallResult,\n readMcpAppResourceUri,\n} from \"../mcp-tool-result\";\n\nexport type { InputContent };\n\ntype AttachmentLike = {\n name?: string | undefined;\n contentType?: string | undefined;\n content?: readonly unknown[] | undefined;\n};\n\ntype ThreadMessageLike = {\n id: string;\n role: string;\n content: unknown;\n name?: string;\n toolCallId?: string;\n error?: string;\n attachments?: readonly AttachmentLike[];\n};\n\ntype AgUiToolCall = {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n};\n\nexport type AgUiMessage =\n | {\n id: string;\n role: string;\n content: string | InputContent[];\n name?: string;\n toolCalls?: AgUiToolCall[];\n }\n | {\n id: string;\n role: \"tool\";\n content: string;\n toolCallId: string;\n error?: string;\n };\n\ntype ToolCallPart = {\n type: \"tool-call\";\n toolCallId?: string;\n toolName: string;\n argsText?: string;\n args?: ReadonlyJSONObject;\n result?: unknown;\n isError?: boolean;\n modelContent?: readonly ToolModelContentPart[];\n unstable_toolMessageId?: string;\n mcp?: ToolCallMessagePartMcpMetadata;\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst getString = (record: Record<string, unknown>, key: string) => {\n const value = record[key];\n return typeof value === \"string\" ? value : undefined;\n};\n\nconst getToolCallId = (record: Record<string, unknown>) =>\n getString(record, \"toolCallId\") ?? getString(record, \"tool_call_id\");\n\nfunction parseJSONText(value: string): unknown {\n if (!value) return value;\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n}\n\nfunction generateId(): string {\n return (\n (globalThis.crypto as { randomUUID?: () => string })?.randomUUID?.() ??\n Math.random().toString(36).slice(2)\n );\n}\n\nfunction normalizeToolCall(part: ToolCallPart): {\n id: string;\n call: AgUiToolCall;\n} {\n const id = part.toolCallId ?? generateId();\n const argsText =\n typeof part.argsText === \"string\"\n ? part.argsText\n : JSON.stringify(part.args ?? {});\n\n return {\n id,\n call: {\n id,\n type: \"function\",\n function: {\n name: part.toolName ?? \"tool\",\n arguments: argsText,\n },\n },\n };\n}\n\nfunction extractText(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n\n return content\n .filter(\n (part): part is { type: \"text\"; text: string } =>\n part?.type === \"text\" && typeof part?.text === \"string\",\n )\n .map((part) => part.text)\n .join(\"\\n\");\n}\n\ntype InputContentSource =\n | { type: \"data\"; value: string; mimeType: string }\n | { type: \"url\"; value: string; mimeType?: string };\n\ntype MediaInputType = \"image\" | \"audio\" | \"video\" | \"document\";\n\nfunction mediaTypeForMime(mimeType: string | undefined): MediaInputType {\n if (mimeType?.startsWith(\"image/\")) return \"image\";\n if (mimeType?.startsWith(\"audio/\")) return \"audio\";\n if (mimeType?.startsWith(\"video/\")) return \"video\";\n return \"document\";\n}\n\n// Build an AG-UI multimodal source from a data URL, raw base64 payload, or an\n// http(s) URL. A `url` source may omit the mime type; a `data` source always\n// resolves one (falling back to application/octet-stream). An explicit\n// `sourceType: \"url\"` on the part forces the url leg for non-http references.\nfunction buildInputSource(\n value: string,\n declaredMimeType: string | undefined,\n sourceType?: string,\n): InputContentSource {\n if (sourceType === \"url\" || httpUrlPattern.test(value)) {\n return declaredMimeType !== undefined\n ? { type: \"url\", value, mimeType: declaredMimeType }\n : { type: \"url\", value };\n }\n const parsed = parseDataUrl(value);\n return {\n type: \"data\",\n value: parsed?.data ?? value,\n mimeType:\n parsed?.mimeType ?? declaredMimeType ?? \"application/octet-stream\",\n };\n}\n\nfunction toInputContent(\n part: unknown,\n fallbackMimeType: string | undefined,\n): InputContent | null {\n if (!isObject(part)) return null;\n const type = getString(part, \"type\");\n\n if (type === \"text\") {\n const text = getString(part, \"text\");\n if (text === undefined) return null;\n return { type: \"text\", text };\n }\n\n if (type === \"image\") {\n const image = getString(part, \"image\");\n if (image === undefined) return null;\n return { type: \"image\", source: buildInputSource(image, fallbackMimeType) };\n }\n\n if (type === \"file\") {\n const data = getString(part, \"data\");\n if (data === undefined) return null;\n const declaredMimeType = getString(part, \"mimeType\") || fallbackMimeType;\n const filename = getString(part, \"filename\");\n const source = buildInputSource(\n data,\n declaredMimeType,\n getString(part, \"sourceType\"),\n );\n const metadata = filename !== undefined ? { filename } : undefined;\n switch (mediaTypeForMime(source.mimeType)) {\n case \"image\":\n return { type: \"image\", source, ...(metadata && { metadata }) };\n case \"audio\":\n return { type: \"audio\", source, ...(metadata && { metadata }) };\n case \"video\":\n return { type: \"video\", source, ...(metadata && { metadata }) };\n default:\n return { type: \"document\", source, ...(metadata && { metadata }) };\n }\n }\n\n if (type === \"audio\") {\n const audio = part.audio;\n if (!isObject(audio)) return null;\n const data = getString(audio, \"data\");\n const format = getString(audio, \"format\");\n if (data === undefined || format === undefined) return null;\n return {\n type: \"audio\",\n source: buildInputSource(\n parseDataUrl(data)?.data ?? data,\n `audio/${format}`,\n ),\n };\n }\n\n return null;\n}\n\ntype SnapshotAttachment = NonNullable<\n CoreThreadMessageLike[\"attachments\"]\n>[number];\n\nconst mediaInputTypes = new Set([\"image\", \"audio\", \"video\", \"document\"]);\n\n// Inverse of buildInputSource.\nfunction inputSourceToString(\n value: unknown,\n): { value: string; mimeType?: string; isUrl?: boolean } | null {\n if (!isObject(value)) return null;\n const sourceValue = getString(value, \"value\");\n if (sourceValue === undefined) return null;\n const mimeType = getString(value, \"mimeType\");\n const type = getString(value, \"type\");\n if (type === \"url\") {\n return {\n value: sourceValue,\n isUrl: true,\n ...(mimeType !== undefined && { mimeType }),\n };\n }\n if (type === \"data\") {\n const resolvedMimeType = mimeType ?? \"application/octet-stream\";\n return {\n value: `data:${resolvedMimeType};base64,${sourceValue}`,\n mimeType: resolvedMimeType,\n };\n }\n return null;\n}\n\n// Mirrors @ag-ui/client's BackwardCompatibility_0_0_47 middleware.\nfunction upgradeBinaryInputPart(\n part: Record<string, unknown>,\n): Record<string, unknown> | null {\n const mimeType = getString(part, \"mimeType\");\n if (mimeType === undefined) return null;\n const data = getString(part, \"data\");\n const url = getString(part, \"url\");\n const source = data\n ? { type: \"data\", value: data, mimeType }\n : url\n ? { type: \"url\", value: url, mimeType }\n : null;\n if (!source) return null;\n const filename = getString(part, \"filename\");\n return {\n type: mediaTypeForMime(mimeType),\n source,\n ...(filename && { metadata: { filename } }),\n };\n}\n\nfunction toSnapshotAttachments(content: unknown): SnapshotAttachment[] {\n if (!Array.isArray(content)) return [];\n\n const attachments: SnapshotAttachment[] = [];\n for (const rawPart of content) {\n if (!isObject(rawPart)) continue;\n const part =\n getString(rawPart, \"type\") === \"binary\"\n ? upgradeBinaryInputPart(rawPart)\n : rawPart;\n if (!part) continue;\n const type = getString(part, \"type\");\n if (type === undefined || !mediaInputTypes.has(type)) continue;\n const source = inputSourceToString(part.source);\n if (!source) continue;\n\n const filename = isObject(part.metadata)\n ? getString(part.metadata, \"filename\")\n : undefined;\n const id = attachments.length.toString();\n\n if (type === \"image\") {\n attachments.push({\n id,\n type: \"image\",\n name: filename ?? \"image\",\n ...(source.mimeType !== undefined && { contentType: source.mimeType }),\n status: { type: \"complete\" },\n content: [\n {\n type: \"image\",\n image: source.value,\n ...(filename !== undefined && { filename }),\n },\n ],\n });\n continue;\n }\n\n const mimeType = source.mimeType ?? \"application/octet-stream\";\n attachments.push({\n id,\n type: type === \"document\" ? \"document\" : \"file\",\n name: filename ?? \"file\",\n contentType: mimeType,\n status: { type: \"complete\" },\n content: [\n {\n type: \"file\",\n data: source.value,\n mimeType,\n ...(source.isUrl && { sourceType: \"url\" as const }),\n ...(filename !== undefined && { filename }),\n },\n ],\n });\n }\n return attachments;\n}\n\nfunction buildUserContent(message: ThreadMessageLike): string | InputContent[] {\n const contentParts = Array.isArray(message.content) ? message.content : [];\n\n const attachments = message.attachments ?? [];\n\n const converted: InputContent[] = [];\n\n // Promote string-form content to a leading text part so it survives when\n // non-text attachments are present (fromAgUiMessages emits string content).\n if (typeof message.content === \"string\" && message.content.length > 0) {\n converted.push({ type: \"text\", text: message.content });\n }\n\n for (const part of contentParts) {\n const input = toInputContent(part, undefined);\n if (input) converted.push(input);\n }\n for (const attachment of attachments) {\n if (!isObject(attachment)) continue;\n const attachmentContent = attachment.content;\n if (!Array.isArray(attachmentContent)) continue;\n const fallbackMime = getString(attachment, \"contentType\");\n for (const part of attachmentContent) {\n const input = toInputContent(part, fallbackMime);\n if (input) converted.push(input);\n }\n }\n\n const hasNonText = converted.some((part) => part.type !== \"text\");\n if (hasNonText) return converted;\n\n // All-text path: collapse to plain string. Join text parts collected from\n // both content and attachments so attachment-sourced text is not dropped.\n if (converted.length === 0) return extractText(message.content);\n return converted\n .filter(\n (part): part is { type: \"text\"; text: string } => part.type === \"text\",\n )\n .map((part) => part.text)\n .join(\"\\n\");\n}\n\nfunction toToolCallPart(value: unknown): ToolCallPart | null {\n if (!isObject(value)) return null;\n const rawFunction = isObject(value.function) ? value.function : null;\n const toolCallId = getString(value, \"toolCallId\") ?? getString(value, \"id\");\n const toolName =\n getString(value, \"toolName\") ??\n getString(value, \"name\") ??\n (rawFunction ? getString(rawFunction, \"name\") : undefined) ??\n \"tool\";\n const argsText =\n getString(value, \"argsText\") ??\n getString(value, \"arguments\") ??\n (rawFunction ? getString(rawFunction, \"arguments\") : undefined);\n\n const parsedArgs =\n typeof argsText === \"string\" ? parseJSONText(argsText) : undefined;\n const args =\n isObject(parsedArgs) && !Array.isArray(parsedArgs)\n ? (parsedArgs as ReadonlyJSONObject)\n : isObject(value.args) && !Array.isArray(value.args)\n ? (value.args as ReadonlyJSONObject)\n : undefined;\n\n const part: ToolCallPart = {\n type: \"tool-call\",\n ...(toolCallId !== undefined ? { toolCallId } : {}),\n toolName,\n argsText: argsText ?? JSON.stringify(args ?? {}),\n ...(args !== undefined ? { args } : {}),\n };\n\n if (value.type === \"tool-call\") {\n const result = value.result;\n const isError = value.isError;\n if (result !== undefined) part.result = result;\n if (typeof isError === \"boolean\") part.isError = isError;\n }\n\n return part;\n}\n\nfunction extractAssistantToolCalls(\n message: Record<string, unknown>,\n): ToolCallPart[] {\n const parts: ToolCallPart[] = [];\n const seenToolCallIds = new Set<string>();\n const pushPart = (part: ToolCallPart | null) => {\n if (!part) return;\n const id = part.toolCallId ?? generateId();\n if (seenToolCallIds.has(id)) return;\n seenToolCallIds.add(id);\n parts.push({\n ...part,\n toolCallId: id,\n });\n };\n\n const content = message.content;\n if (Array.isArray(content)) {\n for (const part of content) {\n if (isObject(part) && part.type === \"tool-call\") {\n pushPart(toToolCallPart(part));\n }\n }\n }\n\n const toolCalls = Array.isArray(message.toolCalls)\n ? message.toolCalls\n : Array.isArray(message.tool_calls)\n ? message.tool_calls\n : [];\n for (const call of toolCalls) {\n pushPart(toToolCallPart(call));\n }\n\n return parts;\n}\n\nfunction validateInterrupts(value: unknown): AgUiInterrupt[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const valid = value.filter(\n (entry): entry is AgUiInterrupt =>\n isObject(entry) &&\n typeof entry.id === \"string\" &&\n typeof entry.reason === \"string\",\n );\n return valid.length > 0 ? valid : undefined;\n}\n\nfunction readPersistedInterrupts(\n metadata: unknown,\n): AgUiInterrupt[] | undefined {\n if (!isObject(metadata)) return undefined;\n const custom = metadata.custom;\n if (!isObject(custom)) return undefined;\n const namespaced = custom[AG_UI_METADATA_NAMESPACE];\n if (!isObject(namespaced)) return undefined;\n return validateInterrupts(namespaced.interrupts);\n}\n\nfunction toAssistantSnapshotMessage(\n rawMessage: Record<string, unknown>,\n): CoreThreadMessageLike {\n const text = extractText(rawMessage.content);\n const toolCallParts = extractAssistantToolCalls(rawMessage);\n const assistantContent = [\n ...(text.length > 0 ? [{ type: \"text\" as const, text }] : []),\n ...toolCallParts,\n ];\n const messageName = getString(rawMessage, \"name\");\n const interrupts = readPersistedInterrupts(rawMessage.metadata);\n return {\n id: getString(rawMessage, \"id\") ?? generateId(),\n role: \"assistant\",\n content: assistantContent.length > 0 ? assistantContent : \"\",\n ...(messageName !== undefined ? { name: messageName } : {}),\n ...(interrupts\n ? {\n metadata: {\n custom: {\n [AG_UI_METADATA_NAMESPACE]: {\n interrupts,\n } satisfies AgUiCustomMetadata,\n },\n },\n }\n : {}),\n };\n}\n\nfunction toUserOrSystemSnapshotMessage(\n role: \"user\" | \"system\",\n rawMessage: Record<string, unknown>,\n): CoreThreadMessageLike {\n const messageName = getString(rawMessage, \"name\");\n const attachments =\n role === \"user\" ? toSnapshotAttachments(rawMessage.content) : [];\n return {\n id: getString(rawMessage, \"id\") ?? generateId(),\n role,\n content: extractText(rawMessage.content),\n ...(messageName !== undefined ? { name: messageName } : {}),\n ...(attachments.length > 0 ? { attachments } : {}),\n };\n}\n\n// Rebuilds the a2ui:<surfaceId> \"present\" tool-call parts for one owning\n// assistant message from a bucket of rebuilt surface state, mirroring the\n// live RunAggregator.synthesizeA2uiToolCalls shape. Non-a2ui parts (text,\n// other tool calls) are preserved; existing a2ui parts are replaced wholesale\n// so create/update/delete within the bucket all converge on the rebuilt set.\nfunction attachA2uiSurfaces(\n message: CoreThreadMessageLike,\n state: A2uiState,\n): CoreThreadMessageLike {\n const a2uiParts: ToolCallPart[] = [];\n for (const [surfaceId, surface] of state) {\n const { spec } = convertSurfaceToUISpec(surface);\n if (!spec) continue;\n a2uiParts.push({\n type: \"tool-call\",\n toolCallId: `a2ui:${surfaceId}`,\n toolName: \"present\",\n args: spec as unknown as ReadonlyJSONObject,\n argsText: JSON.stringify(spec),\n result: {},\n });\n }\n\n const content = Array.isArray(message.content) ? message.content : [];\n const preserved = content.filter(\n (part) =>\n !(\n isObject(part) &&\n part.type === \"tool-call\" &&\n typeof part.toolCallId === \"string\" &&\n part.toolCallId.startsWith(\"a2ui:\")\n ),\n );\n return { ...message, content: [...preserved, ...a2uiParts] };\n}\n\nexport type FromAgUiMessagesOptions = {\n /**\n * Whether to convert `reasoning` messages into visible reasoning parts.\n * Matches the `showThinking` option of `useAgUiRuntime`. Defaults to `true`.\n */\n showThinking?: boolean;\n};\n\nexport function fromAgUiMessages(\n messages: readonly unknown[],\n options?: FromAgUiMessagesOptions,\n): CoreThreadMessageLike[] {\n const showThinking = options?.showThinking ?? true;\n const converted: CoreThreadMessageLike[] = [];\n const a2uiBuckets = new Map<string, A2uiState>();\n const a2uiBucketOwnerIndices = new Map<string, number>();\n\n for (const rawMessage of messages) {\n if (!isObject(rawMessage)) continue;\n const role = getString(rawMessage, \"role\");\n if (!role) continue;\n\n if (role === \"tool\") {\n const toolCallId = getToolCallId(rawMessage) ?? `tool-${generateId()}`;\n const toolMessageId = getString(rawMessage, \"id\");\n const modelContent = extractText(rawMessage.content);\n const mcpResult = parseMcpToolCallResult(rawMessage, modelContent);\n const mcpModelContent = mcpResult\n ? [{ type: \"text\" as const, text: modelContent }]\n : undefined;\n const result =\n mcpResult ??\n (rawMessage.result !== undefined\n ? rawMessage.result\n : typeof rawMessage.content === \"string\"\n ? parseJSONText(rawMessage.content)\n : rawMessage.content);\n const isError =\n typeof rawMessage.error === \"string\" ||\n rawMessage.isError === true ||\n rawMessage.status === \"error\"\n ? true\n : rawMessage.isError === false\n ? false\n : undefined;\n const mcpAppUri = readMcpAppResourceUri(mcpResult?._meta);\n const mcpApp =\n mcpAppUri !== undefined ? { resourceUri: mcpAppUri } : undefined;\n\n let updated = false;\n for (\n let messageIndex = converted.length - 1;\n messageIndex >= 0 && !updated;\n messageIndex--\n ) {\n const message = converted[messageIndex];\n if (\n !message ||\n message.role !== \"assistant\" ||\n !Array.isArray(message.content)\n )\n continue;\n\n for (\n let partIndex = message.content.length - 1;\n partIndex >= 0;\n partIndex--\n ) {\n const part = message.content[partIndex];\n if (!isObject(part) || part.type !== \"tool-call\") continue;\n if (getString(part, \"toolCallId\") !== toolCallId) continue;\n\n const updatedPart: ToolCallPart = {\n ...(part as ToolCallPart),\n result,\n ...(mcpModelContent ? { modelContent: mcpModelContent } : {}),\n ...(isError !== undefined ? { isError } : {}),\n ...(toolMessageId !== undefined\n ? { unstable_toolMessageId: toolMessageId }\n : {}),\n ...(mcpApp ? { mcp: { app: mcpApp } } : {}),\n };\n const updatedContent = message.content.map((contentPart, index) =>\n index === partIndex ? updatedPart : contentPart,\n );\n converted[messageIndex] = { ...message, content: updatedContent };\n updated = true;\n break;\n }\n }\n\n if (updated) {\n continue;\n }\n\n const id = toolMessageId ?? toolCallId;\n const toolName =\n getString(rawMessage, \"name\") ??\n getString(rawMessage, \"toolName\") ??\n \"tool\";\n converted.push({\n id: `${id}:assistant`,\n role: \"assistant\",\n content: [\n {\n type: \"tool-call\",\n toolCallId,\n toolName,\n args: {},\n argsText: \"{}\",\n result,\n ...(mcpModelContent ? { modelContent: mcpModelContent } : {}),\n ...(isError !== undefined ? { isError } : {}),\n ...(toolMessageId !== undefined\n ? { unstable_toolMessageId: toolMessageId }\n : {}),\n ...(mcpApp ? { mcp: { app: mcpApp } } : {}),\n },\n ],\n });\n continue;\n }\n\n if (role === \"activity\") {\n // Only a2ui-surface activity messages have an assistant-part equivalent\n // to rehydrate; other activity types still have no surface to repaint.\n const activityType = getString(rawMessage, \"activityType\");\n if (activityType !== A2UI_SURFACE_ACTIVITY_TYPE) continue;\n const activityContent = isObject(rawMessage.content)\n ? (rawMessage.content as Record<string, unknown>)\n : null;\n const operations = activityContent?.[\"a2ui_operations\"];\n if (!Array.isArray(operations)) continue;\n\n let ownerIndex = -1;\n for (let i = converted.length - 1; i >= 0; i--) {\n const candidate = converted[i];\n if (candidate && candidate.role === \"assistant\") {\n ownerIndex = i;\n break;\n }\n }\n if (ownerIndex === -1) continue;\n\n const owner = converted[ownerIndex]!;\n const bucketKey = getString(rawMessage, \"id\") ?? \"a2ui:anonymous\";\n const { state } = applyA2uiOperations(new Map(), operations);\n a2uiBuckets.delete(bucketKey);\n a2uiBucketOwnerIndices.delete(bucketKey);\n a2uiBuckets.set(bucketKey, state);\n a2uiBucketOwnerIndices.set(bucketKey, ownerIndex);\n\n const ownerState = new Map<string, A2uiSurfaceState>();\n for (const [candidateBucketKey, candidateState] of a2uiBuckets) {\n if (a2uiBucketOwnerIndices.get(candidateBucketKey) !== ownerIndex)\n continue;\n for (const [surfaceId, surface] of candidateState) {\n ownerState.set(surfaceId, surface);\n }\n }\n converted[ownerIndex] = attachA2uiSurfaces(owner, ownerState);\n continue;\n }\n\n if (role === \"assistant\") {\n converted.push(toAssistantSnapshotMessage(rawMessage));\n continue;\n }\n\n if (role === \"reasoning\") {\n // Gate on showThinking so a cold reload matches the live run: the\n // aggregator never stores reasoning parts when showThinking is false.\n if (!showThinking) continue;\n const text = extractText(rawMessage.content);\n if (text.trim().length === 0) continue;\n converted.push({\n id: getString(rawMessage, \"id\") ?? generateId(),\n role: \"assistant\",\n content: [{ type: \"reasoning\", text }],\n });\n continue;\n }\n\n if (role === \"user\" || role === \"system\") {\n converted.push(toUserOrSystemSnapshotMessage(role, rawMessage));\n }\n }\n\n for (let i = 0; i < converted.length; i++) {\n const message = converted[i]!;\n if (message.role !== \"assistant\") continue;\n\n const hasInterrupt =\n readPersistedInterrupts(message.metadata) !== undefined;\n const hasPendingToolCall =\n Array.isArray(message.content) &&\n message.content.some(\n (part) =>\n isObject(part) &&\n part.type === \"tool-call\" &&\n part.result === undefined,\n );\n\n if (hasInterrupt || hasPendingToolCall) {\n converted[i] = {\n ...message,\n status: getAutoStatus(\n false,\n false,\n hasInterrupt,\n hasPendingToolCall,\n undefined,\n ),\n };\n }\n }\n\n return converted;\n}\n\nfunction convertAssistantMessage(\n message: ThreadMessageLike,\n converted: AgUiMessage[],\n): void {\n const content = extractText(message.content);\n const contentArray = Array.isArray(message.content) ? message.content : [];\n\n const toolCallParts = contentArray.filter(\n (part): part is ToolCallPart =>\n part?.type === \"tool-call\" &&\n !(\n typeof part.toolCallId === \"string\" &&\n part.toolCallId.startsWith(\"a2ui:\")\n ),\n );\n\n const toolCalls = toolCallParts.map((part) => ({\n ...normalizeToolCall(part),\n part,\n }));\n\n // Drop assistant messages with no text or tool calls (e.g. an imported\n // reasoning-only entry) so they are not re-sent as a blank assistant turn.\n if (content.length === 0 && toolCalls.length === 0) {\n return;\n }\n\n const assistantMessage: AgUiMessage = {\n id: message.id,\n role: \"assistant\",\n content,\n };\n if (message.name) {\n assistantMessage.name = message.name;\n }\n if (toolCalls.length > 0) {\n assistantMessage.toolCalls = toolCalls.map((entry) => entry.call);\n }\n converted.push(assistantMessage);\n\n for (const { id: toolCallId, part } of toolCalls) {\n if (part.result === undefined) continue;\n\n const resultContent =\n part.modelContent !== undefined\n ? extractText(part.modelContent)\n : typeof part.result === \"string\"\n ? part.result\n : JSON.stringify(part.result);\n\n const toolMessage: AgUiMessage = {\n id: part.unstable_toolMessageId ?? `${toolCallId}:tool`,\n role: \"tool\",\n content: resultContent,\n toolCallId,\n };\n if (part.isError) {\n toolMessage.error = resultContent;\n }\n converted.push(toolMessage);\n }\n}\n\nfunction convertToolMessage(\n message: ThreadMessageLike,\n converted: AgUiMessage[],\n): void {\n const content = extractText(message.content);\n const toolCallId = message.toolCallId ?? generateId();\n\n const toolMessage: AgUiMessage = {\n id: message.id,\n role: \"tool\",\n content,\n toolCallId,\n };\n if (typeof message.error === \"string\") {\n toolMessage.error = message.error;\n }\n converted.push(toolMessage);\n}\n\nexport function toAgUiMessages(\n messages: readonly ThreadMessageLike[],\n): AgUiMessage[] {\n const converted: AgUiMessage[] = [];\n\n for (const message of messages) {\n if (message.role === \"assistant\") {\n convertAssistantMessage(message, converted);\n continue;\n }\n\n if (message.role === \"tool\") {\n convertToolMessage(message, converted);\n continue;\n }\n\n const genericMessage: AgUiMessage = {\n id: message.id,\n role: message.role,\n content:\n message.role === \"user\"\n ? buildUserContent(message)\n : extractText(message.content),\n };\n if (message.name) {\n genericMessage.name = message.name;\n }\n converted.push(genericMessage);\n }\n\n return converted;\n}\n\ntype AgUiTool = {\n name: string;\n description: string | undefined;\n parameters: unknown;\n};\n\nexport function toAgUiTools(\n tools: Record<string, Tool> | undefined,\n): AgUiTool[] {\n if (!tools) return [];\n\n const toolsSchema = toToolsJSONSchema(tools);\n return Object.entries(toolsSchema).map(([name, tool]) => ({\n name,\n description: tool.description,\n parameters: tool.parameters,\n }));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"conversions.js","names":[],"sources":["../../../src/runtime/adapter/conversions.ts"],"sourcesContent":["\"use client\";\n\nimport type { InputContent, RunAgentParameters } from \"@ag-ui/client\";\nimport type {\n ThreadMessageLike as CoreThreadMessageLike,\n ToolCallMessagePartMcpMetadata,\n ToolModelContentPart,\n} from \"@assistant-ui/core\";\nimport {\n getAutoStatus,\n httpUrlPattern,\n parseDataUrl,\n} from \"@assistant-ui/core/internal\";\nimport { type Tool, toToolsJSONSchema } from \"assistant-stream\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport {\n AG_UI_METADATA_NAMESPACE,\n A2UI_SURFACE_ACTIVITY_TYPE,\n type AgUiCustomMetadata,\n} from \"./run-aggregator\";\nimport {\n applyA2uiOperations,\n convertSurfaceToUISpec,\n type A2uiState,\n type A2uiSurfaceState,\n} from \"@assistant-ui/react-generative-ui/a2ui\";\nimport type { AgUiInterrupt } from \"../types\";\nimport {\n parseMcpToolCallResult,\n readMcpAppResourceUri,\n} from \"../mcp-tool-result\";\n\nexport type { InputContent };\n\ntype AttachmentLike = {\n name?: string | undefined;\n contentType?: string | undefined;\n content?: readonly unknown[] | undefined;\n};\n\ntype ThreadMessageLike = {\n id: string;\n role: string;\n content: unknown;\n name?: string;\n toolCallId?: string;\n error?: string;\n attachments?: readonly AttachmentLike[];\n};\n\ntype AgUiToolCall = {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n};\n\nexport type AgUiMessage =\n | {\n id: string;\n role: string;\n content: string | InputContent[];\n name?: string;\n toolCalls?: AgUiToolCall[];\n }\n | {\n id: string;\n role: \"tool\";\n content: string;\n toolCallId: string;\n error?: string;\n };\n\ntype ToolCallPart = {\n type: \"tool-call\";\n toolCallId?: string;\n toolName: string;\n argsText?: string;\n args?: ReadonlyJSONObject;\n result?: unknown;\n isError?: boolean;\n modelContent?: readonly ToolModelContentPart[];\n unstable_toolMessageId?: string;\n mcp?: ToolCallMessagePartMcpMetadata;\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst getString = (record: Record<string, unknown>, key: string) => {\n const value = record[key];\n return typeof value === \"string\" ? value : undefined;\n};\n\nconst getToolCallId = (record: Record<string, unknown>) =>\n getString(record, \"toolCallId\") ?? getString(record, \"tool_call_id\");\n\nfunction parseJSONText(value: string): unknown {\n if (!value) return value;\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n}\n\nfunction generateId(): string {\n return (\n (globalThis.crypto as { randomUUID?: () => string })?.randomUUID?.() ??\n Math.random().toString(36).slice(2)\n );\n}\n\nfunction normalizeToolCall(part: ToolCallPart): {\n id: string;\n call: AgUiToolCall;\n} {\n const id = part.toolCallId ?? generateId();\n const argsText =\n typeof part.argsText === \"string\"\n ? part.argsText\n : JSON.stringify(part.args ?? {});\n\n return {\n id,\n call: {\n id,\n type: \"function\",\n function: {\n name: part.toolName ?? \"tool\",\n arguments: argsText,\n },\n },\n };\n}\n\nfunction extractText(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n\n return content\n .filter(\n (part): part is { type: \"text\"; text: string } =>\n part?.type === \"text\" && typeof part?.text === \"string\",\n )\n .map((part) => part.text)\n .join(\"\\n\");\n}\n\ntype InputContentSource =\n | { type: \"data\"; value: string; mimeType: string }\n | { type: \"url\"; value: string; mimeType?: string };\n\ntype MediaInputType = \"image\" | \"audio\" | \"video\" | \"document\";\n\nfunction mediaTypeForMime(mimeType: string | undefined): MediaInputType {\n if (mimeType?.startsWith(\"image/\")) return \"image\";\n if (mimeType?.startsWith(\"audio/\")) return \"audio\";\n if (mimeType?.startsWith(\"video/\")) return \"video\";\n return \"document\";\n}\n\n// Build an AG-UI multimodal source from a data URL, raw base64 payload, or an\n// http(s) URL. A `url` source may omit the mime type; a `data` source always\n// resolves one (falling back to application/octet-stream). An explicit\n// `sourceType: \"url\"` on the part forces the url leg for non-http references.\nfunction buildInputSource(\n value: string,\n declaredMimeType: string | undefined,\n sourceType?: string,\n): InputContentSource {\n if (sourceType === \"url\" || httpUrlPattern.test(value)) {\n return declaredMimeType !== undefined\n ? { type: \"url\", value, mimeType: declaredMimeType }\n : { type: \"url\", value };\n }\n const parsed = parseDataUrl(value);\n return {\n type: \"data\",\n value: parsed?.data ?? value,\n mimeType:\n parsed?.mimeType ?? declaredMimeType ?? \"application/octet-stream\",\n };\n}\n\nfunction toInputContent(\n part: unknown,\n fallbackMimeType: string | undefined,\n): InputContent | null {\n if (!isObject(part)) return null;\n const type = getString(part, \"type\");\n\n if (type === \"text\") {\n const text = getString(part, \"text\");\n if (text === undefined) return null;\n return { type: \"text\", text };\n }\n\n if (type === \"image\") {\n const image = getString(part, \"image\");\n if (image === undefined) return null;\n return { type: \"image\", source: buildInputSource(image, fallbackMimeType) };\n }\n\n if (type === \"file\") {\n const data = getString(part, \"data\");\n if (data === undefined) return null;\n const declaredMimeType = getString(part, \"mimeType\") || fallbackMimeType;\n const filename = getString(part, \"filename\");\n const source = buildInputSource(\n data,\n declaredMimeType,\n getString(part, \"sourceType\"),\n );\n const metadata = filename !== undefined ? { filename } : undefined;\n switch (mediaTypeForMime(source.mimeType)) {\n case \"image\":\n return { type: \"image\", source, ...(metadata && { metadata }) };\n case \"audio\":\n return { type: \"audio\", source, ...(metadata && { metadata }) };\n case \"video\":\n return { type: \"video\", source, ...(metadata && { metadata }) };\n default:\n return { type: \"document\", source, ...(metadata && { metadata }) };\n }\n }\n\n if (type === \"audio\") {\n const audio = part.audio;\n if (!isObject(audio)) return null;\n const data = getString(audio, \"data\");\n const format = getString(audio, \"format\");\n if (data === undefined || format === undefined) return null;\n return {\n type: \"audio\",\n source: buildInputSource(\n parseDataUrl(data)?.data ?? data,\n `audio/${format}`,\n ),\n };\n }\n\n return null;\n}\n\ntype SnapshotAttachment = NonNullable<\n CoreThreadMessageLike[\"attachments\"]\n>[number];\n\nconst mediaInputTypes = new Set([\"image\", \"audio\", \"video\", \"document\"]);\n\n// Inverse of buildInputSource.\nfunction inputSourceToString(\n value: unknown,\n): { value: string; mimeType?: string; isUrl?: boolean } | null {\n if (!isObject(value)) return null;\n const sourceValue = getString(value, \"value\");\n if (sourceValue === undefined) return null;\n const mimeType = getString(value, \"mimeType\");\n const type = getString(value, \"type\");\n if (type === \"url\") {\n return {\n value: sourceValue,\n isUrl: true,\n ...(mimeType !== undefined && { mimeType }),\n };\n }\n if (type === \"data\") {\n const resolvedMimeType = mimeType ?? \"application/octet-stream\";\n return {\n value: `data:${resolvedMimeType};base64,${sourceValue}`,\n mimeType: resolvedMimeType,\n };\n }\n return null;\n}\n\n// Mirrors @ag-ui/client's BackwardCompatibility_0_0_47 middleware.\nfunction upgradeBinaryInputPart(\n part: Record<string, unknown>,\n): Record<string, unknown> | null {\n const mimeType = getString(part, \"mimeType\");\n if (mimeType === undefined) return null;\n const data = getString(part, \"data\");\n const url = getString(part, \"url\");\n const source = data\n ? { type: \"data\", value: data, mimeType }\n : url\n ? { type: \"url\", value: url, mimeType }\n : null;\n if (!source) return null;\n const filename = getString(part, \"filename\");\n return {\n type: mediaTypeForMime(mimeType),\n source,\n ...(filename && { metadata: { filename } }),\n };\n}\n\nfunction toSnapshotAttachments(content: unknown): SnapshotAttachment[] {\n if (!Array.isArray(content)) return [];\n\n const attachments: SnapshotAttachment[] = [];\n for (const rawPart of content) {\n if (!isObject(rawPart)) continue;\n const part =\n getString(rawPart, \"type\") === \"binary\"\n ? upgradeBinaryInputPart(rawPart)\n : rawPart;\n if (!part) continue;\n const type = getString(part, \"type\");\n if (type === undefined || !mediaInputTypes.has(type)) continue;\n const source = inputSourceToString(part.source);\n if (!source) continue;\n\n const filename = isObject(part.metadata)\n ? getString(part.metadata, \"filename\")\n : undefined;\n const id = attachments.length.toString();\n\n if (type === \"image\") {\n attachments.push({\n id,\n type: \"image\",\n name: filename ?? \"image\",\n ...(source.mimeType !== undefined && { contentType: source.mimeType }),\n status: { type: \"complete\" },\n content: [\n {\n type: \"image\",\n image: source.value,\n ...(filename !== undefined && { filename }),\n },\n ],\n });\n continue;\n }\n\n const mimeType = source.mimeType ?? \"application/octet-stream\";\n attachments.push({\n id,\n type: type === \"document\" ? \"document\" : \"file\",\n name: filename ?? \"file\",\n contentType: mimeType,\n status: { type: \"complete\" },\n content: [\n {\n type: \"file\",\n data: source.value,\n mimeType,\n ...(source.isUrl && { sourceType: \"url\" as const }),\n ...(filename !== undefined && { filename }),\n },\n ],\n });\n }\n return attachments;\n}\n\nfunction buildUserContent(message: ThreadMessageLike): string | InputContent[] {\n const contentParts = Array.isArray(message.content) ? message.content : [];\n\n const attachments = message.attachments ?? [];\n\n const converted: InputContent[] = [];\n\n // Promote string-form content to a leading text part so it survives when\n // non-text attachments are present (fromAgUiMessages emits string content).\n if (typeof message.content === \"string\" && message.content.length > 0) {\n converted.push({ type: \"text\", text: message.content });\n }\n\n for (const part of contentParts) {\n const input = toInputContent(part, undefined);\n if (input) converted.push(input);\n }\n for (const attachment of attachments) {\n if (!isObject(attachment)) continue;\n const attachmentContent = attachment.content;\n if (!Array.isArray(attachmentContent)) continue;\n const fallbackMime = getString(attachment, \"contentType\");\n for (const part of attachmentContent) {\n const input = toInputContent(part, fallbackMime);\n if (input) converted.push(input);\n }\n }\n\n const hasNonText = converted.some((part) => part.type !== \"text\");\n if (hasNonText) return converted;\n\n // All-text path: collapse to plain string. Join text parts collected from\n // both content and attachments so attachment-sourced text is not dropped.\n if (converted.length === 0) return extractText(message.content);\n return converted\n .filter(\n (part): part is { type: \"text\"; text: string } => part.type === \"text\",\n )\n .map((part) => part.text)\n .join(\"\\n\");\n}\n\nfunction toToolCallPart(value: unknown): ToolCallPart | null {\n if (!isObject(value)) return null;\n const rawFunction = isObject(value.function) ? value.function : null;\n const toolCallId = getString(value, \"toolCallId\") ?? getString(value, \"id\");\n const toolName =\n getString(value, \"toolName\") ??\n getString(value, \"name\") ??\n (rawFunction ? getString(rawFunction, \"name\") : undefined) ??\n \"tool\";\n const argsText =\n getString(value, \"argsText\") ??\n getString(value, \"arguments\") ??\n (rawFunction ? getString(rawFunction, \"arguments\") : undefined);\n\n const parsedArgs =\n typeof argsText === \"string\" ? parseJSONText(argsText) : undefined;\n const args =\n isObject(parsedArgs) && !Array.isArray(parsedArgs)\n ? (parsedArgs as ReadonlyJSONObject)\n : isObject(value.args) && !Array.isArray(value.args)\n ? (value.args as ReadonlyJSONObject)\n : undefined;\n\n const part: ToolCallPart = {\n type: \"tool-call\",\n ...(toolCallId !== undefined ? { toolCallId } : {}),\n toolName,\n argsText: argsText ?? JSON.stringify(args ?? {}),\n ...(args !== undefined ? { args } : {}),\n };\n\n if (value.type === \"tool-call\") {\n const result = value.result;\n const isError = value.isError;\n if (result !== undefined) part.result = result;\n if (typeof isError === \"boolean\") part.isError = isError;\n }\n\n return part;\n}\n\nfunction extractAssistantToolCalls(\n message: Record<string, unknown>,\n): ToolCallPart[] {\n const parts: ToolCallPart[] = [];\n const seenToolCallIds = new Set<string>();\n const pushPart = (part: ToolCallPart | null) => {\n if (!part) return;\n const id = part.toolCallId ?? generateId();\n if (seenToolCallIds.has(id)) return;\n seenToolCallIds.add(id);\n parts.push({\n ...part,\n toolCallId: id,\n });\n };\n\n const content = message.content;\n if (Array.isArray(content)) {\n for (const part of content) {\n if (isObject(part) && part.type === \"tool-call\") {\n pushPart(toToolCallPart(part));\n }\n }\n }\n\n const toolCalls = Array.isArray(message.toolCalls)\n ? message.toolCalls\n : Array.isArray(message.tool_calls)\n ? message.tool_calls\n : [];\n for (const call of toolCalls) {\n pushPart(toToolCallPart(call));\n }\n\n return parts;\n}\n\nfunction validateInterrupts(value: unknown): AgUiInterrupt[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const valid = value.filter(\n (entry): entry is AgUiInterrupt =>\n isObject(entry) &&\n typeof entry.id === \"string\" &&\n typeof entry.reason === \"string\",\n );\n return valid.length > 0 ? valid : undefined;\n}\n\nfunction readPersistedInterrupts(\n metadata: unknown,\n): AgUiInterrupt[] | undefined {\n if (!isObject(metadata)) return undefined;\n const custom = metadata.custom;\n if (!isObject(custom)) return undefined;\n const namespaced = custom[AG_UI_METADATA_NAMESPACE];\n if (!isObject(namespaced)) return undefined;\n return validateInterrupts(namespaced.interrupts);\n}\n\nfunction toAssistantSnapshotMessage(\n rawMessage: Record<string, unknown>,\n): CoreThreadMessageLike {\n const text = extractText(rawMessage.content);\n const toolCallParts = extractAssistantToolCalls(rawMessage);\n const assistantContent = [\n ...(text.length > 0 ? [{ type: \"text\" as const, text }] : []),\n ...toolCallParts,\n ];\n const messageName = getString(rawMessage, \"name\");\n const interrupts = readPersistedInterrupts(rawMessage.metadata);\n return {\n id: getString(rawMessage, \"id\") ?? generateId(),\n role: \"assistant\",\n content: assistantContent.length > 0 ? assistantContent : \"\",\n ...(messageName !== undefined ? { name: messageName } : {}),\n ...(interrupts\n ? {\n metadata: {\n custom: {\n [AG_UI_METADATA_NAMESPACE]: {\n interrupts,\n } satisfies AgUiCustomMetadata,\n },\n },\n }\n : {}),\n };\n}\n\nfunction toUserOrSystemSnapshotMessage(\n role: \"user\" | \"system\",\n rawMessage: Record<string, unknown>,\n): CoreThreadMessageLike {\n const messageName = getString(rawMessage, \"name\");\n const attachments =\n role === \"user\" ? toSnapshotAttachments(rawMessage.content) : [];\n return {\n id: getString(rawMessage, \"id\") ?? generateId(),\n role,\n content: extractText(rawMessage.content),\n ...(messageName !== undefined ? { name: messageName } : {}),\n ...(attachments.length > 0 ? { attachments } : {}),\n };\n}\n\n// Rebuilds the a2ui:<surfaceId> \"present\" tool-call parts for one owning\n// assistant message from a bucket of rebuilt surface state, mirroring the\n// live RunAggregator.synthesizeA2uiToolCalls shape. Non-a2ui parts (text,\n// other tool calls) are preserved; existing a2ui parts are replaced wholesale\n// so create/update/delete within the bucket all converge on the rebuilt set.\nfunction attachA2uiSurfaces(\n message: CoreThreadMessageLike,\n state: A2uiState,\n): CoreThreadMessageLike {\n const a2uiParts: ToolCallPart[] = [];\n for (const [surfaceId, surface] of state) {\n const { spec } = convertSurfaceToUISpec(surface);\n if (!spec) continue;\n a2uiParts.push({\n type: \"tool-call\",\n toolCallId: `a2ui:${surfaceId}`,\n toolName: \"present\",\n args: spec as unknown as ReadonlyJSONObject,\n argsText: JSON.stringify(spec),\n result: {},\n });\n }\n\n const content = Array.isArray(message.content) ? message.content : [];\n const preserved = content.filter(\n (part) =>\n !(\n isObject(part) &&\n part.type === \"tool-call\" &&\n typeof part.toolCallId === \"string\" &&\n part.toolCallId.startsWith(\"a2ui:\")\n ),\n );\n return { ...message, content: [...preserved, ...a2uiParts] };\n}\n\nexport type FromAgUiMessagesOptions = {\n /**\n * Whether to convert `reasoning` messages into visible reasoning parts.\n * Matches the `showThinking` option of `useAgUiRuntime`. Defaults to `true`.\n */\n showThinking?: boolean;\n};\n\nexport function fromAgUiMessages(\n messages: readonly unknown[],\n options?: FromAgUiMessagesOptions,\n): CoreThreadMessageLike[] {\n const showThinking = options?.showThinking ?? true;\n const converted: CoreThreadMessageLike[] = [];\n const a2uiBuckets = new Map<string, A2uiState>();\n const a2uiBucketOwnerIndices = new Map<string, number>();\n\n for (const rawMessage of messages) {\n if (!isObject(rawMessage)) continue;\n const role = getString(rawMessage, \"role\");\n if (!role) continue;\n\n if (role === \"tool\") {\n const toolCallId = getToolCallId(rawMessage) ?? `tool-${generateId()}`;\n const toolMessageId = getString(rawMessage, \"id\");\n const modelContent = extractText(rawMessage.content);\n const mcpResult = parseMcpToolCallResult(rawMessage, modelContent);\n const mcpModelContent = mcpResult\n ? [{ type: \"text\" as const, text: modelContent }]\n : undefined;\n const result =\n mcpResult ??\n (rawMessage.result !== undefined\n ? rawMessage.result\n : typeof rawMessage.content === \"string\"\n ? parseJSONText(rawMessage.content)\n : rawMessage.content);\n const isError =\n typeof rawMessage.error === \"string\" ||\n rawMessage.isError === true ||\n rawMessage.status === \"error\"\n ? true\n : rawMessage.isError === false\n ? false\n : undefined;\n const mcpAppUri = readMcpAppResourceUri(mcpResult?._meta);\n const mcpApp =\n mcpAppUri !== undefined ? { resourceUri: mcpAppUri } : undefined;\n\n let updated = false;\n for (\n let messageIndex = converted.length - 1;\n messageIndex >= 0 && !updated;\n messageIndex--\n ) {\n const message = converted[messageIndex];\n if (\n !message ||\n message.role !== \"assistant\" ||\n !Array.isArray(message.content)\n )\n continue;\n\n for (\n let partIndex = message.content.length - 1;\n partIndex >= 0;\n partIndex--\n ) {\n const part = message.content[partIndex];\n if (!isObject(part) || part.type !== \"tool-call\") continue;\n if (getString(part, \"toolCallId\") !== toolCallId) continue;\n\n const updatedPart: ToolCallPart = {\n ...(part as ToolCallPart),\n result,\n ...(mcpModelContent ? { modelContent: mcpModelContent } : {}),\n ...(isError !== undefined ? { isError } : {}),\n ...(toolMessageId !== undefined\n ? { unstable_toolMessageId: toolMessageId }\n : {}),\n ...(mcpApp ? { mcp: { app: mcpApp } } : {}),\n };\n const updatedContent = message.content.map((contentPart, index) =>\n index === partIndex ? updatedPart : contentPart,\n );\n converted[messageIndex] = { ...message, content: updatedContent };\n updated = true;\n break;\n }\n }\n\n if (updated) {\n continue;\n }\n\n const id = toolMessageId ?? toolCallId;\n const toolName =\n getString(rawMessage, \"name\") ??\n getString(rawMessage, \"toolName\") ??\n \"tool\";\n converted.push({\n id: `${id}:assistant`,\n role: \"assistant\",\n content: [\n {\n type: \"tool-call\",\n toolCallId,\n toolName,\n args: {},\n argsText: \"{}\",\n result,\n ...(mcpModelContent ? { modelContent: mcpModelContent } : {}),\n ...(isError !== undefined ? { isError } : {}),\n ...(toolMessageId !== undefined\n ? { unstable_toolMessageId: toolMessageId }\n : {}),\n ...(mcpApp ? { mcp: { app: mcpApp } } : {}),\n },\n ],\n });\n continue;\n }\n\n if (role === \"activity\") {\n // Only a2ui-surface activity messages have an assistant-part equivalent\n // to rehydrate; other activity types still have no surface to repaint.\n const activityType = getString(rawMessage, \"activityType\");\n if (activityType !== A2UI_SURFACE_ACTIVITY_TYPE) continue;\n const activityContent = isObject(rawMessage.content)\n ? (rawMessage.content as Record<string, unknown>)\n : null;\n const operations = activityContent?.[\"a2ui_operations\"];\n if (!Array.isArray(operations)) continue;\n\n let ownerIndex = -1;\n for (let i = converted.length - 1; i >= 0; i--) {\n const candidate = converted[i];\n if (candidate && candidate.role === \"assistant\") {\n ownerIndex = i;\n break;\n }\n }\n if (ownerIndex === -1) continue;\n\n const owner = converted[ownerIndex]!;\n const bucketKey = getString(rawMessage, \"id\") ?? \"a2ui:anonymous\";\n const { state } = applyA2uiOperations(new Map(), operations);\n a2uiBuckets.delete(bucketKey);\n a2uiBucketOwnerIndices.delete(bucketKey);\n a2uiBuckets.set(bucketKey, state);\n a2uiBucketOwnerIndices.set(bucketKey, ownerIndex);\n\n const ownerState = new Map<string, A2uiSurfaceState>();\n for (const [candidateBucketKey, candidateState] of a2uiBuckets) {\n if (a2uiBucketOwnerIndices.get(candidateBucketKey) !== ownerIndex)\n continue;\n for (const [surfaceId, surface] of candidateState) {\n ownerState.set(surfaceId, surface);\n }\n }\n converted[ownerIndex] = attachA2uiSurfaces(owner, ownerState);\n continue;\n }\n\n if (role === \"assistant\") {\n converted.push(toAssistantSnapshotMessage(rawMessage));\n continue;\n }\n\n if (role === \"reasoning\") {\n // Gate on showThinking so a cold reload matches the live run: the\n // aggregator never stores reasoning parts when showThinking is false.\n if (!showThinking) continue;\n const text = extractText(rawMessage.content);\n if (text.trim().length === 0) continue;\n converted.push({\n id: getString(rawMessage, \"id\") ?? generateId(),\n role: \"assistant\",\n content: [{ type: \"reasoning\", text }],\n });\n continue;\n }\n\n if (role === \"user\" || role === \"system\") {\n converted.push(toUserOrSystemSnapshotMessage(role, rawMessage));\n }\n }\n\n for (let i = 0; i < converted.length; i++) {\n const message = converted[i]!;\n if (message.role !== \"assistant\") continue;\n\n const hasInterrupt =\n readPersistedInterrupts(message.metadata) !== undefined;\n const hasPendingToolCall =\n Array.isArray(message.content) &&\n message.content.some(\n (part) =>\n isObject(part) &&\n part.type === \"tool-call\" &&\n part.result === undefined,\n );\n\n if (hasInterrupt || hasPendingToolCall) {\n converted[i] = {\n ...message,\n status: getAutoStatus(\n false,\n false,\n hasInterrupt,\n hasPendingToolCall,\n undefined,\n ),\n };\n }\n }\n\n return converted;\n}\n\nfunction convertAssistantMessage(\n message: ThreadMessageLike,\n converted: AgUiMessage[],\n): void {\n const content = extractText(message.content);\n const contentArray = Array.isArray(message.content) ? message.content : [];\n\n const toolCallParts = contentArray.filter(\n (part): part is ToolCallPart =>\n part?.type === \"tool-call\" &&\n !(\n typeof part.toolCallId === \"string\" &&\n part.toolCallId.startsWith(\"a2ui:\")\n ),\n );\n\n const toolCalls = toolCallParts.map((part) => ({\n ...normalizeToolCall(part),\n part,\n }));\n\n // Drop assistant messages with no text or tool calls (e.g. an imported\n // reasoning-only entry) so they are not re-sent as a blank assistant turn.\n if (content.length === 0 && toolCalls.length === 0) {\n return;\n }\n\n const assistantMessage: AgUiMessage = {\n id: message.id,\n role: \"assistant\",\n content,\n };\n if (message.name) {\n assistantMessage.name = message.name;\n }\n if (toolCalls.length > 0) {\n assistantMessage.toolCalls = toolCalls.map((entry) => entry.call);\n }\n converted.push(assistantMessage);\n\n for (const { id: toolCallId, part } of toolCalls) {\n if (part.result === undefined) continue;\n\n const resultContent =\n part.modelContent !== undefined\n ? extractText(part.modelContent)\n : typeof part.result === \"string\"\n ? part.result\n : JSON.stringify(part.result);\n\n const toolMessage: AgUiMessage = {\n id: part.unstable_toolMessageId ?? `${toolCallId}:tool`,\n role: \"tool\",\n content: resultContent,\n toolCallId,\n };\n if (part.isError) {\n toolMessage.error = resultContent;\n }\n converted.push(toolMessage);\n }\n}\n\nfunction convertToolMessage(\n message: ThreadMessageLike,\n converted: AgUiMessage[],\n): void {\n const content = extractText(message.content);\n const toolCallId = message.toolCallId ?? generateId();\n\n const toolMessage: AgUiMessage = {\n id: message.id,\n role: \"tool\",\n content,\n toolCallId,\n };\n if (typeof message.error === \"string\") {\n toolMessage.error = message.error;\n }\n converted.push(toolMessage);\n}\n\nexport function toAgUiMessages(\n messages: readonly ThreadMessageLike[],\n): AgUiMessage[] {\n const converted: AgUiMessage[] = [];\n\n for (const message of messages) {\n if (message.role === \"assistant\") {\n convertAssistantMessage(message, converted);\n continue;\n }\n\n if (message.role === \"tool\") {\n convertToolMessage(message, converted);\n continue;\n }\n\n const genericMessage: AgUiMessage = {\n id: message.id,\n role: message.role,\n content:\n message.role === \"user\"\n ? buildUserContent(message)\n : extractText(message.content),\n };\n if (message.name) {\n genericMessage.name = message.name;\n }\n converted.push(genericMessage);\n }\n\n return converted;\n}\n\ntype AgUiTool = NonNullable<RunAgentParameters[\"tools\"]>[number];\n\nexport function toAgUiTools(\n tools: Record<string, Tool> | undefined,\n): AgUiTool[] {\n if (!tools) return [];\n\n const toolsSchema = toToolsJSONSchema(tools);\n return Object.entries(toolsSchema).map(([name, tool]) => ({\n name,\n description: tool.description ?? \"\",\n parameters: tool.parameters,\n }));\n}\n"],"mappings":"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"}
@@ -50,6 +50,13 @@ type UseAgUiRuntimeOptions = ExternalStoreSharedOptions & {
50
50
  * Defaults to `true`.
51
51
  */
52
52
  autoCancelPendingToolCalls?: boolean | undefined;
53
+ /**
54
+ * Buffer a message sent while a run is in flight and send it once the run
55
+ * settles, exposing it on `composer.queue` for `ComposerPrimitive.Queue`.
56
+ * The runtime owns the queue lifecycle because flushing needs the agent's
57
+ * send path and the run's own busy and idle edges.
58
+ */
59
+ unstable_enableMessageQueue?: boolean | undefined;
53
60
  onError?: (e: Error) => void;
54
61
  onCancel?: () => void;
55
62
  adapters?: UseAgUiRuntimeAdapters;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/runtime/types.ts"],"mappings":";;;;;;;;;;;;KAuBK;EACH,mBAAmB;EACnB,QAAQ;;;;;;EAMR;;KAGU,2BAA2B,KACrC;EAGA,qBAEM,qBACG,QAAQ,wBAAwB;;KAI/B;EACV,cAAc;EACd,SAAS;EACT,YAAY;EACZ,QAAQ;EACR,WAAW;EACX,UAAU;;;;EAIV,aAAa;;KAGH,wBAAwB;EAClC,OAAO;EACP,SAAS,QAAQ;EACjB;;;;;;;;;;;EAWA;EACA,WAAW,GAAG;EACd;EACA,WAAW;;KAGD;KAMA;EACV;EACA,QAAQ;EACR;EACA;EACA,iBAAiB;EACjB;EACA,WAAW;;KAGD;EACV;EACA;EACA;;KAGU;EACV,qBAAqB;EACrB,iBAAiB,QAAQ;EACzB,2BACE,oBAAoB,sBACjB;EACL,YACE,SAAS,qBACT,qBAAqB,sBAClB;EACL,OAAO;EACP,WACE,MACI,sBACE,MAAM,kCAAkC;;KAItC;EACN;;EACA;EAAmB,YAAY;;KAEzB;EACN;EAAqB;;EAErB;EACA;EACA,UAAU;;EAEV;EAAuB;;EACvB;EAAmB;EAAkB;;EACrC;EAA4B;;EAC5B;EAA8B;EAAoB;;EAClD;EAA0B;;EAC1B;EAA4B;;EAC5B;EAAwB;;EACxB;;EACA;EAAuC;;EACvC;;EACA;;EACA;EAAyB;;EACzB;EAAiC;;EACjC;EAAmC;EAAoB;;EACvD;EAA+B;;EAC/B;EAAuB;;EAEvB;EACA;EACA;EACA;;EAEA;EAAwB;EAAoB;;EAC5C;EAAuB;;EAEvB;EACA;EACA;EACA;EACA;;EAGA;EACA;EACA;EACA;EACA;EACA,YAAY;;EAGZ;EACA;EACA,SAAS;EACT;EACA;;EAEA;EAAa;EAAY;;EACzB;EAAgB;EAAc;;EAC9B;EAAwB;;EACxB;EAAqB;;EACrB;EAA2B"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/runtime/types.ts"],"mappings":";;;;;;;;;;;;KAuBK;EACH,mBAAmB;EACnB,QAAQ;;;;;;EAMR;;KAGU,2BAA2B,KACrC;EAGA,qBAEM,qBACG,QAAQ,wBAAwB;;KAI/B;EACV,cAAc;EACd,SAAS;EACT,YAAY;EACZ,QAAQ;EACR,WAAW;EACX,UAAU;;;;EAIV,aAAa;;KAGH,wBAAwB;EAClC,OAAO;EACP,SAAS,QAAQ;EACjB;;;;;;;;;;;EAWA;;;;;;;EAOA;EACA,WAAW,GAAG;EACd;EACA,WAAW;;KAGD;KAMA;EACV;EACA,QAAQ;EACR;EACA;EACA,iBAAiB;EACjB;EACA,WAAW;;KAGD;EACV;EACA;EACA;;KAGU;EACV,qBAAqB;EACrB,iBAAiB,QAAQ;EACzB,2BACE,oBAAoB,sBACjB;EACL,YACE,SAAS,qBACT,qBAAqB,sBAClB;EACL,OAAO;EACP,WACE,MACI,sBACE,MAAM,kCAAkC;;KAItC;EACN;;EACA;EAAmB,YAAY;;KAEzB;EACN;EAAqB;;EAErB;EACA;EACA,UAAU;;EAEV;EAAuB;;EACvB;EAAmB;EAAkB;;EACrC;EAA4B;;EAC5B;EAA8B;EAAoB;;EAClD;EAA0B;;EAC1B;EAA4B;;EAC5B;EAAwB;;EACxB;;EACA;EAAuC;;EACvC;;EACA;;EACA;EAAyB;;EACzB;EAAiC;;EACjC;EAAmC;EAAoB;;EACvD;EAA+B;;EAC/B;EAAuB;;EAEvB;EACA;EACA;EACA;;EAEA;EAAwB;EAAoB;;EAC5C;EAAuB;;EAEvB;EACA;EACA;EACA;EACA;;EAGA;EACA;EACA;EACA;EACA;EACA,YAAY;;EAGZ;EACA;EACA,SAAS;EACT;EACA;;EAEA;EAAa;EAAY;;EACzB;EAAgB;EAAc;;EAC9B;EAAwB;;EACxB;EAAqB;;EACrB;EAA2B"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAgUiRuntime.d.ts","names":[],"sources":["../src/useAgUiRuntime.ts"],"mappings":";;;KA2BY,uBAAuB;;;;;EAKjC,8CAA8C;;;;;;EAM9C,oCACE,oBAAoB,sBACjB;;iBAGS,eACd,SAAS,wBACR"}
1
+ {"version":3,"file":"useAgUiRuntime.d.ts","names":[],"sources":["../src/useAgUiRuntime.ts"],"mappings":";;;KAyCY,uBAAuB;;;;;EAKjC,8CAA8C;;;;;;EAM9C,oCACE,oBAAoB,sBACjB;;iBAGS,eACd,SAAS,wBACR"}
@@ -3,9 +3,12 @@ import { agUiExtras } from "./agUiExtras.js";
3
3
  import { makeLogger } from "./runtime/logger.js";
4
4
  import { AgUiThreadRuntimeCore } from "./runtime/AgUiThreadRuntimeCore.js";
5
5
  import { useExternalStoreRuntime, useExternalStoreSharedOptions, useRuntimeAdapters } from "@assistant-ui/core/react";
6
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
+ import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
7
+ import { createMessageQueue } from "@assistant-ui/core";
7
8
  //#region src/useAgUiRuntime.ts
8
9
  const EMPTY_INTERRUPTS = [];
10
+ const EMPTY_QUEUE_ITEMS = Object.freeze([]);
11
+ const subscribeNoop = () => () => {};
9
12
  function useAgUiRuntime(options) {
10
13
  const logger = useMemo(() => makeLogger(options.logger), [options.logger]);
11
14
  const [_version, setVersion] = useState(0);
@@ -36,6 +39,35 @@ function useAgUiRuntime(options) {
36
39
  });
37
40
  const [toolStatuses, setToolStatuses] = useState({});
38
41
  const hasExecutingTools = Object.values(toolStatuses).some((s) => s?.type === "executing");
42
+ const isRunning = core.isRunning() || hasExecutingTools;
43
+ const queueRef = useRef(null);
44
+ const busyEdgesRef = useRef(0);
45
+ if (options.unstable_enableMessageQueue && !queueRef.current) queueRef.current = createMessageQueue({ run: (message) => {
46
+ const controller = queueRef.current;
47
+ const edgesAtDispatch = busyEdgesRef.current;
48
+ const releaseIfNoRun = () => {
49
+ if (queueRef.current !== controller || busyEdgesRef.current !== edgesAtDispatch || core.getPendingInterrupts() !== null) return;
50
+ controller?.notifyIdle();
51
+ };
52
+ core.append(message).then(releaseIfNoRun, (e) => {
53
+ releaseIfNoRun();
54
+ logger.error?.("[agui] queued message failed to send", e);
55
+ });
56
+ } });
57
+ else if (!options.unstable_enableMessageQueue && queueRef.current) {
58
+ queueRef.current.clear();
59
+ queueRef.current = null;
60
+ }
61
+ const queueController = options.unstable_enableMessageQueue ? queueRef.current : null;
62
+ const queueItems = useSyncExternalStore(queueController?.subscribe ?? subscribeNoop, () => queueController?.adapter.items ?? EMPTY_QUEUE_ITEMS, () => EMPTY_QUEUE_ITEMS);
63
+ const steerQueueItems = useSyncExternalStore(queueController?.subscribe ?? subscribeNoop, () => queueController?.adapter.steerItems ?? EMPTY_QUEUE_ITEMS, () => EMPTY_QUEUE_ITEMS);
64
+ const queueBusy = isRunning || core.getPendingInterrupts() !== null;
65
+ useEffect(() => {
66
+ if (queueBusy) {
67
+ busyEdgesRef.current++;
68
+ queueController?.notifyBusy();
69
+ } else queueController?.notifyIdle();
70
+ }, [queueBusy, queueController]);
39
71
  const threadList = useMemo(() => {
40
72
  if (!threadListAdapter) return void 0;
41
73
  const { onSwitchToNewThread, onSwitchToThread, ...rest } = threadListAdapter;
@@ -76,7 +108,7 @@ function useAgUiRuntime(options) {
76
108
  isLoading: core.isLoading,
77
109
  messageRepository: core.getMessageRepository(),
78
110
  state: core.getState(),
79
- isRunning: core.isRunning() || hasExecutingTools,
111
+ isRunning,
80
112
  extras: agUiExtras.provide({
81
113
  interrupts: core.getPendingInterrupts()?.interrupts ?? EMPTY_INTERRUPTS,
82
114
  sendA2uiAction: (action) => core.sendA2uiAction(action),
@@ -88,9 +120,16 @@ function useAgUiRuntime(options) {
88
120
  unstable_enableToolInvocations: true,
89
121
  setToolStatuses,
90
122
  onNew: (message) => core.append(message),
91
- onEdit: (message) => core.edit(message),
92
- onReload: (parentId, config) => core.reload(parentId, config),
123
+ onEdit: (message) => {
124
+ queueController?.clear();
125
+ return core.edit(message);
126
+ },
127
+ onReload: (parentId, config) => {
128
+ queueController?.clear();
129
+ return core.reload(parentId, config);
130
+ },
93
131
  onCancel: async () => {
132
+ queueController?.clear();
94
133
  core.cancel();
95
134
  },
96
135
  onAddToolResult: (options) => core.addToolResult(options),
@@ -98,13 +137,17 @@ function useAgUiRuntime(options) {
98
137
  setMessages: (messages) => core.applyExternalMessages(messages),
99
138
  onImport: (messages) => core.applyExternalMessages(messages),
100
139
  onLoadExternalState: (state) => core.loadExternalState(state),
101
- adapters: adapterAdapters
140
+ adapters: adapterAdapters,
141
+ ...queueController && { queue: queueController.adapter }
102
142
  };
103
143
  }, [
104
144
  adapterAdapters,
105
145
  core,
106
146
  _version,
107
- hasExecutingTools,
147
+ isRunning,
148
+ queueController,
149
+ queueItems,
150
+ steerQueueItems,
108
151
  shared
109
152
  ]));
110
153
  const runtime = useMemo(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"useAgUiRuntime.js","names":[],"sources":["../src/useAgUiRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n useExternalStoreRuntime,\n useExternalStoreSharedOptions,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport type { ToolExecutionStatus } from \"@assistant-ui/core\";\nimport type {\n AssistantRuntime,\n AppendMessage,\n ExternalStoreAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport { makeLogger } from \"./runtime/logger\";\nimport type {\n AgUiInterrupt,\n AgUiResumeEntry,\n UseAgUiRuntimeOptions,\n} from \"./runtime/types\";\nimport { AgUiThreadRuntimeCore } from \"./runtime/AgUiThreadRuntimeCore\";\nimport { agUiExtras } from \"./agUiExtras\";\n\nconst EMPTY_INTERRUPTS: readonly AgUiInterrupt[] = [];\n\nexport type AgUiAssistantRuntime = AssistantRuntime & {\n /**\n * @deprecated Use the `useAgUiInterrupts()` hook instead. This method is kept\n * for backward compatibility and will be removed in a future major release.\n */\n unstable_getPendingInterrupts: () => readonly AgUiInterrupt[];\n /**\n * @deprecated Use the `useAgUiSubmitInterruptResponses()` hook instead. This\n * method is kept for backward compatibility and will be removed in a future\n * major release.\n */\n unstable_submitInterruptResponses: (\n responses: readonly AgUiResumeEntry[],\n ) => Promise<void>;\n};\n\nexport function useAgUiRuntime(\n options: UseAgUiRuntimeOptions,\n): AgUiAssistantRuntime {\n const logger = useMemo(() => makeLogger(options.logger), [options.logger]);\n const [_version, setVersion] = useState(0);\n const notifyUpdate = useCallback(() => setVersion((v) => v + 1), []);\n const coreRef = useRef<AgUiThreadRuntimeCore | null>(null);\n const runtimeAdapters = useRuntimeAdapters();\n\n const historyAdapter = options.adapters?.history ?? runtimeAdapters?.history;\n const threadListAdapter = options.adapters?.threadList;\n\n if (!coreRef.current) {\n coreRef.current = new AgUiThreadRuntimeCore({\n agent: options.agent,\n logger,\n showThinking: options.showThinking ?? true,\n autoCancelPendingToolCalls: options.autoCancelPendingToolCalls,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(historyAdapter && { history: historyAdapter }),\n notifyUpdate,\n });\n }\n\n const core = coreRef.current;\n core.updateOptions({\n agent: options.agent,\n logger,\n showThinking: options.showThinking ?? true,\n autoCancelPendingToolCalls: options.autoCancelPendingToolCalls,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(historyAdapter && { history: historyAdapter }),\n });\n\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n\n const hasExecutingTools = Object.values(toolStatuses).some(\n (s) => s?.type === \"executing\",\n );\n\n const threadList = useMemo(() => {\n if (!threadListAdapter) return undefined;\n\n const { onSwitchToNewThread, onSwitchToThread, ...rest } =\n threadListAdapter;\n\n return {\n ...rest,\n onSwitchToNewThread: onSwitchToNewThread\n ? async () => {\n await onSwitchToNewThread();\n core.applyExternalMessages([]);\n core.resetState();\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n // Clear before the thread id flips, or the old messages leak\n // into the new thread as a sibling branch.\n core.applyExternalMessages([]);\n const result = await onSwitchToThread(threadId);\n core.applyExternalMessages(result.messages);\n if (result.state !== undefined) {\n core.loadExternalState(result.state);\n } else {\n core.resetState();\n }\n if (result.unstable_resume) {\n void core.resumeInFlightRun(result.messages);\n }\n }\n : undefined,\n };\n }, [threadListAdapter, core]);\n\n const adapters = options.adapters;\n const adapterAdapters = useMemo(\n () => ({\n attachments: adapters?.attachments ?? runtimeAdapters?.attachments,\n speech: adapters?.speech,\n dictation: adapters?.dictation,\n voice: adapters?.voice,\n feedback: adapters?.feedback,\n threadList,\n }),\n [adapters, runtimeAdapters, threadList],\n );\n\n const shared = useExternalStoreSharedOptions(options);\n const store = useMemo(\n () => {\n void _version; // rerender on version change\n\n return {\n ...shared,\n isLoading: core.isLoading,\n messageRepository: core.getMessageRepository(),\n state: core.getState(),\n isRunning: core.isRunning() || hasExecutingTools,\n extras: agUiExtras.provide({\n interrupts:\n core.getPendingInterrupts()?.interrupts ?? EMPTY_INTERRUPTS,\n sendA2uiAction: (action) => core.sendA2uiAction(action),\n submitInterruptResponses: (responses) =>\n core.submitInterruptResponses(responses),\n steerAway: (message, responses) => core.steerAway(message, responses),\n state: core.getState(),\n setState: (next) => core.setState(next),\n }),\n unstable_enableToolInvocations: true,\n setToolStatuses,\n onNew: (message: AppendMessage) => core.append(message),\n onEdit: (message: AppendMessage) => core.edit(message),\n onReload: (parentId: string | null, config: { runConfig?: any }) =>\n core.reload(parentId, config),\n onCancel: async () => {\n core.cancel();\n },\n onAddToolResult: (options) => core.addToolResult(options),\n onResume: (config) => core.resume(config),\n setMessages: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onImport: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onLoadExternalState: (state: ReadonlyJSONValue) =>\n core.loadExternalState(state),\n adapters: adapterAdapters,\n } satisfies ExternalStoreAdapter<ThreadMessage>;\n },\n // _version is intentionally included to trigger re-computation when core state changes via notifyUpdate\n // toolInvocations intentionally excluded: abort/resume use refs internally and work with stale captures\n [adapterAdapters, core, _version, hasExecutingTools, shared],\n );\n\n const baseRuntime = useExternalStoreRuntime(store);\n\n const runtime = useMemo<AgUiAssistantRuntime>(() => {\n const wrapper = Object.create(baseRuntime) as AgUiAssistantRuntime;\n wrapper.unstable_getPendingInterrupts = () =>\n core.getPendingInterrupts()?.interrupts ?? [];\n wrapper.unstable_submitInterruptResponses = (responses) =>\n core.submitInterruptResponses(responses);\n return wrapper;\n }, [baseRuntime, core]);\n\n useEffect(() => {\n core.attachRuntime(runtime);\n return () => {\n core.detachRuntime();\n };\n }, [core, runtime]);\n\n useEffect(() => {\n core.__internal_load();\n }, [core]);\n\n return runtime;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useAgUiRuntime.js","names":[],"sources":["../src/useAgUiRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport {\n useExternalStoreRuntime,\n useExternalStoreSharedOptions,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport { createMessageQueue } from \"@assistant-ui/core\";\nimport type {\n MessageQueueController,\n ToolExecutionStatus,\n} from \"@assistant-ui/core\";\nimport type {\n AssistantRuntime,\n AppendMessage,\n ExternalStoreAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport { makeLogger } from \"./runtime/logger\";\nimport type {\n AgUiInterrupt,\n AgUiResumeEntry,\n UseAgUiRuntimeOptions,\n} from \"./runtime/types\";\nimport { AgUiThreadRuntimeCore } from \"./runtime/AgUiThreadRuntimeCore\";\nimport { agUiExtras } from \"./agUiExtras\";\nimport type { QueueItemState } from \"@assistant-ui/core/store\";\n\nconst EMPTY_INTERRUPTS: readonly AgUiInterrupt[] = [];\nconst EMPTY_QUEUE_ITEMS: readonly QueueItemState[] = Object.freeze([]);\nconst subscribeNoop = () => () => {};\n\nexport type AgUiAssistantRuntime = AssistantRuntime & {\n /**\n * @deprecated Use the `useAgUiInterrupts()` hook instead. This method is kept\n * for backward compatibility and will be removed in a future major release.\n */\n unstable_getPendingInterrupts: () => readonly AgUiInterrupt[];\n /**\n * @deprecated Use the `useAgUiSubmitInterruptResponses()` hook instead. This\n * method is kept for backward compatibility and will be removed in a future\n * major release.\n */\n unstable_submitInterruptResponses: (\n responses: readonly AgUiResumeEntry[],\n ) => Promise<void>;\n};\n\nexport function useAgUiRuntime(\n options: UseAgUiRuntimeOptions,\n): AgUiAssistantRuntime {\n const logger = useMemo(() => makeLogger(options.logger), [options.logger]);\n const [_version, setVersion] = useState(0);\n const notifyUpdate = useCallback(() => setVersion((v) => v + 1), []);\n const coreRef = useRef<AgUiThreadRuntimeCore | null>(null);\n const runtimeAdapters = useRuntimeAdapters();\n\n const historyAdapter = options.adapters?.history ?? runtimeAdapters?.history;\n const threadListAdapter = options.adapters?.threadList;\n\n if (!coreRef.current) {\n coreRef.current = new AgUiThreadRuntimeCore({\n agent: options.agent,\n logger,\n showThinking: options.showThinking ?? true,\n autoCancelPendingToolCalls: options.autoCancelPendingToolCalls,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(historyAdapter && { history: historyAdapter }),\n notifyUpdate,\n });\n }\n\n const core = coreRef.current;\n core.updateOptions({\n agent: options.agent,\n logger,\n showThinking: options.showThinking ?? true,\n autoCancelPendingToolCalls: options.autoCancelPendingToolCalls,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(historyAdapter && { history: historyAdapter }),\n });\n\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n\n const hasExecutingTools = Object.values(toolStatuses).some(\n (s) => s?.type === \"executing\",\n );\n const isRunning = core.isRunning() || hasExecutingTools;\n\n // The driver sends through the agent core rather than the runtime, because\n // the runtime's append routes every tail append back into this queue.\n const queueRef = useRef<MessageQueueController | null>(null);\n // Counts rendered busy edges, whether a run start or an interrupt arriving.\n // A dispatch whose count never moves was never observed as busy, so no\n // falling edge is coming to release the queue.\n const busyEdgesRef = useRef(0);\n if (options.unstable_enableMessageQueue && !queueRef.current) {\n queueRef.current = createMessageQueue({\n run: (message) => {\n const controller = queueRef.current;\n const edgesAtDispatch = busyEdgesRef.current;\n // The queue drops the item before dispatching and stays busy until an\n // idle edge. An append observed as busy is released by that falling\n // edge, and releasing again here would advance the queue twice; one\n // that never runs (a pre-run rejection, or startRun false) has to be\n // released here or every later send buffers forever. An interrupt is\n // rechecked because a run that resolves without yielding to React\n // reaches here before the effect below has seen either edge.\n const releaseIfNoRun = () => {\n if (\n queueRef.current !== controller ||\n busyEdgesRef.current !== edgesAtDispatch ||\n core.getPendingInterrupts() !== null\n )\n return;\n controller?.notifyIdle();\n };\n void core.append(message).then(releaseIfNoRun, (e: unknown) => {\n releaseIfNoRun();\n logger.error?.(\"[agui] queued message failed to send\", e);\n });\n },\n });\n } else if (!options.unstable_enableMessageQueue && queueRef.current) {\n queueRef.current.clear();\n queueRef.current = null;\n }\n const queueController = options.unstable_enableMessageQueue\n ? queueRef.current\n : null;\n\n // Feeds the store memo below: the runtime core skips an adapter whose\n // identity is unchanged, so queue items have to move the store reference or\n // subscribers never see composer.queue change.\n const queueItems = useSyncExternalStore(\n queueController?.subscribe ?? subscribeNoop,\n () => queueController?.adapter.items ?? EMPTY_QUEUE_ITEMS,\n () => EMPTY_QUEUE_ITEMS,\n );\n const steerQueueItems = useSyncExternalStore(\n queueController?.subscribe ?? subscribeNoop,\n () => queueController?.adapter.steerItems ?? EMPTY_QUEUE_ITEMS,\n () => EMPTY_QUEUE_ITEMS,\n );\n\n // Holds the queue while client-side tools are still executing, and while an\n // interrupt is unanswered: `append` refuses a new run until it is resolved,\n // and the queue drops an item before dispatching it, so draining into that\n // refusal would lose the message instead of keeping it visible.\n const queueBusy = isRunning || core.getPendingInterrupts() !== null;\n useEffect(() => {\n if (queueBusy) {\n busyEdgesRef.current++;\n queueController?.notifyBusy();\n } else {\n queueController?.notifyIdle();\n }\n }, [queueBusy, queueController]);\n\n const threadList = useMemo(() => {\n if (!threadListAdapter) return undefined;\n\n const { onSwitchToNewThread, onSwitchToThread, ...rest } =\n threadListAdapter;\n\n return {\n ...rest,\n onSwitchToNewThread: onSwitchToNewThread\n ? async () => {\n await onSwitchToNewThread();\n core.applyExternalMessages([]);\n core.resetState();\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n // Clear before the thread id flips, or the old messages leak\n // into the new thread as a sibling branch.\n core.applyExternalMessages([]);\n const result = await onSwitchToThread(threadId);\n core.applyExternalMessages(result.messages);\n if (result.state !== undefined) {\n core.loadExternalState(result.state);\n } else {\n core.resetState();\n }\n if (result.unstable_resume) {\n void core.resumeInFlightRun(result.messages);\n }\n }\n : undefined,\n };\n }, [threadListAdapter, core]);\n\n const adapters = options.adapters;\n const adapterAdapters = useMemo(\n () => ({\n attachments: adapters?.attachments ?? runtimeAdapters?.attachments,\n speech: adapters?.speech,\n dictation: adapters?.dictation,\n voice: adapters?.voice,\n feedback: adapters?.feedback,\n threadList,\n }),\n [adapters, runtimeAdapters, threadList],\n );\n\n const shared = useExternalStoreSharedOptions(options);\n const store = useMemo(\n () => {\n void _version; // rerender on version change\n\n return {\n ...shared,\n isLoading: core.isLoading,\n messageRepository: core.getMessageRepository(),\n state: core.getState(),\n isRunning,\n extras: agUiExtras.provide({\n interrupts:\n core.getPendingInterrupts()?.interrupts ?? EMPTY_INTERRUPTS,\n sendA2uiAction: (action) => core.sendA2uiAction(action),\n submitInterruptResponses: (responses) =>\n core.submitInterruptResponses(responses),\n steerAway: (message, responses) => core.steerAway(message, responses),\n state: core.getState(),\n setState: (next) => core.setState(next),\n }),\n unstable_enableToolInvocations: true,\n setToolStatuses,\n onNew: (message: AppendMessage) => core.append(message),\n onEdit: (message: AppendMessage) => {\n queueController?.clear();\n return core.edit(message);\n },\n onReload: (parentId: string | null, config: { runConfig?: any }) => {\n queueController?.clear();\n return core.reload(parentId, config);\n },\n onCancel: async () => {\n queueController?.clear();\n core.cancel();\n },\n onAddToolResult: (options) => core.addToolResult(options),\n onResume: (config) => core.resume(config),\n setMessages: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onImport: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onLoadExternalState: (state: ReadonlyJSONValue) =>\n core.loadExternalState(state),\n adapters: adapterAdapters,\n ...(queueController && { queue: queueController.adapter }),\n } satisfies ExternalStoreAdapter<ThreadMessage>;\n },\n // _version is intentionally included to trigger re-computation when core state changes via notifyUpdate\n // toolInvocations intentionally excluded: abort/resume use refs internally and work with stale captures\n [\n adapterAdapters,\n core,\n _version,\n isRunning,\n queueController,\n queueItems,\n steerQueueItems,\n shared,\n ],\n );\n\n const baseRuntime = useExternalStoreRuntime(store);\n\n const runtime = useMemo<AgUiAssistantRuntime>(() => {\n const wrapper = Object.create(baseRuntime) as AgUiAssistantRuntime;\n wrapper.unstable_getPendingInterrupts = () =>\n core.getPendingInterrupts()?.interrupts ?? [];\n wrapper.unstable_submitInterruptResponses = (responses) =>\n core.submitInterruptResponses(responses);\n return wrapper;\n }, [baseRuntime, core]);\n\n useEffect(() => {\n core.attachRuntime(runtime);\n return () => {\n core.detachRuntime();\n };\n }, [core, runtime]);\n\n useEffect(() => {\n core.__internal_load();\n }, [core]);\n\n return runtime;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-ag-ui",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "description": "AG-UI protocol adapter for assistant-ui",
5
5
  "keywords": [
6
6
  "ag-ui",
@@ -30,10 +30,10 @@
30
30
  "sideEffects": false,
31
31
  "dependencies": {
32
32
  "@ag-ui/client": "^0.0.57",
33
- "@assistant-ui/core": "^0.3.4",
34
- "@assistant-ui/react-generative-ui": "^0.0.11",
35
- "@assistant-ui/store": "^0.3.3",
36
- "assistant-stream": "^0.3.32",
33
+ "@assistant-ui/core": "^0.3.8",
34
+ "@assistant-ui/react-generative-ui": "^0.0.12",
35
+ "@assistant-ui/store": "^0.3.7",
36
+ "assistant-stream": "^0.3.35",
37
37
  "fast-json-patch": "^3.1.1"
38
38
  },
39
39
  "peerDependencies": {
@@ -46,11 +46,14 @@
46
46
  }
47
47
  },
48
48
  "devDependencies": {
49
+ "@testing-library/dom": "^10.4.1",
50
+ "@testing-library/react": "^16.3.2",
49
51
  "@types/react": "^19.2.17",
52
+ "jsdom": "^29.1.1",
50
53
  "react": "^19.2.8",
51
54
  "vitest": "^4.1.10",
52
55
  "zod": "^4.4.3",
53
- "@assistant-ui/x-buildutils": "0.0.20"
56
+ "@assistant-ui/x-buildutils": "0.0.21"
54
57
  },
55
58
  "publishConfig": {
56
59
  "access": "public",
@@ -20,7 +20,12 @@ import type {
20
20
  ToolCallMessagePart,
21
21
  } from "@assistant-ui/core";
22
22
  import { MessageRepository } from "@assistant-ui/core/internal";
23
- import type { AbstractAgent } from "@ag-ui/client";
23
+ import type {
24
+ AbstractAgent,
25
+ AgentSubscriber,
26
+ RunAgentParameters,
27
+ RunAgentResult,
28
+ } from "@ag-ui/client";
24
29
  import jsonpatch, { type Operation } from "fast-json-patch";
25
30
  import type { Logger } from "./logger";
26
31
  import { readMcpAppResourceUri } from "./mcp-tool-result";
@@ -41,6 +46,15 @@ import {
41
46
  } from "./adapter/conversions";
42
47
  import { createAgUiSubscriber } from "./adapter/subscriber";
43
48
 
49
+ // AbstractAgent.runAgent declares two parameters. HttpAgent ignores a third and
50
+ // is cancelled through agent.abortRun(); the run options stay for subclasses
51
+ // that inherit the base no-op abortRun and have no other cancellation hook.
52
+ type RunAgentWithRunOptions = (
53
+ parameters: RunAgentParameters,
54
+ subscriber: AgentSubscriber,
55
+ options: { signal: AbortSignal },
56
+ ) => Promise<RunAgentResult>;
57
+
44
58
  const optimisticPrefix = "__optimistic__";
45
59
  const generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
46
60
  const isOptimisticId = (id: string) => id.startsWith(optimisticPrefix);
@@ -76,6 +90,36 @@ type CoreOptions = {
76
90
 
77
91
  const FALLBACK_USER_STATUS = { type: "complete", reason: "unknown" } as const;
78
92
 
93
+ type AgUiRuntimeCallbackName = "onError" | "onCancel";
94
+
95
+ const reportCallbackError = (name: AgUiRuntimeCallbackName, error: unknown) => {
96
+ console.error(`[react-ag-ui] ${name} callback threw an error`, error);
97
+ };
98
+
99
+ const invokeRuntimeCallback = <TArgs extends unknown[]>(
100
+ name: AgUiRuntimeCallbackName,
101
+ callback: ((...args: TArgs) => void) | undefined,
102
+ ...args: TArgs
103
+ ) => {
104
+ if (!callback) return;
105
+
106
+ try {
107
+ const result = callback(...args) as unknown;
108
+ if (
109
+ result !== null &&
110
+ (typeof result === "object" || typeof result === "function") &&
111
+ "then" in result &&
112
+ typeof result.then === "function"
113
+ ) {
114
+ void Promise.resolve(result).catch((error) => {
115
+ reportCallbackError(name, error);
116
+ });
117
+ }
118
+ } catch (error) {
119
+ reportCallbackError(name, error);
120
+ }
121
+ };
122
+
79
123
  export class AgUiThreadRuntimeCore {
80
124
  private agent: AbstractAgent;
81
125
  private logger: Logger;
@@ -90,6 +134,9 @@ export class AgUiThreadRuntimeCore {
90
134
  private exportedRepository: ExportedMessageRepository | undefined;
91
135
  private isRunningFlag = false;
92
136
  private abortController: AbortController | null = null;
137
+ // The agent that started the active run. updateOptions can swap this.agent
138
+ // mid-run, and cancelling has to reach the agent holding the live request.
139
+ private activeRunAgent: AbstractAgent | null = null;
93
140
  private stateSnapshot: ReadonlyJSONValue | undefined;
94
141
  private pendingError: Error | null = null;
95
142
  private history: ThreadHistoryAdapter | undefined;
@@ -269,7 +316,9 @@ export class AgUiThreadRuntimeCore {
269
316
  })
270
317
  .catch((error) => {
271
318
  this.logger.error?.("[agui] failed to load history", error);
272
- this.onError?.(
319
+ invokeRuntimeCallback(
320
+ "onError",
321
+ this.onError,
273
322
  error instanceof Error ? error : new Error(String(error)),
274
323
  );
275
324
  })
@@ -333,7 +382,16 @@ export class AgUiThreadRuntimeCore {
333
382
 
334
383
  async cancel(): Promise<void> {
335
384
  if (!this.abortController) return;
336
- this.abortController.abort();
385
+ // Before the local abort, whose listener runs onCancel synchronously: a
386
+ // callback that starts another run replaces the agent's controller, and
387
+ // aborting afterwards would kill that replacement and leave this run live.
388
+ // The local abort is unconditional because abortRun is a user subclass's
389
+ // code, and a throw there would otherwise strand the thread as running.
390
+ try {
391
+ (this.activeRunAgent ?? this.agent).abortRun();
392
+ } finally {
393
+ this.abortController.abort();
394
+ }
337
395
  }
338
396
 
339
397
  async resume(config: ResumeRunConfig): Promise<void> {
@@ -354,7 +412,7 @@ export class AgUiThreadRuntimeCore {
354
412
  "[agui] unstable_resume requires a ThreadHistoryAdapter with a resume() method; skipping resume after thread switch",
355
413
  );
356
414
  this.logger.error?.(error.message);
357
- this.onError?.(error);
415
+ invokeRuntimeCallback("onError", this.onError, error);
358
416
  return;
359
417
  }
360
418
  const parentId = messages.at(-1)?.id ?? null;
@@ -761,7 +819,11 @@ export class AgUiThreadRuntimeCore {
761
819
 
762
820
  private startResumeRun(messageId: string): void {
763
821
  void this.startRun(messageId, this.lastRunConfig).catch((error) => {
764
- this.onError?.(error instanceof Error ? error : new Error(String(error)));
822
+ invokeRuntimeCallback(
823
+ "onError",
824
+ this.onError,
825
+ error instanceof Error ? error : new Error(String(error)),
826
+ );
765
827
  });
766
828
  }
767
829
 
@@ -978,6 +1040,8 @@ export class AgUiThreadRuntimeCore {
978
1040
  const abortController = new AbortController();
979
1041
  const abortSignal = abortController.signal;
980
1042
  this.abortController = abortController;
1043
+ const runAgentInstance = this.agent;
1044
+ this.activeRunAgent = runAgentInstance;
981
1045
 
982
1046
  let cancelRun = () => dispatch({ type: "RUN_CANCELLED" });
983
1047
  abortSignal.addEventListener(
@@ -985,7 +1049,7 @@ export class AgUiThreadRuntimeCore {
985
1049
  () => {
986
1050
  cancelRun();
987
1051
  this.finishRun(abortController);
988
- this.onCancel?.();
1052
+ invokeRuntimeCallback("onCancel", this.onCancel);
989
1053
  },
990
1054
  { once: true },
991
1055
  );
@@ -1021,26 +1085,27 @@ export class AgUiThreadRuntimeCore {
1021
1085
  runId,
1022
1086
  logger: this.logger,
1023
1087
  onRunFailed: (error) => {
1088
+ if (abortSignal.aborted) return;
1024
1089
  this.pendingError = error;
1025
- this.onError?.(error);
1090
+ invokeRuntimeCallback("onError", this.onError, error);
1026
1091
  },
1027
1092
  });
1028
1093
  try {
1029
- (this.agent as any).messages = input.messages;
1030
- (this.agent as any).threadId = input.threadId;
1031
- (this.agent as any).state = input.state ?? null;
1094
+ (runAgentInstance as any).messages = input.messages;
1095
+ (runAgentInstance as any).threadId = input.threadId;
1096
+ (runAgentInstance as any).state = input.state ?? null;
1032
1097
  } catch {
1033
1098
  // ignore
1034
1099
  }
1035
- await (this.agent as any).runAgent(input, subscriber, {
1036
- signal: abortSignal,
1037
- });
1100
+ const runAgent: RunAgentWithRunOptions =
1101
+ runAgentInstance.runAgent.bind(runAgentInstance);
1102
+ await runAgent(input, subscriber, { signal: abortSignal });
1038
1103
  }
1039
1104
  } catch (error) {
1040
1105
  if (!abortSignal.aborted) {
1041
1106
  const err = error instanceof Error ? error : new Error(String(error));
1042
1107
  dispatch({ type: "RUN_ERROR", message: err.message });
1043
- this.onError?.(err);
1108
+ invokeRuntimeCallback("onError", this.onError, err);
1044
1109
  this.pendingError = this.pendingError ?? err;
1045
1110
  }
1046
1111
  } finally {
@@ -1142,7 +1207,7 @@ export class AgUiThreadRuntimeCore {
1142
1207
  ctx.applyUpdate({
1143
1208
  status: { type: "incomplete", reason: "error", error: err.message },
1144
1209
  });
1145
- this.onError?.(err);
1210
+ invokeRuntimeCallback("onError", this.onError, err);
1146
1211
  this.pendingError = this.pendingError ?? err;
1147
1212
  return;
1148
1213
  }
@@ -1217,6 +1282,7 @@ export class AgUiThreadRuntimeCore {
1217
1282
  private finishRun(controller: AbortController | null) {
1218
1283
  if (this.abortController === controller) {
1219
1284
  this.abortController = null;
1285
+ this.activeRunAgent = null;
1220
1286
  }
1221
1287
  this.setRunning(false);
1222
1288
  }