@assistant-ui/react-ag-ui 0.0.54 → 0.0.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +2 -2
  3. package/dist/runtime/AgUiThreadRuntimeCore.d.ts +23 -2
  4. package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +1 -1
  5. package/dist/runtime/AgUiThreadRuntimeCore.js +169 -35
  6. package/dist/runtime/AgUiThreadRuntimeCore.js.map +1 -1
  7. package/dist/runtime/adapter/conversions.d.ts +13 -3
  8. package/dist/runtime/adapter/conversions.d.ts.map +1 -1
  9. package/dist/runtime/adapter/conversions.js +58 -26
  10. package/dist/runtime/adapter/conversions.js.map +1 -1
  11. package/dist/runtime/adapter/run-aggregator.d.ts +5 -1
  12. package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
  13. package/dist/runtime/adapter/run-aggregator.js +26 -1
  14. package/dist/runtime/adapter/run-aggregator.js.map +1 -1
  15. package/dist/runtime/adapter/subscriber.d.ts +3 -0
  16. package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
  17. package/dist/runtime/adapter/subscriber.js +9 -0
  18. package/dist/runtime/adapter/subscriber.js.map +1 -1
  19. package/dist/runtime/adapter/tool-approval.d.ts +56 -0
  20. package/dist/runtime/adapter/tool-approval.d.ts.map +1 -0
  21. package/dist/runtime/adapter/tool-approval.js +244 -0
  22. package/dist/runtime/adapter/tool-approval.js.map +1 -0
  23. package/dist/runtime/event-parser.d.ts.map +1 -1
  24. package/dist/runtime/event-parser.js +2 -1
  25. package/dist/runtime/event-parser.js.map +1 -1
  26. package/dist/runtime/interrupt-internals.d.ts +8 -0
  27. package/dist/runtime/interrupt-internals.d.ts.map +1 -0
  28. package/dist/runtime/interrupt-internals.js +25 -0
  29. package/dist/runtime/interrupt-internals.js.map +1 -0
  30. package/dist/useAgUiRuntime.js +3 -0
  31. package/dist/useAgUiRuntime.js.map +1 -1
  32. package/package.json +7 -7
  33. package/src/index.ts +8 -2
  34. package/src/runtime/AgUiThreadRuntimeCore.ts +314 -41
  35. package/src/runtime/adapter/conversions.content.test.ts +61 -3
  36. package/src/runtime/adapter/conversions.ts +83 -39
  37. package/src/runtime/adapter/run-aggregator.approval.test.ts +103 -0
  38. package/src/runtime/adapter/run-aggregator.ts +37 -1
  39. package/src/runtime/adapter/subscriber.ts +14 -0
  40. package/src/runtime/adapter/tool-approval.test.ts +681 -0
  41. package/src/runtime/adapter/tool-approval.ts +366 -0
  42. package/src/runtime/event-parser.ts +10 -1
  43. package/src/runtime/interrupt-internals.ts +35 -0
  44. package/src/useAgUiRuntime.approval.test.tsx +885 -0
  45. package/src/useAgUiRuntime.ts +4 -0
@@ -1 +1 @@
1
- {"version":3,"file":"run-aggregator.js","names":[],"sources":["../../../src/runtime/adapter/run-aggregator.ts"],"sourcesContent":["\"use client\";\n\nimport {\n isMcpAppUri,\n type ChatModelRunResult,\n type MessageTiming,\n type ThreadAssistantMessagePart,\n type ToolCallMessagePart,\n type ToolModelContentPart,\n} from \"@assistant-ui/core\";\nimport {\n applyA2uiOperations,\n convertSurfaceToUISpec,\n type A2uiState,\n type A2uiSurfaceState,\n} from \"@assistant-ui/react-generative-ui/a2ui\";\nimport { readMcpAppResourceUri } from \"../mcp-tool-result\";\nimport type { AgUiEvent, AgUiInterrupt } from \"../types\";\nimport type { Logger } from \"../logger\";\n\nexport const AG_UI_METADATA_NAMESPACE = \"agui\";\n\nexport type AgUiOpaqueReasoning = {\n id: string;\n encryptedValue: string;\n after?: boolean;\n};\n\nexport type AgUiCustomMetadata = {\n interrupts?: AgUiInterrupt[];\n opaqueReasoning?: AgUiOpaqueReasoning[];\n};\n\ntype Emit = (update: ChatModelRunResult) => void;\n\ntype ToolCallState = {\n toolCallId: string;\n toolCallName: string;\n argsText: string;\n parsedArgs: Record<string, unknown> | undefined;\n result: unknown;\n isError: boolean | undefined;\n parentMessageId?: string;\n toolMessageId?: string;\n mcpAppResourceUri?: string;\n mcpAppServerId?: string;\n modelContent?: ToolModelContentPart[];\n snapshotResultApplied: boolean;\n};\n\nexport const MCP_APPS_ACTIVITY_TYPE = \"mcp-apps\";\n\nexport const A2UI_SURFACE_ACTIVITY_TYPE = \"a2ui-surface\";\n\nexport const isPlainObject = (\n value: unknown,\n): value is Record<string, unknown> =>\n !!value && typeof value === \"object\" && !Array.isArray(value);\n\nexport type RunAggregatorOptions = {\n showThinking: boolean;\n logger: Logger;\n emit: Emit;\n onServerMessageId?: (messageId: string) => void;\n};\n\n/**\n * Collects AG-UI events into assistant-ui run snapshots that can be yielded from a ChatModelAdapter.\n *\n * The aggregator keeps a single assistant message worth of parts. Each incoming event updates the parts and\n * emits a fresh snapshot through the provided `emit` callback.\n */\nexport class RunAggregator {\n private readonly emitUpdate: Emit;\n private readonly showThinking: boolean;\n private readonly logger: Logger;\n private readonly onServerMessageId: ((messageId: string) => void) | undefined;\n\n private status: ChatModelRunResult[\"status\"] | undefined;\n private interrupts: AgUiInterrupt[] | undefined;\n private readonly textParts = new Map<\n string,\n { buffer: string; touched: boolean }\n >();\n private activeTextMessageId: string | undefined;\n private readonly reasoningParts = new Map<string, string>(); // key → buffer\n private readonly reasoningSignatures = new Map<string, string>();\n private readonly reasoningMessageIds = new Map<string, string>();\n private readonly anonymousReasoningKeys = new Set<string>();\n private activeReasoningKey: string | undefined;\n private reasoningPartCounter = 0;\n private readonly toolCalls = new Map<string, ToolCallState>();\n private readonly a2uiBuckets = new Map<string, A2uiState>();\n private readonly a2uiToolCallIds = new Set<string>();\n private lastResolvedToolCallId: string | undefined;\n private readonly partOrder: (\n | { kind: \"text\"; key: string }\n | { kind: \"reasoning\"; key: string }\n | { kind: \"tool-call\"; toolCallId: string }\n )[] = [];\n private textPartCounter = 0;\n private serverMessageIdReported = false;\n\n private streamStartTime: number | undefined;\n private firstTokenTime: number | undefined;\n private totalChunks = 0;\n\n constructor(options: RunAggregatorOptions) {\n this.emitUpdate = options.emit;\n this.showThinking = options.showThinking;\n this.logger = options.logger;\n this.onServerMessageId = options.onServerMessageId;\n }\n\n hasToolCall(toolCallId: string): boolean {\n return this.toolCalls.has(toolCallId);\n }\n\n handle(event: AgUiEvent): void {\n switch (event.type) {\n case \"RUN_STARTED\": {\n this.clearTextParts();\n this.reasoningParts.clear();\n this.reasoningSignatures.clear();\n this.reasoningMessageIds.clear();\n this.anonymousReasoningKeys.clear();\n this.activeReasoningKey = undefined;\n this.reasoningPartCounter = 0;\n this.toolCalls.clear();\n this.a2uiBuckets.clear();\n this.a2uiToolCallIds.clear();\n this.lastResolvedToolCallId = undefined;\n this.partOrder.length = 0;\n this.textPartCounter = 0;\n this.activeTextMessageId = undefined;\n this.interrupts = undefined;\n this.serverMessageIdReported = false;\n this.streamStartTime = Date.now();\n this.firstTokenTime = undefined;\n this.totalChunks = 0;\n this.status = { type: \"running\" };\n this.emit();\n break;\n }\n case \"RUN_FINISHED\": {\n if (event.outcome?.type === \"interrupt\") {\n this.interrupts = event.outcome.interrupts;\n this.status = { type: \"requires-action\", reason: \"interrupt\" };\n this.emit();\n break;\n }\n\n this.interrupts = undefined;\n const hasUnresolvedToolCalls = Array.from(this.toolCalls.values()).some(\n (tc) => tc.result === undefined,\n );\n\n this.status =\n event.outcome?.type === \"success\" || !hasUnresolvedToolCalls\n ? { type: \"complete\", reason: \"unknown\" }\n : { type: \"requires-action\", reason: \"tool-calls\" };\n this.emit();\n break;\n }\n case \"RUN_ERROR\": {\n this.status = {\n type: \"incomplete\",\n reason: \"error\",\n ...(event.message !== undefined ? { error: event.message } : {}),\n };\n this.emit();\n break;\n }\n case \"RUN_CANCELLED\": {\n this.status = { type: \"incomplete\", reason: \"cancelled\" };\n this.emit();\n break;\n }\n\n case \"TEXT_MESSAGE_START\": {\n this.reportServerMessageId(event.messageId);\n const id = this.startTextMessage(event.messageId);\n if (id) {\n this.markTextPartTouched(id);\n }\n this.emit();\n break;\n }\n case \"TEXT_MESSAGE_CONTENT\":\n case \"TEXT_MESSAGE_CHUNK\": {\n const incomingId = \"messageId\" in event ? event.messageId : undefined;\n this.reportServerMessageId(incomingId);\n if (!event.delta) break;\n this.recordFirstToken();\n const id = this.resolveTextMessageId(incomingId);\n this.appendText(id, event.delta);\n this.totalChunks++;\n this.emit();\n break;\n }\n case \"TEXT_MESSAGE_END\": {\n this.reportServerMessageId(event.messageId);\n if (event.messageId && this.activeTextMessageId === event.messageId) {\n this.activeTextMessageId = undefined;\n }\n this.emit();\n break;\n }\n\n case \"THINKING_START\":\n case \"THINKING_TEXT_MESSAGE_START\":\n case \"REASONING_START\":\n case \"REASONING_MESSAGE_START\":\n this.handleReasoningStart(\n \"messageId\" in event ? event.messageId : undefined,\n event.type === \"REASONING_MESSAGE_START\",\n );\n break;\n case \"REASONING_ENCRYPTED_VALUE\":\n if (event.subtype === \"message\") {\n // entityId names any message, not necessarily a reasoning one, so an\n // unmatched id may only claim a block that has no id to contradict it.\n const active = this.activeReasoningKey;\n const key = this.reasoningParts.has(event.entityId)\n ? event.entityId\n : active !== undefined && this.anonymousReasoningKeys.has(active)\n ? active\n : undefined;\n if (key !== undefined) {\n this.reasoningSignatures.set(key, event.encryptedValue);\n this.emit();\n }\n }\n break;\n case \"THINKING_TEXT_MESSAGE_CONTENT\":\n this.handleReasoningContent(event.delta);\n this.totalChunks++;\n this.recordFirstToken();\n break;\n case \"REASONING_MESSAGE_CONTENT\":\n this.handleReasoningContent(\n event.delta,\n \"messageId\" in event ? event.messageId : undefined,\n true,\n );\n this.totalChunks++;\n this.recordFirstToken();\n break;\n case \"THINKING_TEXT_MESSAGE_END\":\n case \"THINKING_END\":\n case \"REASONING_MESSAGE_END\":\n case \"REASONING_END\":\n this.handleReasoningEnd();\n break;\n\n case \"TOOL_CALL_START\": {\n this.reportServerMessageId(event.parentMessageId);\n this.startToolCall(\n event.toolCallId,\n event.toolCallName,\n event.parentMessageId,\n );\n this.emit();\n break;\n }\n case \"TOOL_CALL_ARGS\":\n case \"TOOL_CALL_CHUNK\": {\n if (event.type === \"TOOL_CALL_CHUNK\") {\n this.reportServerMessageId(event.parentMessageId);\n }\n if (!event.delta) break;\n this.appendToolArgs(event.toolCallId, event.delta);\n this.emit();\n break;\n }\n case \"TOOL_CALL_END\": {\n this.emit();\n break;\n }\n case \"TOOL_CALL_RESULT\": {\n this.finishToolCall(\n event.toolCallId,\n event.content ?? \"\",\n typeof event.mcpResult?.isError === \"boolean\"\n ? event.mcpResult.isError\n : event.role === \"tool\"\n ? false\n : undefined,\n event.messageId,\n event.mcpResult,\n );\n this.emit();\n break;\n }\n case \"ACTIVITY_SNAPSHOT\": {\n if (event.activityType === A2UI_SURFACE_ACTIVITY_TYPE) {\n this.handleA2uiActivitySnapshot(event);\n break;\n }\n if (event.activityType !== MCP_APPS_ACTIVITY_TYPE) break;\n const toolCallId = event.content.toolCallId;\n const entry =\n typeof toolCallId === \"string\"\n ? this.toolCalls.get(toolCallId)\n : this.lastResolvedToolCallId\n ? this.toolCalls.get(this.lastResolvedToolCallId)\n : undefined;\n const resourceUri = event.content.resourceUri;\n if (\n entry &&\n typeof resourceUri === \"string\" &&\n isMcpAppUri(resourceUri)\n ) {\n entry.mcpAppResourceUri = resourceUri;\n const id = event.content.serverId;\n const hash = event.content.serverHash;\n if (typeof id === \"string\" && id.length > 0) {\n entry.mcpAppServerId = id;\n } else if (typeof hash === \"string\" && hash.length > 0) {\n entry.mcpAppServerId = hash;\n }\n const result = event.content.result;\n if (isPlainObject(result)) {\n if (\n entry.result !== undefined &&\n entry.modelContent === undefined\n ) {\n entry.modelContent = [\n {\n type: \"text\",\n text:\n typeof entry.result === \"string\"\n ? entry.result\n : JSON.stringify(entry.result),\n },\n ];\n }\n entry.result = result;\n entry.snapshotResultApplied = true;\n if (typeof result.isError === \"boolean\") {\n entry.isError = result.isError;\n }\n }\n this.emit();\n }\n break;\n }\n\n default: {\n this.logger.debug?.(\"[agui] aggregator ignored event\", event);\n }\n }\n }\n\n private handleA2uiActivitySnapshot(\n event: Extract<AgUiEvent, { type: \"ACTIVITY_SNAPSHOT\" }>,\n ): void {\n const operations = event.content[\"a2ui_operations\"];\n if (!Array.isArray(operations)) return;\n\n const messageId = event.messageId ?? \"a2ui:anonymous\";\n if (event.replace === false && this.a2uiBuckets.has(messageId)) return;\n\n const { state, warnings } = applyA2uiOperations(new Map(), operations);\n for (const warning of warnings) {\n this.logger.debug(\"[agui] a2ui operation warning\", warning);\n }\n this.a2uiBuckets.delete(messageId);\n this.a2uiBuckets.set(messageId, state);\n this.synthesizeA2uiToolCalls();\n this.emit();\n }\n\n private synthesizeA2uiToolCalls(): void {\n const surfaces = new Map<string, A2uiSurfaceState>();\n\n for (const bucket of this.a2uiBuckets.values()) {\n for (const [surfaceId, surface] of bucket) {\n surfaces.set(surfaceId, surface);\n }\n }\n\n const activeToolCallIds = new Set<string>();\n for (const [surfaceId, surface] of surfaces) {\n const toolCallId = `a2ui:${surfaceId}`;\n const { spec, warnings } = convertSurfaceToUISpec(surface);\n for (const warning of warnings) {\n this.logger.debug(\"[agui] a2ui surface conversion warning\", warning);\n }\n if (!spec) continue;\n\n activeToolCallIds.add(toolCallId);\n\n const entry: ToolCallState = {\n toolCallId,\n toolCallName: \"present\",\n argsText: JSON.stringify(spec),\n parsedArgs: spec,\n result: {},\n isError: undefined,\n snapshotResultApplied: false,\n };\n if (!this.toolCalls.has(toolCallId)) {\n this.partOrder.push({ kind: \"tool-call\", toolCallId });\n }\n this.toolCalls.set(toolCallId, entry);\n this.a2uiToolCallIds.add(toolCallId);\n }\n\n for (const toolCallId of this.a2uiToolCallIds) {\n if (activeToolCallIds.has(toolCallId)) continue;\n this.toolCalls.delete(toolCallId);\n const partIndex = this.partOrder.findIndex(\n (part) => part.kind === \"tool-call\" && part.toolCallId === toolCallId,\n );\n if (partIndex !== -1) this.partOrder.splice(partIndex, 1);\n this.a2uiToolCallIds.delete(toolCallId);\n }\n }\n\n private reportServerMessageId(messageId: string | undefined): void {\n if (this.serverMessageIdReported || !messageId) return;\n this.serverMessageIdReported = true;\n this.onServerMessageId?.(messageId);\n }\n\n private clearTextParts(): void {\n this.textParts.clear();\n }\n\n private generateTextKey(): string {\n this.textPartCounter += 1;\n return `text-${this.textPartCounter}`;\n }\n\n private startTextMessage(messageId?: string): string {\n const id = messageId ?? this.generateTextKey();\n this.ensureTextPart(id);\n this.activeTextMessageId = id;\n return id;\n }\n\n private resolveTextMessageId(messageId?: string): string {\n if (messageId) {\n this.ensureTextPart(messageId);\n this.activeTextMessageId = messageId;\n return messageId;\n }\n\n if (this.activeTextMessageId) {\n return this.activeTextMessageId;\n }\n\n const generated = this.generateTextKey();\n this.ensureTextPart(generated);\n this.activeTextMessageId = generated;\n return generated;\n }\n\n private ensureTextPart(id: string): void {\n if (!this.textParts.has(id)) {\n this.textParts.set(id, { buffer: \"\", touched: false });\n if (\n !this.partOrder.some((part) => part.kind === \"text\" && part.key === id)\n ) {\n this.partOrder.push({ kind: \"text\", key: id });\n }\n }\n }\n\n private markTextPartTouched(id: string): void {\n const entry = this.textParts.get(id);\n if (!entry) return;\n entry.touched = true;\n }\n\n private appendText(id: string, delta: string): void {\n this.ensureTextPart(id);\n const entry = this.textParts.get(id);\n if (!entry) return;\n entry.buffer += delta;\n entry.touched = true;\n }\n\n private startToolCall(\n id: string | undefined,\n name?: string,\n parentMessageId?: string,\n ) {\n if (!id) return;\n // A new tool call acts as a boundary: any anonymous text that arrives\n // after it should be a new part, not appended to the pre-tool text.\n this.activeTextMessageId = undefined;\n if (\n !this.partOrder.some(\n (part) => part.kind === \"tool-call\" && part.toolCallId === id,\n )\n ) {\n this.insertToolPart(id, parentMessageId);\n }\n const state: ToolCallState = {\n toolCallId: id,\n toolCallName: name ?? \"tool\",\n argsText: \"\",\n parsedArgs: undefined,\n result: undefined,\n isError: undefined,\n snapshotResultApplied: false,\n };\n if (parentMessageId) {\n state.parentMessageId = parentMessageId;\n }\n this.toolCalls.set(id, state);\n }\n\n // The message and tool-call channels are unordered on the wire, so anchor a\n // tool call under its parentMessageId text part instead of appending it.\n private insertToolPart(id: string, parentMessageId?: string) {\n const entry = { kind: \"tool-call\", toolCallId: id } as const;\n if (parentMessageId) {\n const parentIndex = this.partOrder.findIndex(\n (part) => part.kind === \"text\" && part.key === parentMessageId,\n );\n if (parentIndex !== -1) {\n let insertAt = parentIndex + 1;\n while (insertAt < this.partOrder.length) {\n const part = this.partOrder[insertAt];\n if (\n part?.kind === \"tool-call\" &&\n this.toolCalls.get(part.toolCallId)?.parentMessageId ===\n parentMessageId\n ) {\n insertAt += 1;\n continue;\n }\n break;\n }\n this.partOrder.splice(insertAt, 0, entry);\n return;\n }\n }\n this.partOrder.push(entry);\n }\n\n private appendToolArgs(id: string | undefined, delta: string) {\n const entry = id ? this.toolCalls.get(id) : undefined;\n if (!entry) return;\n entry.argsText += delta;\n try {\n const parsed = JSON.parse(entry.argsText);\n if (parsed && typeof parsed === \"object\") {\n entry.parsedArgs = parsed as Record<string, unknown>;\n } else {\n entry.parsedArgs = undefined;\n }\n } catch {\n entry.parsedArgs = undefined;\n }\n }\n\n private finishToolCall(\n id: string,\n content: string,\n isError?: boolean,\n toolMessageId?: string,\n mcpResult?: Record<string, unknown>,\n ) {\n if (!id) return;\n let entry = this.toolCalls.get(id);\n if (!entry) {\n entry = {\n toolCallId: id,\n toolCallName: \"tool\",\n argsText: \"\",\n parsedArgs: undefined,\n result: undefined,\n isError: undefined,\n snapshotResultApplied: false,\n };\n this.toolCalls.set(id, entry);\n }\n if (\n !this.partOrder.some(\n (part) => part.kind === \"tool-call\" && part.toolCallId === id,\n )\n ) {\n this.partOrder.push({ kind: \"tool-call\", toolCallId: id });\n }\n if (mcpResult !== undefined && !entry.snapshotResultApplied) {\n entry.result = mcpResult;\n entry.modelContent = [{ type: \"text\", text: content }];\n entry.snapshotResultApplied = true;\n entry.isError = isError;\n if (entry.mcpAppResourceUri === undefined) {\n const uri = readMcpAppResourceUri(mcpResult._meta);\n if (uri !== undefined) entry.mcpAppResourceUri = uri;\n }\n } else if (entry.snapshotResultApplied) {\n if (entry.modelContent === undefined) {\n entry.modelContent = [{ type: \"text\", text: content }];\n }\n if (entry.isError === undefined) {\n entry.isError = isError;\n }\n } else {\n entry.result = tryParseJSON(content);\n entry.isError = isError;\n }\n if (toolMessageId) {\n entry.toolMessageId = toolMessageId;\n }\n this.lastResolvedToolCallId = id;\n }\n\n private emit(): void {\n const snapshot: ThreadAssistantMessagePart[] = [];\n\n for (const part of this.partOrder) {\n if (part.kind === \"reasoning\") {\n if (this.showThinking) {\n const buffer = this.reasoningParts.get(part.key) ?? \"\";\n if (buffer.length > 0 || this.activeReasoningKey === part.key) {\n const encryptedValue = this.reasoningSignatures.get(part.key);\n const reasoningId = this.reasoningMessageIds.get(part.key);\n const meta = {\n ...(reasoningId !== undefined ? { reasoningId } : {}),\n ...(encryptedValue !== undefined ? { encryptedValue } : {}),\n };\n snapshot.push({\n type: \"reasoning\",\n text: buffer,\n ...(Object.keys(meta).length > 0\n ? { providerMetadata: { [AG_UI_METADATA_NAMESPACE]: meta } }\n : {}),\n } as const);\n }\n }\n continue;\n }\n\n if (part.kind === \"text\") {\n const entry = this.textParts.get(part.key);\n if (entry?.touched) {\n snapshot.push({ type: \"text\", text: entry.buffer } as const);\n }\n continue;\n }\n\n const entry = this.toolCalls.get(part.toolCallId);\n if (!entry) continue;\n const toolPart: ToolCallMessagePart = {\n type: \"tool-call\",\n toolCallId: entry.toolCallId,\n toolName: entry.toolCallName,\n args: (entry.parsedArgs ?? {}) as any,\n argsText: entry.argsText,\n ...(entry.result !== undefined ? { result: entry.result } : {}),\n ...(entry.modelContent !== undefined\n ? { modelContent: entry.modelContent }\n : {}),\n ...(entry.isError !== undefined ? { isError: entry.isError } : {}),\n ...(entry.mcpAppResourceUri\n ? {\n mcp: {\n app: {\n resourceUri: entry.mcpAppResourceUri,\n ...(entry.mcpAppServerId\n ? { serverId: entry.mcpAppServerId }\n : {}),\n },\n },\n }\n : {}),\n ...(entry.parentMessageId ? { parentId: entry.parentMessageId } : {}),\n ...(entry.toolMessageId\n ? { unstable_toolMessageId: entry.toolMessageId }\n : {}),\n } as ToolCallMessagePart & { unstable_toolMessageId?: string };\n snapshot.push(toolPart);\n }\n\n const timing = this.getTiming();\n const metadata = {\n ...(timing ? { timing } : {}),\n ...(this.interrupts\n ? {\n custom: {\n [AG_UI_METADATA_NAMESPACE]: {\n interrupts: this.interrupts,\n } satisfies AgUiCustomMetadata,\n },\n }\n : {}),\n };\n const result: ChatModelRunResult = {\n content: snapshot,\n ...(this.status ? { status: this.status } : undefined),\n ...(Object.keys(metadata).length > 0 ? { metadata } : undefined),\n };\n this.emitUpdate(result);\n }\n\n private recordFirstToken(): void {\n if (\n this.firstTokenTime === undefined &&\n this.streamStartTime !== undefined\n ) {\n this.firstTokenTime = Date.now() - this.streamStartTime;\n }\n }\n\n private getTiming(): MessageTiming | undefined {\n if (this.streamStartTime === undefined) return undefined;\n\n const now = Date.now();\n const totalStreamTime = now - this.streamStartTime;\n const tokenCount =\n this.totalChunks > 0\n ? Math.ceil(\n Array.from(this.textParts.values()).reduce(\n (sum, p) => sum + p.buffer.length,\n 0,\n ) / 4,\n )\n : undefined;\n const tokensPerSecond =\n tokenCount && totalStreamTime > 0\n ? (tokenCount / totalStreamTime) * 1000\n : undefined;\n\n return {\n streamStartTime: this.streamStartTime,\n ...(this.firstTokenTime !== undefined\n ? { firstTokenTime: this.firstTokenTime }\n : {}),\n totalStreamTime,\n ...(tokenCount !== undefined ? { tokenCount } : {}),\n ...(tokensPerSecond !== undefined ? { tokensPerSecond } : {}),\n totalChunks: this.totalChunks,\n toolCallCount: this.toolCalls.size,\n };\n }\n\n private handleReasoningStart(messageId?: string, isMessageId = false): void {\n if (!this.showThinking) return;\n // A reasoning block acts as a boundary: anonymous text arriving after it\n // should be a new part, not appended to any pre-reasoning text.\n this.activeTextMessageId = undefined;\n const key = messageId ?? `__auto-reasoning-${++this.reasoningPartCounter}`;\n // Two different questions: which id may be replayed as a ReasoningMessage.id\n // (only the message-scoped aliases carry one), and which block an unmatched\n // signature may claim (any block opened without an id at all).\n if (messageId === undefined) {\n this.anonymousReasoningKeys.add(key);\n } else if (isMessageId) {\n this.reasoningMessageIds.set(key, messageId);\n }\n if (!this.reasoningParts.has(key)) {\n this.reasoningParts.set(key, \"\");\n this.partOrder.push({ kind: \"reasoning\", key });\n }\n this.activeReasoningKey = key;\n this.emit();\n }\n\n private handleReasoningContent(\n delta: string,\n messageId?: string,\n isMessageId = false,\n ): void {\n if (!this.showThinking || !delta) return;\n if (!this.activeReasoningKey) {\n // Content arrived without a preceding START — create the slot lazily.\n this.handleReasoningStart(messageId, isMessageId);\n }\n const key = this.activeReasoningKey;\n if (!key) return;\n this.reasoningParts.set(key, (this.reasoningParts.get(key) ?? \"\") + delta);\n this.emit();\n }\n\n private handleReasoningEnd(): void {\n if (!this.showThinking) return;\n this.activeReasoningKey = undefined;\n this.emit();\n }\n}\n\nexport function tryParseJSON(value: string): unknown {\n if (!value) return value;\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"run-aggregator.js","names":[],"sources":["../../../src/runtime/adapter/run-aggregator.ts"],"sourcesContent":["\"use client\";\n\nimport {\n isMcpAppUri,\n type ChatModelRunResult,\n type MessageTiming,\n type ThreadAssistantMessagePart,\n type ToolCallMessagePart,\n type ToolModelContentPart,\n} from \"@assistant-ui/core\";\nimport {\n applyA2uiOperations,\n convertSurfaceToUISpec,\n type A2uiState,\n type A2uiSurfaceState,\n} from \"@assistant-ui/react-generative-ui/a2ui\";\nimport { readMcpAppResourceUri } from \"../mcp-tool-result\";\nimport { projectAgUiToolApprovals } from \"./tool-approval\";\nimport type { AgUiEvent, AgUiInterrupt } from \"../types\";\nimport type { Logger } from \"../logger\";\n\nexport const AG_UI_METADATA_NAMESPACE = \"agui\";\n\nexport type AgUiOpaqueReasoning = {\n id: string;\n encryptedValue: string;\n after?: boolean;\n};\n\nexport type AgUiCustomMetadata = {\n interrupts?: AgUiInterrupt[];\n opaqueReasoning?: AgUiOpaqueReasoning[];\n};\n\ntype Emit = (update: ChatModelRunResult) => void;\n\ntype ToolCallState = {\n toolCallId: string;\n toolCallName: string;\n argsText: string;\n parsedArgs: Record<string, unknown> | undefined;\n result: unknown;\n isError: boolean | undefined;\n parentMessageId?: string;\n toolMessageId?: string;\n mcpAppResourceUri?: string;\n mcpAppServerId?: string;\n modelContent?: ToolModelContentPart[];\n snapshotResultApplied: boolean;\n};\n\nexport const MCP_APPS_ACTIVITY_TYPE = \"mcp-apps\";\n\nexport const A2UI_SURFACE_ACTIVITY_TYPE = \"a2ui-surface\";\n\nexport const isPlainObject = (\n value: unknown,\n): value is Record<string, unknown> =>\n !!value && typeof value === \"object\" && !Array.isArray(value);\n\nexport type RunAggregatorOptions = {\n showThinking: boolean;\n logger: Logger;\n emit: Emit;\n onServerMessageId?: (messageId: string) => void;\n};\n\n/**\n * Collects AG-UI events into assistant-ui run snapshots that can be yielded from a ChatModelAdapter.\n *\n * The aggregator keeps a single assistant message worth of parts. Each incoming event updates the parts and\n * emits a fresh snapshot through the provided `emit` callback. `CUSTOM` events\n * become canonical data parts; integration plumbing names such as\n * `on_interrupt`, `PredictState`, `Exit`, `hook_error`, `state_update_error`,\n * `system:*`, and `MultiAgentHandoff` are forwarded the same way, so apps only\n * need data renderers for names they own.\n */\nexport class RunAggregator {\n private readonly emitUpdate: Emit;\n private readonly showThinking: boolean;\n private readonly logger: Logger;\n private readonly onServerMessageId: ((messageId: string) => void) | undefined;\n\n private status: ChatModelRunResult[\"status\"] | undefined;\n private interrupts: AgUiInterrupt[] | undefined;\n private readonly textParts = new Map<\n string,\n { buffer: string; touched: boolean }\n >();\n private activeTextMessageId: string | undefined;\n private readonly reasoningParts = new Map<string, string>(); // key → buffer\n private readonly reasoningSignatures = new Map<string, string>();\n private readonly reasoningMessageIds = new Map<string, string>();\n private readonly anonymousReasoningKeys = new Set<string>();\n private activeReasoningKey: string | undefined;\n private reasoningPartCounter = 0;\n private readonly toolCalls = new Map<string, ToolCallState>();\n private readonly a2uiBuckets = new Map<string, A2uiState>();\n private readonly a2uiToolCallIds = new Set<string>();\n private lastResolvedToolCallId: string | undefined;\n private readonly partOrder: (\n | { kind: \"text\"; key: string }\n | { kind: \"reasoning\"; key: string }\n | { kind: \"tool-call\"; toolCallId: string }\n | { kind: \"data\"; name: string; value: unknown }\n )[] = [];\n private textPartCounter = 0;\n private serverMessageIdReported = false;\n\n private streamStartTime: number | undefined;\n private firstTokenTime: number | undefined;\n private totalChunks = 0;\n\n constructor(options: RunAggregatorOptions) {\n this.emitUpdate = options.emit;\n this.showThinking = options.showThinking;\n this.logger = options.logger;\n this.onServerMessageId = options.onServerMessageId;\n }\n\n hasToolCall(toolCallId: string): boolean {\n return this.toolCalls.has(toolCallId);\n }\n\n handle(event: AgUiEvent): void {\n switch (event.type) {\n case \"RUN_STARTED\": {\n this.clearTextParts();\n this.reasoningParts.clear();\n this.reasoningSignatures.clear();\n this.reasoningMessageIds.clear();\n this.anonymousReasoningKeys.clear();\n this.activeReasoningKey = undefined;\n this.reasoningPartCounter = 0;\n this.toolCalls.clear();\n this.a2uiBuckets.clear();\n this.a2uiToolCallIds.clear();\n this.lastResolvedToolCallId = undefined;\n this.partOrder.length = 0;\n this.textPartCounter = 0;\n this.activeTextMessageId = undefined;\n this.interrupts = undefined;\n this.serverMessageIdReported = false;\n this.streamStartTime = Date.now();\n this.firstTokenTime = undefined;\n this.totalChunks = 0;\n this.status = { type: \"running\" };\n this.emit();\n break;\n }\n case \"RUN_FINISHED\": {\n if (event.outcome?.type === \"interrupt\") {\n this.interrupts = event.outcome.interrupts;\n this.status = { type: \"requires-action\", reason: \"interrupt\" };\n this.emit();\n break;\n }\n\n this.interrupts = undefined;\n const hasUnresolvedToolCalls = Array.from(this.toolCalls.values()).some(\n (tc) => tc.result === undefined,\n );\n\n this.status =\n event.outcome?.type === \"success\" || !hasUnresolvedToolCalls\n ? { type: \"complete\", reason: \"unknown\" }\n : { type: \"requires-action\", reason: \"tool-calls\" };\n this.emit();\n break;\n }\n case \"RUN_ERROR\": {\n this.status = {\n type: \"incomplete\",\n reason: \"error\",\n ...(event.message !== undefined ? { error: event.message } : {}),\n };\n this.emit();\n break;\n }\n case \"RUN_CANCELLED\": {\n this.status = { type: \"incomplete\", reason: \"cancelled\" };\n this.emit();\n break;\n }\n\n case \"TEXT_MESSAGE_START\": {\n this.reportServerMessageId(event.messageId);\n const id = this.startTextMessage(event.messageId);\n if (id) {\n this.markTextPartTouched(id);\n }\n this.emit();\n break;\n }\n case \"TEXT_MESSAGE_CONTENT\":\n case \"TEXT_MESSAGE_CHUNK\": {\n const incomingId = \"messageId\" in event ? event.messageId : undefined;\n this.reportServerMessageId(incomingId);\n if (!event.delta) break;\n this.recordFirstToken();\n const id = this.resolveTextMessageId(incomingId);\n this.appendText(id, event.delta);\n this.totalChunks++;\n this.emit();\n break;\n }\n case \"TEXT_MESSAGE_END\": {\n this.reportServerMessageId(event.messageId);\n if (event.messageId && this.activeTextMessageId === event.messageId) {\n this.activeTextMessageId = undefined;\n }\n this.emit();\n break;\n }\n\n case \"CUSTOM\": {\n this.activeTextMessageId = undefined;\n this.partOrder.push({\n kind: \"data\",\n name: event.name,\n value: event.value,\n });\n this.emit();\n break;\n }\n\n case \"THINKING_START\":\n case \"THINKING_TEXT_MESSAGE_START\":\n case \"REASONING_START\":\n case \"REASONING_MESSAGE_START\":\n this.handleReasoningStart(\n \"messageId\" in event ? event.messageId : undefined,\n event.type === \"REASONING_MESSAGE_START\",\n );\n break;\n case \"REASONING_ENCRYPTED_VALUE\":\n if (event.subtype === \"message\") {\n // entityId names any message, not necessarily a reasoning one, so an\n // unmatched id may only claim a block that has no id to contradict it.\n const active = this.activeReasoningKey;\n const key = this.reasoningParts.has(event.entityId)\n ? event.entityId\n : active !== undefined && this.anonymousReasoningKeys.has(active)\n ? active\n : undefined;\n if (key !== undefined) {\n this.reasoningSignatures.set(key, event.encryptedValue);\n this.emit();\n }\n }\n break;\n case \"THINKING_TEXT_MESSAGE_CONTENT\":\n this.handleReasoningContent(event.delta);\n this.totalChunks++;\n this.recordFirstToken();\n break;\n case \"REASONING_MESSAGE_CONTENT\":\n this.handleReasoningContent(\n event.delta,\n \"messageId\" in event ? event.messageId : undefined,\n true,\n );\n this.totalChunks++;\n this.recordFirstToken();\n break;\n case \"THINKING_TEXT_MESSAGE_END\":\n case \"THINKING_END\":\n case \"REASONING_MESSAGE_END\":\n case \"REASONING_END\":\n this.handleReasoningEnd();\n break;\n\n case \"TOOL_CALL_START\": {\n this.reportServerMessageId(event.parentMessageId);\n this.startToolCall(\n event.toolCallId,\n event.toolCallName,\n event.parentMessageId,\n );\n this.emit();\n break;\n }\n case \"TOOL_CALL_ARGS\":\n case \"TOOL_CALL_CHUNK\": {\n if (event.type === \"TOOL_CALL_CHUNK\") {\n this.reportServerMessageId(event.parentMessageId);\n }\n if (!event.delta) break;\n this.appendToolArgs(event.toolCallId, event.delta);\n this.emit();\n break;\n }\n case \"TOOL_CALL_END\": {\n this.emit();\n break;\n }\n case \"TOOL_CALL_RESULT\": {\n this.finishToolCall(\n event.toolCallId,\n event.content ?? \"\",\n typeof event.mcpResult?.isError === \"boolean\"\n ? event.mcpResult.isError\n : event.role === \"tool\"\n ? false\n : undefined,\n event.messageId,\n event.mcpResult,\n );\n this.emit();\n break;\n }\n case \"ACTIVITY_SNAPSHOT\": {\n if (event.activityType === A2UI_SURFACE_ACTIVITY_TYPE) {\n this.handleA2uiActivitySnapshot(event);\n break;\n }\n if (event.activityType !== MCP_APPS_ACTIVITY_TYPE) break;\n const toolCallId = event.content.toolCallId;\n const entry =\n typeof toolCallId === \"string\"\n ? this.toolCalls.get(toolCallId)\n : this.lastResolvedToolCallId\n ? this.toolCalls.get(this.lastResolvedToolCallId)\n : undefined;\n const resourceUri = event.content.resourceUri;\n if (\n entry &&\n typeof resourceUri === \"string\" &&\n isMcpAppUri(resourceUri)\n ) {\n entry.mcpAppResourceUri = resourceUri;\n const id = event.content.serverId;\n const hash = event.content.serverHash;\n if (typeof id === \"string\" && id.length > 0) {\n entry.mcpAppServerId = id;\n } else if (typeof hash === \"string\" && hash.length > 0) {\n entry.mcpAppServerId = hash;\n }\n const result = event.content.result;\n if (isPlainObject(result)) {\n if (\n entry.result !== undefined &&\n entry.modelContent === undefined\n ) {\n entry.modelContent = [\n {\n type: \"text\",\n text:\n typeof entry.result === \"string\"\n ? entry.result\n : JSON.stringify(entry.result),\n },\n ];\n }\n entry.result = result;\n entry.snapshotResultApplied = true;\n if (typeof result.isError === \"boolean\") {\n entry.isError = result.isError;\n }\n }\n this.emit();\n }\n break;\n }\n\n default: {\n this.logger.debug?.(\"[agui] aggregator ignored event\", event);\n }\n }\n }\n\n private handleA2uiActivitySnapshot(\n event: Extract<AgUiEvent, { type: \"ACTIVITY_SNAPSHOT\" }>,\n ): void {\n const operations = event.content[\"a2ui_operations\"];\n if (!Array.isArray(operations)) return;\n\n const messageId = event.messageId ?? \"a2ui:anonymous\";\n if (event.replace === false && this.a2uiBuckets.has(messageId)) return;\n\n const { state, warnings } = applyA2uiOperations(new Map(), operations);\n for (const warning of warnings) {\n this.logger.debug(\"[agui] a2ui operation warning\", warning);\n }\n this.a2uiBuckets.delete(messageId);\n this.a2uiBuckets.set(messageId, state);\n this.synthesizeA2uiToolCalls();\n this.emit();\n }\n\n private synthesizeA2uiToolCalls(): void {\n const surfaces = new Map<string, A2uiSurfaceState>();\n\n for (const bucket of this.a2uiBuckets.values()) {\n for (const [surfaceId, surface] of bucket) {\n surfaces.set(surfaceId, surface);\n }\n }\n\n const activeToolCallIds = new Set<string>();\n for (const [surfaceId, surface] of surfaces) {\n const toolCallId = `a2ui:${surfaceId}`;\n const { spec, warnings } = convertSurfaceToUISpec(surface);\n for (const warning of warnings) {\n this.logger.debug(\"[agui] a2ui surface conversion warning\", warning);\n }\n if (!spec) continue;\n\n activeToolCallIds.add(toolCallId);\n\n const entry: ToolCallState = {\n toolCallId,\n toolCallName: \"present\",\n argsText: JSON.stringify(spec),\n parsedArgs: spec,\n result: {},\n isError: undefined,\n snapshotResultApplied: false,\n };\n if (!this.toolCalls.has(toolCallId)) {\n this.partOrder.push({ kind: \"tool-call\", toolCallId });\n }\n this.toolCalls.set(toolCallId, entry);\n this.a2uiToolCallIds.add(toolCallId);\n }\n\n for (const toolCallId of this.a2uiToolCallIds) {\n if (activeToolCallIds.has(toolCallId)) continue;\n this.toolCalls.delete(toolCallId);\n const partIndex = this.partOrder.findIndex(\n (part) => part.kind === \"tool-call\" && part.toolCallId === toolCallId,\n );\n if (partIndex !== -1) this.partOrder.splice(partIndex, 1);\n this.a2uiToolCallIds.delete(toolCallId);\n }\n }\n\n private reportServerMessageId(messageId: string | undefined): void {\n if (this.serverMessageIdReported || !messageId) return;\n this.serverMessageIdReported = true;\n this.onServerMessageId?.(messageId);\n }\n\n private clearTextParts(): void {\n this.textParts.clear();\n }\n\n private generateTextKey(): string {\n this.textPartCounter += 1;\n return `text-${this.textPartCounter}`;\n }\n\n private startTextMessage(messageId?: string): string {\n const id = messageId ?? this.generateTextKey();\n this.ensureTextPart(id);\n this.activeTextMessageId = id;\n return id;\n }\n\n private resolveTextMessageId(messageId?: string): string {\n if (messageId) {\n this.ensureTextPart(messageId);\n this.activeTextMessageId = messageId;\n return messageId;\n }\n\n if (this.activeTextMessageId) {\n return this.activeTextMessageId;\n }\n\n const generated = this.generateTextKey();\n this.ensureTextPart(generated);\n this.activeTextMessageId = generated;\n return generated;\n }\n\n private ensureTextPart(id: string): void {\n if (!this.textParts.has(id)) {\n this.textParts.set(id, { buffer: \"\", touched: false });\n if (\n !this.partOrder.some((part) => part.kind === \"text\" && part.key === id)\n ) {\n this.partOrder.push({ kind: \"text\", key: id });\n }\n }\n }\n\n private markTextPartTouched(id: string): void {\n const entry = this.textParts.get(id);\n if (!entry) return;\n entry.touched = true;\n }\n\n private appendText(id: string, delta: string): void {\n this.ensureTextPart(id);\n const entry = this.textParts.get(id);\n if (!entry) return;\n entry.buffer += delta;\n entry.touched = true;\n }\n\n private startToolCall(\n id: string | undefined,\n name?: string,\n parentMessageId?: string,\n ) {\n if (!id) return;\n // A new tool call acts as a boundary: any anonymous text that arrives\n // after it should be a new part, not appended to the pre-tool text.\n this.activeTextMessageId = undefined;\n if (\n !this.partOrder.some(\n (part) => part.kind === \"tool-call\" && part.toolCallId === id,\n )\n ) {\n this.insertToolPart(id, parentMessageId);\n }\n const state: ToolCallState = {\n toolCallId: id,\n toolCallName: name ?? \"tool\",\n argsText: \"\",\n parsedArgs: undefined,\n result: undefined,\n isError: undefined,\n snapshotResultApplied: false,\n };\n if (parentMessageId) {\n state.parentMessageId = parentMessageId;\n }\n this.toolCalls.set(id, state);\n }\n\n // The message and tool-call channels are unordered on the wire, so anchor a\n // tool call under its parentMessageId text part instead of appending it.\n private insertToolPart(id: string, parentMessageId?: string) {\n const entry = { kind: \"tool-call\", toolCallId: id } as const;\n if (parentMessageId) {\n const parentIndex = this.partOrder.findIndex(\n (part) => part.kind === \"text\" && part.key === parentMessageId,\n );\n if (parentIndex !== -1) {\n let insertAt = parentIndex + 1;\n while (insertAt < this.partOrder.length) {\n const part = this.partOrder[insertAt];\n if (\n part?.kind === \"tool-call\" &&\n this.toolCalls.get(part.toolCallId)?.parentMessageId ===\n parentMessageId\n ) {\n insertAt += 1;\n continue;\n }\n break;\n }\n this.partOrder.splice(insertAt, 0, entry);\n return;\n }\n }\n this.partOrder.push(entry);\n }\n\n private appendToolArgs(id: string | undefined, delta: string) {\n const entry = id ? this.toolCalls.get(id) : undefined;\n if (!entry) return;\n entry.argsText += delta;\n try {\n const parsed = JSON.parse(entry.argsText);\n if (parsed && typeof parsed === \"object\") {\n entry.parsedArgs = parsed as Record<string, unknown>;\n } else {\n entry.parsedArgs = undefined;\n }\n } catch {\n entry.parsedArgs = undefined;\n }\n }\n\n private finishToolCall(\n id: string,\n content: string,\n isError?: boolean,\n toolMessageId?: string,\n mcpResult?: Record<string, unknown>,\n ) {\n if (!id) return;\n let entry = this.toolCalls.get(id);\n if (!entry) {\n entry = {\n toolCallId: id,\n toolCallName: \"tool\",\n argsText: \"\",\n parsedArgs: undefined,\n result: undefined,\n isError: undefined,\n snapshotResultApplied: false,\n };\n this.toolCalls.set(id, entry);\n }\n if (\n !this.partOrder.some(\n (part) => part.kind === \"tool-call\" && part.toolCallId === id,\n )\n ) {\n this.partOrder.push({ kind: \"tool-call\", toolCallId: id });\n }\n if (mcpResult !== undefined && !entry.snapshotResultApplied) {\n entry.result = mcpResult;\n entry.modelContent = [{ type: \"text\", text: content }];\n entry.snapshotResultApplied = true;\n entry.isError = isError;\n if (entry.mcpAppResourceUri === undefined) {\n const uri = readMcpAppResourceUri(mcpResult._meta);\n if (uri !== undefined) entry.mcpAppResourceUri = uri;\n }\n } else if (entry.snapshotResultApplied) {\n if (entry.modelContent === undefined) {\n entry.modelContent = [{ type: \"text\", text: content }];\n }\n if (entry.isError === undefined) {\n entry.isError = isError;\n }\n } else {\n entry.result = tryParseJSON(content);\n entry.isError = isError;\n }\n if (toolMessageId) {\n entry.toolMessageId = toolMessageId;\n }\n this.lastResolvedToolCallId = id;\n }\n\n private emit(): void {\n const snapshot: ThreadAssistantMessagePart[] = [];\n // A run that ended incomplete can no longer be resumed, so a gate left over\n // from an earlier interrupt outcome is unanswerable and must not stay\n // projected. The interrupts themselves are kept on the message, since the\n // bespoke hooks read that payload.\n const approvals = projectAgUiToolApprovals(\n this.status?.type === \"requires-action\" ? this.interrupts : undefined,\n new Set(this.toolCalls.keys()),\n );\n\n for (const part of this.partOrder) {\n if (part.kind === \"reasoning\") {\n if (this.showThinking) {\n const buffer = this.reasoningParts.get(part.key) ?? \"\";\n if (buffer.length > 0 || this.activeReasoningKey === part.key) {\n const encryptedValue = this.reasoningSignatures.get(part.key);\n const reasoningId = this.reasoningMessageIds.get(part.key);\n const meta = {\n ...(reasoningId !== undefined ? { reasoningId } : {}),\n ...(encryptedValue !== undefined ? { encryptedValue } : {}),\n };\n snapshot.push({\n type: \"reasoning\",\n text: buffer,\n ...(Object.keys(meta).length > 0\n ? { providerMetadata: { [AG_UI_METADATA_NAMESPACE]: meta } }\n : {}),\n } as const);\n }\n }\n continue;\n }\n\n if (part.kind === \"text\") {\n const entry = this.textParts.get(part.key);\n if (entry?.touched) {\n snapshot.push({ type: \"text\", text: entry.buffer } as const);\n }\n continue;\n }\n\n if (part.kind === \"data\") {\n snapshot.push({\n type: \"data\",\n name: part.name,\n data: part.value,\n });\n continue;\n }\n\n const entry = this.toolCalls.get(part.toolCallId);\n if (!entry) continue;\n const approval = approvals.get(entry.toolCallId);\n const toolPart: ToolCallMessagePart = {\n type: \"tool-call\",\n toolCallId: entry.toolCallId,\n toolName: entry.toolCallName,\n args: (entry.parsedArgs ?? {}) as any,\n argsText: entry.argsText,\n ...(approval ? { approval } : {}),\n ...(entry.result !== undefined ? { result: entry.result } : {}),\n ...(entry.modelContent !== undefined\n ? { modelContent: entry.modelContent }\n : {}),\n ...(entry.isError !== undefined ? { isError: entry.isError } : {}),\n ...(entry.mcpAppResourceUri\n ? {\n mcp: {\n app: {\n resourceUri: entry.mcpAppResourceUri,\n ...(entry.mcpAppServerId\n ? { serverId: entry.mcpAppServerId }\n : {}),\n },\n },\n }\n : {}),\n ...(entry.parentMessageId ? { parentId: entry.parentMessageId } : {}),\n ...(entry.toolMessageId\n ? { unstable_toolMessageId: entry.toolMessageId }\n : {}),\n } as ToolCallMessagePart & { unstable_toolMessageId?: string };\n snapshot.push(toolPart);\n }\n\n const timing = this.getTiming();\n const metadata = {\n ...(timing ? { timing } : {}),\n ...(this.interrupts\n ? {\n custom: {\n [AG_UI_METADATA_NAMESPACE]: {\n interrupts: this.interrupts,\n } satisfies AgUiCustomMetadata,\n },\n }\n : {}),\n };\n const result: ChatModelRunResult = {\n content: snapshot,\n ...(this.status ? { status: this.status } : undefined),\n ...(Object.keys(metadata).length > 0 ? { metadata } : undefined),\n };\n this.emitUpdate(result);\n }\n\n private recordFirstToken(): void {\n if (\n this.firstTokenTime === undefined &&\n this.streamStartTime !== undefined\n ) {\n this.firstTokenTime = Date.now() - this.streamStartTime;\n }\n }\n\n private getTiming(): MessageTiming | undefined {\n if (this.streamStartTime === undefined) return undefined;\n\n const now = Date.now();\n const totalStreamTime = now - this.streamStartTime;\n const tokenCount =\n this.totalChunks > 0\n ? Math.ceil(\n Array.from(this.textParts.values()).reduce(\n (sum, p) => sum + p.buffer.length,\n 0,\n ) / 4,\n )\n : undefined;\n const tokensPerSecond =\n tokenCount && totalStreamTime > 0\n ? (tokenCount / totalStreamTime) * 1000\n : undefined;\n\n return {\n streamStartTime: this.streamStartTime,\n ...(this.firstTokenTime !== undefined\n ? { firstTokenTime: this.firstTokenTime }\n : {}),\n totalStreamTime,\n ...(tokenCount !== undefined ? { tokenCount } : {}),\n ...(tokensPerSecond !== undefined ? { tokensPerSecond } : {}),\n totalChunks: this.totalChunks,\n toolCallCount: this.toolCalls.size,\n };\n }\n\n private handleReasoningStart(messageId?: string, isMessageId = false): void {\n if (!this.showThinking) return;\n // A reasoning block acts as a boundary: anonymous text arriving after it\n // should be a new part, not appended to any pre-reasoning text.\n this.activeTextMessageId = undefined;\n const key = messageId ?? `__auto-reasoning-${++this.reasoningPartCounter}`;\n // Two different questions: which id may be replayed as a ReasoningMessage.id\n // (only the message-scoped aliases carry one), and which block an unmatched\n // signature may claim (any block opened without an id at all).\n if (messageId === undefined) {\n this.anonymousReasoningKeys.add(key);\n } else if (isMessageId) {\n this.reasoningMessageIds.set(key, messageId);\n }\n if (!this.reasoningParts.has(key)) {\n this.reasoningParts.set(key, \"\");\n this.partOrder.push({ kind: \"reasoning\", key });\n }\n this.activeReasoningKey = key;\n this.emit();\n }\n\n private handleReasoningContent(\n delta: string,\n messageId?: string,\n isMessageId = false,\n ): void {\n if (!this.showThinking || !delta) return;\n if (!this.activeReasoningKey) {\n // Content arrived without a preceding START — create the slot lazily.\n this.handleReasoningStart(messageId, isMessageId);\n }\n const key = this.activeReasoningKey;\n if (!key) return;\n this.reasoningParts.set(key, (this.reasoningParts.get(key) ?? \"\") + delta);\n this.emit();\n }\n\n private handleReasoningEnd(): void {\n if (!this.showThinking) return;\n this.activeReasoningKey = undefined;\n this.emit();\n }\n}\n\nexport function tryParseJSON(value: string): unknown {\n if (!value) return value;\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n}\n"],"mappings":"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"}
@@ -87,6 +87,9 @@ type Subscriber = {
87
87
  onRunFinishedEvent?: (payload: {
88
88
  event: unknown;
89
89
  }) => void;
90
+ onRunErrorEvent?: (payload: {
91
+ event: unknown;
92
+ }) => void;
90
93
  onRunFinalized?: () => void;
91
94
  onRunFailed?: (payload: {
92
95
  error: Error;
@@ -1 +1 @@
1
- {"version":3,"file":"subscriber.d.ts","names":[],"sources":["../../../src/runtime/adapter/subscriber.ts"],"mappings":";;;KAMK,YAAY,OAAO;KAEnB;EACH,WAAW;IAAW;;EACtB,2BAA2B;IAAW;;EACtC,6BAA6B;IAAW;;EACxC,yBAAyB;IAAW;;EACpC,2BAA2B;IAAW;;EACtC,wBAAwB;IAAW;;EACnC,sBAAsB;IAAW;;EACjC,mCAAmC;IAAW;;EAC9C,qCAAqC;IAAW;;EAChD,iCAAiC;IAAW;;EAC5C,yBAAyB;IAAW;;EACpC,uBAAuB;IAAW;;EAClC,gCAAgC;IAAW;;EAC3C,kCAAkC;IAAW;;EAC7C,8BAA8B;IAAW;;EACzC,kCAAkC;IAAW;;EAC7C,wBAAwB;IAAW;;EACnC,uBAAuB;IAAW;;EAClC,sBAAsB;IAAW;;EACjC,wBAAwB;IAAW;;EACnC,yBAAyB;IAAW;;EACpC,2BAA2B;IAAW;;EACtC,wBAAwB;IAAW;;EACnC,qBAAqB;IAAW;;EAChC,2BAA2B;IAAW;;EACtC,iBAAiB;IAAW;;EAC5B,cAAc;IAAW;;EACzB,sBAAsB;IAAW;;EACjC;EACA,eAAe;IAAW,OAAO;;;KAiC9B;EACH,UAAU;EACV;EACA,SAAS;EACT,eAAe,OAAO;;cAGX,uBACX,SAAS,sBACR;KAmGS,iBAAiB,kBAAkB"}
1
+ {"version":3,"file":"subscriber.d.ts","names":[],"sources":["../../../src/runtime/adapter/subscriber.ts"],"mappings":";;;KAMK,YAAY,OAAO;KAEnB;EACH,WAAW;IAAW;;EACtB,2BAA2B;IAAW;;EACtC,6BAA6B;IAAW;;EACxC,yBAAyB;IAAW;;EACpC,2BAA2B;IAAW;;EACtC,wBAAwB;IAAW;;EACnC,sBAAsB;IAAW;;EACjC,mCAAmC;IAAW;;EAC9C,qCAAqC;IAAW;;EAChD,iCAAiC;IAAW;;EAC5C,yBAAyB;IAAW;;EACpC,uBAAuB;IAAW;;EAClC,gCAAgC;IAAW;;EAC3C,kCAAkC;IAAW;;EAC7C,8BAA8B;IAAW;;EACzC,kCAAkC;IAAW;;EAC7C,wBAAwB;IAAW;;EACnC,uBAAuB;IAAW;;EAClC,sBAAsB;IAAW;;EACjC,wBAAwB;IAAW;;EACnC,yBAAyB;IAAW;;EACpC,2BAA2B;IAAW;;EACtC,wBAAwB;IAAW;;EACnC,qBAAqB;IAAW;;EAChC,2BAA2B;IAAW;;EACtC,iBAAiB;IAAW;;EAC5B,cAAc;IAAW;;EACzB,sBAAsB;IAAW;;EACjC,mBAAmB;IAAW;;EAC9B;EACA,eAAe;IAAW,OAAO;;;KAiC9B;EACH,UAAU;EACV;EACA,SAAS;EACT,eAAe,OAAO;;cAGX,uBACX,SAAS,sBACR;KAgHS,iBAAiB,kBAAkB"}
@@ -60,11 +60,20 @@ const createAgUiSubscriber = (options) => {
60
60
  onCustomEvent: ({ event }) => dispatchIfValid(event, "CUSTOM"),
61
61
  onRawEvent: ({ event }) => dispatchIfValid(event, "RAW"),
62
62
  onRunFinishedEvent: ({ event }) => {
63
+ if (runFinishedDispatched) return;
63
64
  const parsed = ensureEvent(event, "RUN_FINISHED", logger);
64
65
  if (!parsed) return;
65
66
  runFinishedDispatched = true;
66
67
  dispatch(parsed);
67
68
  },
69
+ onRunErrorEvent: ({ event }) => {
70
+ const parsed = ensureEvent(event, "RUN_ERROR", logger);
71
+ if (!parsed || parsed.type !== "RUN_ERROR") return;
72
+ runFinishedDispatched = true;
73
+ dispatch(parsed);
74
+ const error = Object.assign(new Error(parsed.message ?? "Run failed"), parsed.code === void 0 ? {} : { code: parsed.code });
75
+ onRunFailed?.(error);
76
+ },
68
77
  onRunFinalized: () => {
69
78
  if (runFinishedDispatched) return;
70
79
  dispatch({
@@ -1 +1 @@
1
- {"version":3,"file":"subscriber.js","names":[],"sources":["../../../src/runtime/adapter/subscriber.ts"],"sourcesContent":["\"use client\";\n\nimport type { AgUiEvent } from \"../types\";\nimport { parseAgUiEvent } from \"../event-parser\";\nimport type { Logger } from \"../logger\";\n\ntype Dispatch = (event: AgUiEvent) => void;\n\ntype Subscriber = {\n onEvent?: (payload: { event: unknown }) => void;\n onTextMessageStartEvent?: (payload: { event: unknown }) => void;\n onTextMessageContentEvent?: (payload: { event: unknown }) => void;\n onTextMessageEndEvent?: (payload: { event: unknown }) => void;\n onTextMessageChunkEvent?: (payload: { event: unknown }) => void;\n onThinkingStartEvent?: (payload: { event: unknown }) => void;\n onThinkingEndEvent?: (payload: { event: unknown }) => void;\n onThinkingTextMessageStartEvent?: (payload: { event: unknown }) => void;\n onThinkingTextMessageContentEvent?: (payload: { event: unknown }) => void;\n onThinkingTextMessageEndEvent?: (payload: { event: unknown }) => void;\n onReasoningStartEvent?: (payload: { event: unknown }) => void;\n onReasoningEndEvent?: (payload: { event: unknown }) => void;\n onReasoningMessageStartEvent?: (payload: { event: unknown }) => void;\n onReasoningMessageContentEvent?: (payload: { event: unknown }) => void;\n onReasoningMessageEndEvent?: (payload: { event: unknown }) => void;\n onReasoningEncryptedValueEvent?: (payload: { event: unknown }) => void;\n onToolCallStartEvent?: (payload: { event: unknown }) => void;\n onToolCallArgsEvent?: (payload: { event: unknown }) => void;\n onToolCallEndEvent?: (payload: { event: unknown }) => void;\n onToolCallChunkEvent?: (payload: { event: unknown }) => void;\n onToolCallResultEvent?: (payload: { event: unknown }) => void;\n onActivitySnapshotEvent?: (payload: { event: unknown }) => void;\n onStateSnapshotEvent?: (payload: { event: unknown }) => void;\n onStateDeltaEvent?: (payload: { event: unknown }) => void;\n onMessagesSnapshotEvent?: (payload: { event: unknown }) => void;\n onCustomEvent?: (payload: { event: unknown }) => void;\n onRawEvent?: (payload: { event: unknown }) => void;\n onRunFinishedEvent?: (payload: { event: unknown }) => void;\n onRunFinalized?: () => void;\n onRunFailed?: (payload: { error: Error }) => void;\n};\n\nconst isAbortError = (error: Error): boolean => {\n if (error.name === \"AbortError\") return true;\n const message = typeof error.message === \"string\" ? error.message : \"\";\n return (\n message === \"Fetch is aborted\" ||\n message === \"signal is aborted without reason\" ||\n message === \"component unmounted\"\n );\n};\n\nconst ensureEvent = (\n raw: unknown,\n type: AgUiEvent[\"type\"],\n logger?: Logger,\n): AgUiEvent | null => {\n const parseOptions = logger ? { logger } : undefined;\n let parsed: AgUiEvent | null;\n if (raw && typeof raw === \"object\") {\n const payload = raw as Record<string, unknown>;\n if (typeof payload.type === \"string\") {\n parsed = parseAgUiEvent(payload, parseOptions);\n } else {\n parsed = parseAgUiEvent({ type, ...payload }, parseOptions);\n }\n } else {\n parsed = parseAgUiEvent({ type }, parseOptions);\n }\n return parsed && parsed.type === type ? parsed : null;\n};\n\ntype SubscriberOptions = {\n dispatch: Dispatch;\n runId: string;\n logger?: Logger;\n onRunFailed?: (error: Error) => void;\n};\n\nexport const createAgUiSubscriber = (\n options: SubscriberOptions,\n): Subscriber => {\n const { dispatch, runId, logger, onRunFailed } = options;\n let runFinishedDispatched = false;\n const dispatchIfValid = (raw: unknown, type: AgUiEvent[\"type\"]) => {\n const event = ensureEvent(raw, type, logger);\n if (!event) return;\n dispatch(event);\n };\n return {\n onEvent: ({ event }) => {\n const typeCandidate =\n event && typeof event === \"object\"\n ? (event as Record<string, unknown>).type\n : undefined;\n if (typeof typeCandidate === \"string\") {\n // Typed handlers will receive this via the discriminated callbacks; avoid duplicates.\n return;\n }\n const parsed = parseAgUiEvent(event);\n if (parsed) dispatch(parsed);\n },\n onTextMessageStartEvent: ({ event }) =>\n dispatchIfValid(event, \"TEXT_MESSAGE_START\"),\n onTextMessageContentEvent: ({ event }) =>\n dispatchIfValid(event, \"TEXT_MESSAGE_CONTENT\"),\n onTextMessageEndEvent: ({ event }) =>\n dispatchIfValid(event, \"TEXT_MESSAGE_END\"),\n onTextMessageChunkEvent: ({ event }) =>\n dispatchIfValid(event, \"TEXT_MESSAGE_CHUNK\"),\n onThinkingStartEvent: ({ event }) =>\n dispatchIfValid(event, \"THINKING_START\"),\n onThinkingEndEvent: ({ event }) => dispatchIfValid(event, \"THINKING_END\"),\n onThinkingTextMessageStartEvent: ({ event }) =>\n dispatchIfValid(event, \"THINKING_TEXT_MESSAGE_START\"),\n onThinkingTextMessageContentEvent: ({ event }) =>\n dispatchIfValid(event, \"THINKING_TEXT_MESSAGE_CONTENT\"),\n onThinkingTextMessageEndEvent: ({ event }) =>\n dispatchIfValid(event, \"THINKING_TEXT_MESSAGE_END\"),\n onReasoningStartEvent: ({ event }) =>\n dispatchIfValid(event, \"REASONING_START\"),\n onReasoningEndEvent: ({ event }) => dispatchIfValid(event, \"REASONING_END\"),\n onReasoningMessageStartEvent: ({ event }) =>\n dispatchIfValid(event, \"REASONING_MESSAGE_START\"),\n onReasoningMessageContentEvent: ({ event }) =>\n dispatchIfValid(event, \"REASONING_MESSAGE_CONTENT\"),\n onReasoningMessageEndEvent: ({ event }) =>\n dispatchIfValid(event, \"REASONING_MESSAGE_END\"),\n onReasoningEncryptedValueEvent: ({ event }) =>\n dispatchIfValid(event, \"REASONING_ENCRYPTED_VALUE\"),\n onToolCallStartEvent: ({ event }) =>\n dispatchIfValid(event, \"TOOL_CALL_START\"),\n onToolCallArgsEvent: ({ event }) =>\n dispatchIfValid(event, \"TOOL_CALL_ARGS\"),\n onToolCallEndEvent: ({ event }) => dispatchIfValid(event, \"TOOL_CALL_END\"),\n onToolCallChunkEvent: ({ event }) =>\n dispatchIfValid(event, \"TOOL_CALL_CHUNK\"),\n onToolCallResultEvent: ({ event }) =>\n dispatchIfValid(event, \"TOOL_CALL_RESULT\"),\n onActivitySnapshotEvent: ({ event }) =>\n dispatchIfValid(event, \"ACTIVITY_SNAPSHOT\"),\n onStateSnapshotEvent: ({ event }) =>\n dispatchIfValid(event, \"STATE_SNAPSHOT\"),\n onStateDeltaEvent: ({ event }) => dispatchIfValid(event, \"STATE_DELTA\"),\n onMessagesSnapshotEvent: ({ event }) =>\n dispatchIfValid(event, \"MESSAGES_SNAPSHOT\"),\n onCustomEvent: ({ event }) => dispatchIfValid(event, \"CUSTOM\"),\n onRawEvent: ({ event }) => dispatchIfValid(event, \"RAW\"),\n onRunFinishedEvent: ({ event }) => {\n const parsed = ensureEvent(event, \"RUN_FINISHED\", logger);\n if (!parsed) return;\n runFinishedDispatched = true;\n dispatch(parsed);\n },\n onRunFinalized: () => {\n if (runFinishedDispatched) return;\n dispatch({ type: \"RUN_FINISHED\", runId });\n },\n onRunFailed: ({ error }) => {\n runFinishedDispatched = true;\n onRunFailed?.(error);\n if (isAbortError(error)) {\n dispatch({ type: \"RUN_CANCELLED\" } satisfies AgUiEvent);\n return;\n }\n const message =\n typeof error.message === \"string\" ? error.message : \"Run failed\";\n const code =\n typeof (error as any)?.code === \"string\"\n ? (error as any).code\n : undefined;\n dispatch({\n type: \"RUN_ERROR\" as const,\n ...(message !== undefined ? { message } : {}),\n ...(code !== undefined ? { code } : {}),\n } satisfies AgUiEvent);\n },\n };\n};\n\nexport type AgUiSubscriber = ReturnType<typeof createAgUiSubscriber>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"subscriber.js","names":[],"sources":["../../../src/runtime/adapter/subscriber.ts"],"sourcesContent":["\"use client\";\n\nimport type { AgUiEvent } from \"../types\";\nimport { parseAgUiEvent } from \"../event-parser\";\nimport type { Logger } from \"../logger\";\n\ntype Dispatch = (event: AgUiEvent) => void;\n\ntype Subscriber = {\n onEvent?: (payload: { event: unknown }) => void;\n onTextMessageStartEvent?: (payload: { event: unknown }) => void;\n onTextMessageContentEvent?: (payload: { event: unknown }) => void;\n onTextMessageEndEvent?: (payload: { event: unknown }) => void;\n onTextMessageChunkEvent?: (payload: { event: unknown }) => void;\n onThinkingStartEvent?: (payload: { event: unknown }) => void;\n onThinkingEndEvent?: (payload: { event: unknown }) => void;\n onThinkingTextMessageStartEvent?: (payload: { event: unknown }) => void;\n onThinkingTextMessageContentEvent?: (payload: { event: unknown }) => void;\n onThinkingTextMessageEndEvent?: (payload: { event: unknown }) => void;\n onReasoningStartEvent?: (payload: { event: unknown }) => void;\n onReasoningEndEvent?: (payload: { event: unknown }) => void;\n onReasoningMessageStartEvent?: (payload: { event: unknown }) => void;\n onReasoningMessageContentEvent?: (payload: { event: unknown }) => void;\n onReasoningMessageEndEvent?: (payload: { event: unknown }) => void;\n onReasoningEncryptedValueEvent?: (payload: { event: unknown }) => void;\n onToolCallStartEvent?: (payload: { event: unknown }) => void;\n onToolCallArgsEvent?: (payload: { event: unknown }) => void;\n onToolCallEndEvent?: (payload: { event: unknown }) => void;\n onToolCallChunkEvent?: (payload: { event: unknown }) => void;\n onToolCallResultEvent?: (payload: { event: unknown }) => void;\n onActivitySnapshotEvent?: (payload: { event: unknown }) => void;\n onStateSnapshotEvent?: (payload: { event: unknown }) => void;\n onStateDeltaEvent?: (payload: { event: unknown }) => void;\n onMessagesSnapshotEvent?: (payload: { event: unknown }) => void;\n onCustomEvent?: (payload: { event: unknown }) => void;\n onRawEvent?: (payload: { event: unknown }) => void;\n onRunFinishedEvent?: (payload: { event: unknown }) => void;\n onRunErrorEvent?: (payload: { event: unknown }) => void;\n onRunFinalized?: () => void;\n onRunFailed?: (payload: { error: Error }) => void;\n};\n\nconst isAbortError = (error: Error): boolean => {\n if (error.name === \"AbortError\") return true;\n const message = typeof error.message === \"string\" ? error.message : \"\";\n return (\n message === \"Fetch is aborted\" ||\n message === \"signal is aborted without reason\" ||\n message === \"component unmounted\"\n );\n};\n\nconst ensureEvent = (\n raw: unknown,\n type: AgUiEvent[\"type\"],\n logger?: Logger,\n): AgUiEvent | null => {\n const parseOptions = logger ? { logger } : undefined;\n let parsed: AgUiEvent | null;\n if (raw && typeof raw === \"object\") {\n const payload = raw as Record<string, unknown>;\n if (typeof payload.type === \"string\") {\n parsed = parseAgUiEvent(payload, parseOptions);\n } else {\n parsed = parseAgUiEvent({ type, ...payload }, parseOptions);\n }\n } else {\n parsed = parseAgUiEvent({ type }, parseOptions);\n }\n return parsed && parsed.type === type ? parsed : null;\n};\n\ntype SubscriberOptions = {\n dispatch: Dispatch;\n runId: string;\n logger?: Logger;\n onRunFailed?: (error: Error) => void;\n};\n\nexport const createAgUiSubscriber = (\n options: SubscriberOptions,\n): Subscriber => {\n const { dispatch, runId, logger, onRunFailed } = options;\n let runFinishedDispatched = false;\n const dispatchIfValid = (raw: unknown, type: AgUiEvent[\"type\"]) => {\n const event = ensureEvent(raw, type, logger);\n if (!event) return;\n dispatch(event);\n };\n return {\n onEvent: ({ event }) => {\n const typeCandidate =\n event && typeof event === \"object\"\n ? (event as Record<string, unknown>).type\n : undefined;\n if (typeof typeCandidate === \"string\") {\n // Typed handlers will receive this via the discriminated callbacks; avoid duplicates.\n return;\n }\n const parsed = parseAgUiEvent(event);\n if (parsed) dispatch(parsed);\n },\n onTextMessageStartEvent: ({ event }) =>\n dispatchIfValid(event, \"TEXT_MESSAGE_START\"),\n onTextMessageContentEvent: ({ event }) =>\n dispatchIfValid(event, \"TEXT_MESSAGE_CONTENT\"),\n onTextMessageEndEvent: ({ event }) =>\n dispatchIfValid(event, \"TEXT_MESSAGE_END\"),\n onTextMessageChunkEvent: ({ event }) =>\n dispatchIfValid(event, \"TEXT_MESSAGE_CHUNK\"),\n onThinkingStartEvent: ({ event }) =>\n dispatchIfValid(event, \"THINKING_START\"),\n onThinkingEndEvent: ({ event }) => dispatchIfValid(event, \"THINKING_END\"),\n onThinkingTextMessageStartEvent: ({ event }) =>\n dispatchIfValid(event, \"THINKING_TEXT_MESSAGE_START\"),\n onThinkingTextMessageContentEvent: ({ event }) =>\n dispatchIfValid(event, \"THINKING_TEXT_MESSAGE_CONTENT\"),\n onThinkingTextMessageEndEvent: ({ event }) =>\n dispatchIfValid(event, \"THINKING_TEXT_MESSAGE_END\"),\n onReasoningStartEvent: ({ event }) =>\n dispatchIfValid(event, \"REASONING_START\"),\n onReasoningEndEvent: ({ event }) => dispatchIfValid(event, \"REASONING_END\"),\n onReasoningMessageStartEvent: ({ event }) =>\n dispatchIfValid(event, \"REASONING_MESSAGE_START\"),\n onReasoningMessageContentEvent: ({ event }) =>\n dispatchIfValid(event, \"REASONING_MESSAGE_CONTENT\"),\n onReasoningMessageEndEvent: ({ event }) =>\n dispatchIfValid(event, \"REASONING_MESSAGE_END\"),\n onReasoningEncryptedValueEvent: ({ event }) =>\n dispatchIfValid(event, \"REASONING_ENCRYPTED_VALUE\"),\n onToolCallStartEvent: ({ event }) =>\n dispatchIfValid(event, \"TOOL_CALL_START\"),\n onToolCallArgsEvent: ({ event }) =>\n dispatchIfValid(event, \"TOOL_CALL_ARGS\"),\n onToolCallEndEvent: ({ event }) => dispatchIfValid(event, \"TOOL_CALL_END\"),\n onToolCallChunkEvent: ({ event }) =>\n dispatchIfValid(event, \"TOOL_CALL_CHUNK\"),\n onToolCallResultEvent: ({ event }) =>\n dispatchIfValid(event, \"TOOL_CALL_RESULT\"),\n onActivitySnapshotEvent: ({ event }) =>\n dispatchIfValid(event, \"ACTIVITY_SNAPSHOT\"),\n onStateSnapshotEvent: ({ event }) =>\n dispatchIfValid(event, \"STATE_SNAPSHOT\"),\n onStateDeltaEvent: ({ event }) => dispatchIfValid(event, \"STATE_DELTA\"),\n onMessagesSnapshotEvent: ({ event }) =>\n dispatchIfValid(event, \"MESSAGES_SNAPSHOT\"),\n onCustomEvent: ({ event }) => dispatchIfValid(event, \"CUSTOM\"),\n onRawEvent: ({ event }) => dispatchIfValid(event, \"RAW\"),\n onRunFinishedEvent: ({ event }) => {\n if (runFinishedDispatched) return;\n const parsed = ensureEvent(event, \"RUN_FINISHED\", logger);\n if (!parsed) return;\n runFinishedDispatched = true;\n dispatch(parsed);\n },\n onRunErrorEvent: ({ event }) => {\n const parsed = ensureEvent(event, \"RUN_ERROR\", logger);\n if (!parsed || parsed.type !== \"RUN_ERROR\") return;\n runFinishedDispatched = true;\n dispatch(parsed);\n\n const error = Object.assign(\n new Error(parsed.message ?? \"Run failed\"),\n parsed.code === undefined ? {} : { code: parsed.code },\n );\n onRunFailed?.(error);\n },\n onRunFinalized: () => {\n if (runFinishedDispatched) return;\n dispatch({ type: \"RUN_FINISHED\", runId });\n },\n onRunFailed: ({ error }) => {\n runFinishedDispatched = true;\n onRunFailed?.(error);\n if (isAbortError(error)) {\n dispatch({ type: \"RUN_CANCELLED\" } satisfies AgUiEvent);\n return;\n }\n const message =\n typeof error.message === \"string\" ? error.message : \"Run failed\";\n const code =\n typeof (error as any)?.code === \"string\"\n ? (error as any).code\n : undefined;\n dispatch({\n type: \"RUN_ERROR\" as const,\n ...(message !== undefined ? { message } : {}),\n ...(code !== undefined ? { code } : {}),\n } satisfies AgUiEvent);\n },\n };\n};\n\nexport type AgUiSubscriber = ReturnType<typeof createAgUiSubscriber>;\n"],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { AgUiInterrupt, AgUiResumeEntry } from "../types.js";
2
+ import { RespondToToolApprovalOptions, ThreadAssistantMessagePart, ToolCallMessagePart } from "@assistant-ui/core";
3
+ //#region src/runtime/adapter/tool-approval.d.ts
4
+ type ToolApproval = NonNullable<ToolCallMessagePart["approval"]>;
5
+ /**
6
+ * AG-UI binds a decision to a tool call with `reason: "tool_call"`;
7
+ * `confirmation` is a free-standing yes/no and `input_required` wants structured
8
+ * input, so both stay on the bespoke interrupt hooks, as does a `tool_call`
9
+ * whose `responseSchema` asks for more than this seam can send. A batch mixing a
10
+ * gate with any other interrupt projects nothing: AG-UI resumes with one
11
+ * response per open interrupt, so a half-owned batch cannot be completed from
12
+ * either side alone.
13
+ *
14
+ * The same rule covers binding. A gate naming a tool call the message does not
15
+ * render can never be decided through this seam, so the batch it belongs to
16
+ * could never be completed either: the decisions taken on its visible siblings
17
+ * would sit on the message while the run waited for a response that no click
18
+ * can produce. `boundToolCallIds` leaves such a batch bespoke instead.
19
+ */
20
+ declare const projectAgUiToolApprovals: (interrupts: readonly AgUiInterrupt[] | undefined, boundToolCallIds?: ReadonlySet<string>) => ReadonlyMap<string, ToolApproval>;
21
+ /**
22
+ * Records a decision on the gated tool call. The decision is held on the part
23
+ * until every gate in the batch is answered, because AG-UI resumes a run with
24
+ * one response per open interrupt rather than one response per gate. A chosen
25
+ * `optionId` is not recorded: the projection exposes an approval without
26
+ * options, and core rejects an `optionId` absent from them, so no option can
27
+ * reach this seam.
28
+ */
29
+ declare const withToolApprovalDecision: (content: readonly ThreadAssistantMessagePart[], { approvalId, approved, reason }: RespondToToolApprovalOptions) => readonly ThreadAssistantMessagePart[];
30
+ /**
31
+ * Maps the recorded decisions onto AG-UI resume entries, or `null` while the
32
+ * batch is incomplete. A denial is a resolved response carrying
33
+ * `approved: false`, not a cancellation: `cancelled` means the user abandoned
34
+ * the interrupt without meaningful input and carries no payload, which would
35
+ * drop the denial reason on the wire.
36
+ */
37
+ declare const buildToolApprovalResume: (content: readonly ThreadAssistantMessagePart[], interrupts: readonly AgUiInterrupt[]) => AgUiResumeEntry[] | null;
38
+ /**
39
+ * Settles every gate the submitted resume array names, so the displayed outcome
40
+ * cannot contradict the wire. A decision recorded locally is not exempt: a gate
41
+ * approved while the batch waited on its sibling is still cancelled when
42
+ * `steerAway` submits a cancellation for it. A gate is shown as cancelled only
43
+ * where its own entry is cancelled; a resolved entry supplies the decision it
44
+ * carries, and one that carries none settles the gate with no decision rather
45
+ * than fabricating one or keeping an unsent local decision beside it.
46
+ *
47
+ * A settlement always overrides an earlier one, in either direction. A resolved
48
+ * settlement leaves no state a locally recorded decision does not also leave —
49
+ * core's `resolution` covers only `cancelled` and `expired` — so honoring the
50
+ * first settlement instead would mean treating an unsent local decision as
51
+ * terminal, which is exactly what the cancellation case must override.
52
+ */
53
+ declare const withSettledToolApprovals: (content: readonly ThreadAssistantMessagePart[], resume: readonly AgUiResumeEntry[]) => readonly ThreadAssistantMessagePart[];
54
+ //#endregion
55
+ export { buildToolApprovalResume, projectAgUiToolApprovals, withSettledToolApprovals, withToolApprovalDecision };
56
+ //# sourceMappingURL=tool-approval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-approval.d.ts","names":[],"sources":["../../../src/runtime/adapter/tool-approval.ts"],"mappings":";;;KAYK,eAAe,YAAY;;;;;;;;;;;;;;;;cAyLnB,2BACX,qBAAqB,6BACrB,mBAAmB,wBAClB,oBAAoB;;;;;;;;;cA0BV,2BACX,kBAAkB,gCAClB,YAAA,UAAA,UAAkC,0CACxB;;;;;;;;cA0BC,0BACX,kBAAkB,8BAClB,qBAAqB,oBACpB;;;;;;;;;;;;;;;;cAwFU,2BACX,kBAAkB,8BAClB,iBAAiB,+BACP"}
@@ -0,0 +1,244 @@
1
+ import { readRawResponseSchema, withoutRawResponseSchema } from "../interrupt-internals.js";
2
+ import { buildResumeArray } from "@ag-ui/client";
3
+ //#region src/runtime/adapter/tool-approval.ts
4
+ const EMPTY_APPROVALS = /* @__PURE__ */ new Map();
5
+ const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
6
+ const isString = (value) => typeof value === "string";
7
+ const JSON_SCHEMA_TYPES = /* @__PURE__ */ new Set([
8
+ "null",
9
+ "boolean",
10
+ "object",
11
+ "array",
12
+ "number",
13
+ "integer",
14
+ "string"
15
+ ]);
16
+ const isUniqueStrings = (value) => Array.isArray(value) && value.every(isString) && new Set(value).size === value.length;
17
+ const isSchemaType = (type) => isString(type) && JSON_SCHEMA_TYPES.has(type) || Array.isArray(type) && type.length > 0 && isUniqueStrings(type) && type.every((entry) => JSON_SCHEMA_TYPES.has(entry));
18
+ const acceptsType = (type, primitive) => type === primitive || Array.isArray(type) && isSchemaType(type) && type.includes(primitive);
19
+ const acceptsObject = (type) => acceptsType(type, "object");
20
+ const SUB_SCHEMA_ANNOTATIONS = /* @__PURE__ */ new Set([
21
+ "$schema",
22
+ "$id",
23
+ "title",
24
+ "description"
25
+ ]);
26
+ /**
27
+ * A field schema accepts a value of `primitive` only where nothing in it can
28
+ * narrow past the type: an annotation carries no constraint, and an absent
29
+ * `type` constrains nothing. Any other keyword — `const`, `enum`, `anyOf`,
30
+ * `format` — can reject the value this seam sends, so it is not interpreted.
31
+ */
32
+ const acceptsOnly = (schema, primitive) => isPlainObject(schema) && Object.entries(schema).every(([keyword, value]) => keyword === "type" ? acceptsType(value, primitive) : SUB_SCHEMA_ANNOTATIONS.has(keyword) && isString(value));
33
+ /**
34
+ * Keywords of a sub-schema whose value shape a validator reads, each paired
35
+ * with that shape: the same shapes the top-level schema is held to, plus
36
+ * `properties` and `items`, which carry schemas, so a malformed one nested
37
+ * under them is as uncompilable as a malformed one at the top. A keyword this
38
+ * seam cannot judge is left alone: unlike the top-level schema, a field the
39
+ * seam never sends needs only to be compilable, not permissive.
40
+ */
41
+ const SUB_SCHEMA_SHAPES = new Map([
42
+ ["type", isSchemaType],
43
+ ["required", isUniqueStrings],
44
+ ...[...SUB_SCHEMA_ANNOTATIONS].map((annotation) => [annotation, isString]),
45
+ ["properties", (value) => isPlainObject(value) && Object.values(value).every((schema) => isWellFormedSubSchema(schema))],
46
+ ["items", (value) => isWellFormedSubSchema(value)]
47
+ ]);
48
+ /**
49
+ * A schema the server cannot compile fails the run whatever payload follows, so
50
+ * a field this seam never sends is still checked for the shape a validator
51
+ * reads it as. A boolean is a schema in its own right.
52
+ */
53
+ const isWellFormedSubSchema = (schema) => typeof schema === "boolean" || isPlainObject(schema) && Object.entries(schema).every(([keyword, value]) => {
54
+ const holds = SUB_SCHEMA_SHAPES.get(keyword);
55
+ return holds === void 0 || holds(value);
56
+ });
57
+ /**
58
+ * AG-UI's own tool-approval schema declares the fields it expects under
59
+ * `properties`, so a declaration is read where it cannot reject what the seam
60
+ * sends: `approved` must accept a boolean and `reason` a string. A field the
61
+ * seam never sends is unconstrained by `required` (which admits only
62
+ * `approved`), so only its well-formedness as a sub-schema is checked — the
63
+ * `editedArgs` of the spec's approve-with-edits shape is such a field.
64
+ */
65
+ const acceptsSeamProperties = (properties) => isPlainObject(properties) && Object.entries(properties).every(([field, schema]) => {
66
+ if (field === "approved") return acceptsOnly(schema, "boolean");
67
+ if (field === "reason") return acceptsOnly(schema, "string");
68
+ return isWellFormedSubSchema(schema);
69
+ });
70
+ const acceptsSeamPayload = (required) => Array.isArray(required) && required.every((field) => field === "approved") && new Set(required).size === required.length;
71
+ /**
72
+ * Keywords that cannot reject a payload this seam emits, each paired with the
73
+ * check that its value is the well-formed shape the keyword is read as. A
74
+ * keyword whose value a validator would reject as an invalid schema is not
75
+ * decided here, because a server that compiles the schema fails the run. An
76
+ * unlisted keyword — `additionalProperties`, `allOf`, `$ref` — makes the gate
77
+ * bespoke rather than being interpreted. The map is keyed by
78
+ * `Map` rather than by object property, because a schema key of `constructor`
79
+ * or `toString` would otherwise read an `Object.prototype` member as its
80
+ * validator and `__proto__` would read the prototype itself.
81
+ */
82
+ const SEAM_SAFE_KEYWORDS = /* @__PURE__ */ new Map([
83
+ ["$schema", isString],
84
+ ["$id", isString],
85
+ ["title", isString],
86
+ ["description", isString],
87
+ ["type", acceptsObject],
88
+ ["required", acceptsSeamPayload],
89
+ ["properties", acceptsSeamProperties]
90
+ ]);
91
+ /**
92
+ * The seam sends `{ approved }` and may add `reason`, so it may answer a
93
+ * `responseSchema` only where acceptance of those exact payloads is established
94
+ * by construction. AG-UI answers a resume payload its schema rejects with
95
+ * `RunError`, and the host can always build the exact payload through the
96
+ * bespoke interrupt hooks, so an undecidable schema is left to them. Only an
97
+ * absent schema and a plain object of well-formed allowed keywords establish
98
+ * that: a boolean schema (`false` rejects every payload), a malformed one, and
99
+ * a keyword whose value is not the shape it interprets are all left bespoke.
100
+ */
101
+ const isSeamAnswerable = (schema) => {
102
+ if (schema === void 0) return true;
103
+ if (!isPlainObject(schema)) return false;
104
+ return Object.entries(schema).every(([keyword, value]) => {
105
+ const accepts = SEAM_SAFE_KEYWORDS.get(keyword);
106
+ return accepts !== void 0 && accepts(value);
107
+ });
108
+ };
109
+ const isGate = (interrupt) => interrupt.reason === "tool_call" && !!interrupt.id && !!interrupt.toolCallId && readRawResponseSchema(interrupt) === void 0 && isSeamAnswerable(interrupt.responseSchema);
110
+ const isPending = (approval) => !!approval && approval.approved === void 0 && !approval.resolution;
111
+ /**
112
+ * AG-UI binds a decision to a tool call with `reason: "tool_call"`;
113
+ * `confirmation` is a free-standing yes/no and `input_required` wants structured
114
+ * input, so both stay on the bespoke interrupt hooks, as does a `tool_call`
115
+ * whose `responseSchema` asks for more than this seam can send. A batch mixing a
116
+ * gate with any other interrupt projects nothing: AG-UI resumes with one
117
+ * response per open interrupt, so a half-owned batch cannot be completed from
118
+ * either side alone.
119
+ *
120
+ * The same rule covers binding. A gate naming a tool call the message does not
121
+ * render can never be decided through this seam, so the batch it belongs to
122
+ * could never be completed either: the decisions taken on its visible siblings
123
+ * would sit on the message while the run waited for a response that no click
124
+ * can produce. `boundToolCallIds` leaves such a batch bespoke instead.
125
+ */
126
+ const projectAgUiToolApprovals = (interrupts, boundToolCallIds) => {
127
+ if (!interrupts?.length || !interrupts.every(isGate)) return EMPTY_APPROVALS;
128
+ if (boundToolCallIds && !interrupts.every((interrupt) => boundToolCallIds.has(interrupt.toolCallId))) return EMPTY_APPROVALS;
129
+ const toolCallIds = interrupts.map((interrupt) => interrupt.toolCallId);
130
+ if (new Set(toolCallIds).size !== toolCallIds.length) return EMPTY_APPROVALS;
131
+ return new Map(interrupts.map((interrupt) => [interrupt.toolCallId, { id: interrupt.id }]));
132
+ };
133
+ /**
134
+ * Records a decision on the gated tool call. The decision is held on the part
135
+ * until every gate in the batch is answered, because AG-UI resumes a run with
136
+ * one response per open interrupt rather than one response per gate. A chosen
137
+ * `optionId` is not recorded: the projection exposes an approval without
138
+ * options, and core rejects an `optionId` absent from them, so no option can
139
+ * reach this seam.
140
+ */
141
+ const withToolApprovalDecision = (content, { approvalId, approved, reason }) => {
142
+ let changed = false;
143
+ const next = content.map((part) => {
144
+ if (part.type !== "tool-call") return part;
145
+ const approval = part.approval;
146
+ if (approval?.id !== approvalId || !isPending(approval)) return part;
147
+ changed = true;
148
+ return {
149
+ ...part,
150
+ approval: {
151
+ ...approval,
152
+ approved,
153
+ ...reason !== void 0 && { reason }
154
+ }
155
+ };
156
+ });
157
+ return changed ? next : content;
158
+ };
159
+ /**
160
+ * Maps the recorded decisions onto AG-UI resume entries, or `null` while the
161
+ * batch is incomplete. A denial is a resolved response carrying
162
+ * `approved: false`, not a cancellation: `cancelled` means the user abandoned
163
+ * the interrupt without meaningful input and carries no payload, which would
164
+ * drop the denial reason on the wire.
165
+ */
166
+ const buildToolApprovalResume = (content, interrupts) => {
167
+ const open = new Set(interrupts.map((interrupt) => interrupt.id));
168
+ const responses = /* @__PURE__ */ new Map();
169
+ for (const part of content) {
170
+ if (part.type !== "tool-call") continue;
171
+ const approval = part.approval;
172
+ if (!approval || approval.approved === void 0) continue;
173
+ if (!open.has(approval.id)) continue;
174
+ responses.set(approval.id, {
175
+ status: "resolved",
176
+ payload: {
177
+ approved: approval.approved,
178
+ ...approval.reason !== void 0 && { reason: approval.reason }
179
+ }
180
+ });
181
+ }
182
+ if (interrupts.some((interrupt) => !responses.has(interrupt.id))) return null;
183
+ return buildResumeArray(interrupts.map((interrupt) => {
184
+ const { responseSchema: _schema, ...rest } = withoutRawResponseSchema(interrupt);
185
+ return rest;
186
+ }), Object.fromEntries(responses));
187
+ };
188
+ const settle = (approval, entry) => {
189
+ const { approved: _approved, reason: _reason, resolution: _resolution, ...rest } = approval;
190
+ if (entry.status === "cancelled") return {
191
+ ...rest,
192
+ resolution: "cancelled"
193
+ };
194
+ const payload = entry.payload;
195
+ if (typeof payload !== "object" || payload === null || typeof payload.approved !== "boolean") return {
196
+ ...rest,
197
+ resolution: "cancelled"
198
+ };
199
+ const { approved, reason } = payload;
200
+ return {
201
+ ...rest,
202
+ approved,
203
+ ...typeof reason === "string" && { reason }
204
+ };
205
+ };
206
+ /**
207
+ * Settles every gate the submitted resume array names, so the displayed outcome
208
+ * cannot contradict the wire. A decision recorded locally is not exempt: a gate
209
+ * approved while the batch waited on its sibling is still cancelled when
210
+ * `steerAway` submits a cancellation for it. A gate is shown as cancelled only
211
+ * where its own entry is cancelled; a resolved entry supplies the decision it
212
+ * carries, and one that carries none settles the gate with no decision rather
213
+ * than fabricating one or keeping an unsent local decision beside it.
214
+ *
215
+ * A settlement always overrides an earlier one, in either direction. A resolved
216
+ * settlement leaves no state a locally recorded decision does not also leave —
217
+ * core's `resolution` covers only `cancelled` and `expired` — so honoring the
218
+ * first settlement instead would mean treating an unsent local decision as
219
+ * terminal, which is exactly what the cancellation case must override.
220
+ */
221
+ const withSettledToolApprovals = (content, resume) => {
222
+ if (resume.length === 0) return content;
223
+ const byId = new Map(resume.map((entry) => [entry.interruptId, entry]));
224
+ let changed = false;
225
+ const next = content.map((part) => {
226
+ if (part.type !== "tool-call") return part;
227
+ const approval = part.approval;
228
+ if (!approval) return part;
229
+ const entry = byId.get(approval.id);
230
+ if (!entry) return part;
231
+ const settled = settle(approval, entry);
232
+ if (!settled) return part;
233
+ changed = true;
234
+ return {
235
+ ...part,
236
+ approval: settled
237
+ };
238
+ });
239
+ return changed ? next : content;
240
+ };
241
+ //#endregion
242
+ export { buildToolApprovalResume, projectAgUiToolApprovals, withSettledToolApprovals, withToolApprovalDecision };
243
+
244
+ //# sourceMappingURL=tool-approval.js.map