@assistant-ui/react-ag-ui 0.0.56 → 0.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts +5 -17
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtime/AgUiThreadRuntimeCore.js +133 -234
- package/dist/runtime/AgUiThreadRuntimeCore.js.map +1 -1
- package/dist/runtime/adapter/conversions.d.ts.map +1 -1
- package/dist/runtime/adapter/conversions.js +54 -25
- package/dist/runtime/adapter/conversions.js.map +1 -1
- package/dist/runtime/adapter/run-aggregator.d.ts +31 -4
- package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
- package/dist/runtime/adapter/run-aggregator.js +419 -123
- package/dist/runtime/adapter/run-aggregator.js.map +1 -1
- package/dist/runtime/adapter/subscriber.d.ts +9 -0
- package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
- package/dist/runtime/adapter/subscriber.js +4 -1
- package/dist/runtime/adapter/subscriber.js.map +1 -1
- package/dist/runtime/adapter/tool-approval.d.ts +3 -1
- package/dist/runtime/adapter/tool-approval.d.ts.map +1 -1
- package/dist/runtime/adapter/tool-approval.js +11 -12
- package/dist/runtime/adapter/tool-approval.js.map +1 -1
- package/dist/runtime/event-parser.d.ts.map +1 -1
- package/dist/runtime/event-parser.js +176 -45
- package/dist/runtime/event-parser.js.map +1 -1
- package/dist/runtime/types.d.ts +42 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/useAgUiRuntime.js +12 -9
- package/dist/useAgUiRuntime.js.map +1 -1
- package/package.json +10 -10
- package/src/runtime/AgUiThreadRuntimeCore.test.ts +105 -0
- package/src/runtime/AgUiThreadRuntimeCore.ts +190 -319
- package/src/runtime/adapter/conversions.ts +128 -36
- package/src/runtime/adapter/run-aggregator.approval.test.ts +128 -0
- package/src/runtime/adapter/run-aggregator.ts +670 -148
- package/src/runtime/adapter/subscriber.ts +10 -1
- package/src/runtime/adapter/tool-approval.test.ts +75 -0
- package/src/runtime/adapter/tool-approval.ts +17 -12
- package/src/runtime/event-parser.ts +216 -36
- package/src/runtime/types.ts +70 -12
- package/src/useAgUiRuntime.approval.test.tsx +34 -24
- package/src/useAgUiRuntime.history.test.tsx +76 -0
- package/src/useAgUiRuntime.toolDeferral.test.tsx +186 -0
- package/src/useAgUiRuntime.ts +12 -9
|
@@ -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 { 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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"}
|
|
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 MessageStatus,\n type MessageTiming,\n type ThreadAssistantMessagePart,\n type ThreadMessage,\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\nconst ROOT_SCOPE = \"\";\n\n// Defensive guard against a malformed or cyclic parentToolCallId /\n// parentSubagentRunId chain, not a realistic nesting depth.\nconst MAX_SUBAGENT_DEPTH = 16;\n\ntype SubagentRunState = {\n subagentRunId: string;\n name: string;\n description?: string;\n parentSubagentRunId?: string;\n parentToolCallId?: string;\n parentMessageId?: string;\n createdAt: Date;\n status: MessageStatus;\n result?: unknown;\n interruptIds?: string[];\n errorCode?: string;\n};\n\ntype ToolApproval = NonNullable<ToolCallMessagePart[\"approval\"]>;\n\ntype PartOrderEntry =\n | { kind: \"text\"; key: string; subagentRunId?: string }\n | { kind: \"reasoning\"; key: string; subagentRunId?: string }\n | { kind: \"tool-call\"; toolCallId: string }\n | { kind: \"data\"; name: string; value: unknown };\n\ntype BuildContext = {\n subagentsByParentToolCallId: Map<string, string[]>;\n reachable: Set<string>;\n approvals: ReadonlyMap<string, ToolApproval>;\n // partOrder bucketed by the scope each part renders in, so one emit walks\n // every part once however many subagents the run spawned.\n partsByScope: Map<string, { index: number; part: PartOrderEntry }[]>;\n // Present only for the root walk: the pass that builds the snapshot is also\n // the one that decides where a retracted reasoning signature anchors, so the\n // two can never disagree about what materialized.\n root?: {\n opaqueCandidates: (AgUiOpaqueReasoning & { anchor: number })[];\n lastMaterializedIndex: number;\n };\n};\n\nexport type AgUiOpaqueReasoning = {\n id: string;\n encryptedValue: string;\n after?: boolean;\n};\n\nexport type AgUiCustomMetadata = {\n /** Wire role restored on export for messages the internal model cannot\n * represent (a developer record rides as a system message). */\n role?: \"developer\";\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 subagentRunId?: string;\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 onTextMessageStart?: (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 private readonly onTextMessageStart:\n | ((messageId: string) => void)\n | undefined;\n\n private status: ChatModelRunResult[\"status\"] | undefined;\n private interrupts: AgUiInterrupt[] | undefined;\n private readonly textParts = new Map<string, { buffer: string }>();\n private readonly activeTextMessageIdByScope = new Map<string, string>();\n private readonly reasoningParts = new Map<string, string>(); // key → buffer\n private readonly reasoningSignatures = new Map<string, string>();\n private readonly reasoningSignatureIds = new Map<string, string>();\n // Signatures captured while thinking is hidden have no block to live on;\n // they are transport state that must survive to the next run input. The\n // claim rules mirror the visible path: an id-carrying block, or an open\n // anonymous one.\n private readonly hiddenSignatures = new Map<string, string>();\n private readonly hiddenSignatureAnchors = new Map<string, number>();\n private readonly hiddenBlockAnchors = new Map<string, number>();\n private hiddenAnonymousAnchor: number | undefined;\n private readonly hiddenReasoningIds = new Set<string>();\n private hiddenActiveReasoning: \"none\" | \"anonymous\" | \"identified\" = \"none\";\n private hasEmittedOpaqueReasoning = false;\n private readonly loggedDroppedOpaqueIds = new Set<string>();\n private readonly reasoningMessageIds = new Map<string, string>();\n private readonly anonymousReasoningKeys = new Set<string>();\n private readonly activeReasoningKeyByScope = new Map<string, string>();\n private readonly subagentRuns = new Map<string, SubagentRunState>();\n // The nesting graph only moves when a run is announced or a tool call is\n // created, which is rare next to emit(), so it is derived once per change\n // rather than per streamed delta.\n private nestingMemo:\n | {\n subagentsByParentToolCallId: Map<string, string[]>;\n reachable: Set<string>;\n }\n | 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 readonly lastResolvedToolCallIdByScope = new Map<string, string>();\n private readonly partOrder: PartOrderEntry[] = [];\n private textPartCounter = 0;\n private serverMessageIdReported = false;\n private reportedServerMessageId: string | undefined;\n private lastTextMessageId: string | undefined;\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 this.onTextMessageStart = options.onTextMessageStart;\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.resetMessageParts();\n this.interrupts = undefined;\n this.serverMessageIdReported = false;\n this.lastTextMessageId = undefined;\n this.reportedServerMessageId = undefined;\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 // A call nested inside a subagent message is answerable the same way\n // a root call is: getPendingToolCalls and addToolResult walk\n // ToolCallMessagePart.messages, so any unresolved call keeps the run\n // resumable.\n const hasUnresolvedToolCalls = Array.from(this.toolCalls.values()).some(\n (tc) => tc.result === undefined,\n );\n\n this.status = hasUnresolvedToolCalls\n ? { type: \"requires-action\", reason: \"tool-calls\" }\n : { type: \"complete\", reason: \"unknown\" };\n this.closeOpenSubagentRuns(this.status);\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.closeOpenSubagentRuns(this.status);\n this.emit();\n break;\n }\n case \"RUN_CANCELLED\": {\n this.status = { type: \"incomplete\", reason: \"cancelled\" };\n this.closeOpenSubagentRuns(this.status);\n this.emit();\n break;\n }\n\n case \"TEXT_MESSAGE_START\": {\n const scope = this.scopeOf(event);\n if (scope === ROOT_SCOPE) {\n this.beginDistinctTextMessage(event.messageId);\n this.reportServerMessageId(event.messageId);\n if (event.messageId) this.lastTextMessageId = event.messageId;\n }\n this.startTextMessage(scope, event.messageId);\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 const scope = this.scopeOf(event);\n if (scope === ROOT_SCOPE) {\n this.beginDistinctTextMessage(incomingId);\n this.reportServerMessageId(incomingId);\n if (incomingId) this.lastTextMessageId = incomingId;\n }\n if (!event.delta) break;\n this.recordFirstToken();\n const id = this.resolveTextMessageId(scope, incomingId);\n this.appendText(id, event.delta);\n this.totalChunks++;\n this.emit();\n break;\n }\n case \"TEXT_MESSAGE_END\": {\n const scope = this.scopeOf(event);\n if (scope === ROOT_SCOPE) this.reportServerMessageId(event.messageId);\n if (\n event.messageId &&\n this.activeTextMessageIdByScope.get(scope) ===\n this.partKey(scope, event.messageId)\n ) {\n this.activeTextMessageIdByScope.delete(scope);\n }\n this.emit();\n break;\n }\n\n case \"CUSTOM\": {\n this.activeTextMessageIdByScope.delete(ROOT_SCOPE);\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 this.scopeOf(\"subagentRunId\" in event ? event : {}),\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 const scope = this.scopeOf(event);\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.activeReasoningKeyByScope.get(scope);\n const entityKey = this.partKey(scope, event.entityId);\n const key = this.showThinking\n ? this.reasoningParts.has(entityKey)\n ? entityKey\n : active !== undefined && this.anonymousReasoningKeys.has(active)\n ? active\n : undefined\n : undefined;\n if (key !== undefined) {\n this.reasoningSignatures.set(key, event.encryptedValue);\n this.reasoningSignatureIds.set(key, event.entityId);\n this.emit();\n } else if (\n !this.showThinking &&\n // Hidden-reasoning bookkeeping stays root-only for this task — a\n // subagent's opaque/hidden reasoning signatures are not preserved.\n scope === ROOT_SCOPE &&\n event.entityId.trim().length > 0 &&\n event.encryptedValue.trim().length > 0 &&\n (this.hiddenReasoningIds.has(event.entityId) ||\n this.hiddenActiveReasoning === \"anonymous\")\n ) {\n this.hiddenSignatures.set(event.entityId, event.encryptedValue);\n this.hiddenSignatureAnchors.set(\n event.entityId,\n this.hiddenBlockAnchors.get(event.entityId) ??\n this.hiddenAnonymousAnchor ??\n this.partOrder.length,\n );\n this.emit();\n }\n }\n break;\n case \"THINKING_TEXT_MESSAGE_CONTENT\":\n this.handleReasoningContent(ROOT_SCOPE, event.delta);\n this.totalChunks++;\n this.recordFirstToken();\n break;\n case \"REASONING_MESSAGE_CONTENT\":\n this.handleReasoningContent(\n this.scopeOf(event),\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 this.handleReasoningEnd(ROOT_SCOPE);\n break;\n case \"REASONING_MESSAGE_END\":\n case \"REASONING_END\":\n this.handleReasoningEnd(this.scopeOf(event));\n break;\n\n case \"TOOL_CALL_START\": {\n const scope = this.scopeOf(event);\n if (scope === ROOT_SCOPE) {\n this.reportServerMessageId(event.parentMessageId);\n }\n this.startToolCall(\n scope,\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 (\n event.type === \"TOOL_CALL_CHUNK\" &&\n this.scopeOf(event) === ROOT_SCOPE\n ) {\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 this.scopeOf(event),\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 activityScope = this.scopeOf(event);\n const toolCallId = event.content.toolCallId;\n const fallbackId =\n this.lastResolvedToolCallIdByScope.get(activityScope);\n const entry =\n typeof toolCallId === \"string\"\n ? this.toolCalls.get(toolCallId)\n : fallbackId\n ? this.toolCalls.get(fallbackId)\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 case \"SUBAGENT_STARTED\": {\n // A suspended subagent is re-announced with the same id on resume, so\n // a repeat is a continuation: it may refresh the descriptive fields\n // and reopen the status, but re-parenting an established run would\n // move its already-rendered output to a different tool call.\n const existing = this.subagentRuns.get(event.subagentRunId);\n if (existing) {\n existing.name = event.name;\n if (event.description !== undefined)\n existing.description = event.description;\n existing.status = { type: \"running\" };\n this.emit();\n break;\n }\n this.nestingMemo = undefined;\n this.subagentRuns.set(event.subagentRunId, {\n subagentRunId: event.subagentRunId,\n name: event.name,\n ...(event.description !== undefined\n ? { description: event.description }\n : {}),\n ...(event.parentSubagentRunId !== undefined\n ? { parentSubagentRunId: event.parentSubagentRunId }\n : {}),\n ...(event.parentToolCallId !== undefined\n ? { parentToolCallId: event.parentToolCallId }\n : {}),\n ...(event.parentMessageId !== undefined\n ? { parentMessageId: event.parentMessageId }\n : {}),\n createdAt: new Date(),\n status: { type: \"running\" },\n });\n this.emit();\n break;\n }\n case \"SUBAGENT_FINISHED\": {\n const run = this.subagentRuns.get(event.subagentRunId);\n if (run) {\n run.status =\n event.outcome?.type === \"suspended\"\n ? { type: \"requires-action\", reason: \"interrupt\" }\n : { type: \"complete\", reason: \"unknown\" };\n if (event.result !== undefined) run.result = event.result;\n if (event.outcome?.type === \"suspended\" && event.outcome.interruptIds)\n run.interruptIds = event.outcome.interruptIds;\n }\n this.emit();\n break;\n }\n case \"SUBAGENT_ERROR\": {\n const run = this.subagentRuns.get(event.subagentRunId);\n if (run) {\n run.status = {\n type: \"incomplete\",\n reason: \"error\",\n ...(event.message !== undefined ? { error: event.message } : {}),\n };\n if (event.code !== undefined) run.errorCode = event.code;\n }\n this.emit();\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.nestingMemo = undefined;\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.nestingMemo = undefined;\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 (!messageId) return;\n if (this.lastTextMessageId === undefined) {\n this.lastTextMessageId = messageId;\n }\n if (this.serverMessageIdReported) return;\n this.serverMessageIdReported = true;\n this.reportedServerMessageId = messageId;\n this.onServerMessageId?.(messageId);\n }\n\n private clearTextParts(): void {\n this.textParts.clear();\n }\n\n private resetMessageParts(): void {\n this.clearTextParts();\n this.reasoningParts.clear();\n this.reasoningSignatures.clear();\n this.reasoningSignatureIds.clear();\n this.reasoningMessageIds.clear();\n this.hiddenSignatures.clear();\n this.hiddenSignatureAnchors.clear();\n this.hiddenBlockAnchors.clear();\n this.hiddenAnonymousAnchor = undefined;\n this.hiddenReasoningIds.clear();\n this.hiddenActiveReasoning = \"none\";\n this.loggedDroppedOpaqueIds.clear();\n this.anonymousReasoningKeys.clear();\n this.activeReasoningKeyByScope.clear();\n this.nestingMemo = undefined;\n this.subagentRuns.clear();\n this.reasoningPartCounter = 0;\n this.toolCalls.clear();\n this.a2uiBuckets.clear();\n this.a2uiToolCallIds.clear();\n this.lastResolvedToolCallIdByScope.clear();\n this.partOrder.length = 0;\n this.textPartCounter = 0;\n this.activeTextMessageIdByScope.clear();\n this.reportedServerMessageId = undefined;\n }\n\n // A subagent only ever reports its own terminal, so a run that ends while\n // one is still streaming would leave that nested message spinning forever.\n private closeOpenSubagentRuns(status: MessageStatus): void {\n for (const run of this.subagentRuns.values()) {\n if (run.status.type === \"running\") run.status = status;\n }\n }\n\n private scopeOf(event: { subagentRunId?: string }): string {\n return event.subagentRunId ?? ROOT_SCOPE;\n }\n\n // Nothing in the AG-UI schema makes a messageId unique across subagent runs,\n // so the accumulators key on the scope too. Root keys stay bare, which is\n // what the wire-id collision checks against reported message ids compare on.\n private partKey(scope: string, id: string): string {\n return scope === ROOT_SCOPE ? id : `${scope}\\u0000${id}`;\n }\n\n private beginDistinctTextMessage(messageId: string | undefined): void {\n if (\n !messageId ||\n this.lastTextMessageId === undefined ||\n this.lastTextMessageId === messageId ||\n !this.onTextMessageStart\n ) {\n return;\n }\n this.resetMessageParts();\n this.onTextMessageStart(messageId);\n }\n\n private generateTextKey(): string {\n this.textPartCounter += 1;\n return `text-${this.textPartCounter}`;\n }\n\n private startTextMessage(scope: string, messageId?: string): string {\n const id = messageId ?? this.generateTextKey();\n const key = this.ensureTextPart(scope, id);\n this.activeTextMessageIdByScope.set(scope, key);\n return key;\n }\n\n private resolveTextMessageId(scope: string, messageId?: string): string {\n if (messageId) {\n const key = this.ensureTextPart(scope, messageId);\n this.activeTextMessageIdByScope.set(scope, key);\n return key;\n }\n\n const active = this.activeTextMessageIdByScope.get(scope);\n if (active) {\n return active;\n }\n\n const key = this.ensureTextPart(scope, this.generateTextKey());\n this.activeTextMessageIdByScope.set(scope, key);\n return key;\n }\n\n private ensureTextPart(scope: string, id: string): string {\n const key = this.partKey(scope, id);\n if (!this.textParts.has(key)) {\n this.textParts.set(key, { buffer: \"\" });\n if (\n !this.partOrder.some((part) => part.kind === \"text\" && part.key === key)\n ) {\n this.partOrder.push(\n scope === ROOT_SCOPE\n ? { kind: \"text\", key }\n : { kind: \"text\", key, subagentRunId: scope },\n );\n }\n }\n return key;\n }\n\n private appendText(id: string, delta: string): void {\n const entry = this.textParts.get(id);\n if (!entry) return;\n entry.buffer += delta;\n }\n\n private startToolCall(\n scope: string,\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 // scoped, so a subagent's tool call doesn't break the root run's text\n // and vice versa.\n this.activeTextMessageIdByScope.delete(scope);\n if (\n !this.partOrder.some(\n (part) => part.kind === \"tool-call\" && part.toolCallId === id,\n )\n ) {\n this.insertToolPart(scope, 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 if (scope !== ROOT_SCOPE) {\n state.subagentRunId = scope;\n }\n this.nestingMemo = undefined;\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(scope: string, id: string, parentMessageId?: string) {\n const entry = { kind: \"tool-call\", toolCallId: id } as const;\n if (parentMessageId) {\n const parentKey = this.partKey(scope, parentMessageId);\n const parentIndex = this.partOrder.findIndex(\n (part) => part.kind === \"text\" && part.key === parentKey,\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 scope: string,\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 ...(scope !== ROOT_SCOPE ? { subagentRunId: scope } : {}),\n };\n this.nestingMemo = undefined;\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.lastResolvedToolCallIdByScope.set(scope, id);\n }\n\n private nesting(): {\n subagentsByParentToolCallId: Map<string, string[]>;\n reachable: Set<string>;\n } {\n if (!this.nestingMemo) {\n const subagentsByParentToolCallId = this.subagentsByParentToolCallId();\n this.nestingMemo = {\n subagentsByParentToolCallId,\n reachable: this.reachableSubagentRunIds(subagentsByParentToolCallId),\n };\n }\n return this.nestingMemo;\n }\n\n private subagentsByParentToolCallId(): Map<string, string[]> {\n const map = new Map<string, string[]>();\n for (const run of this.subagentRuns.values()) {\n if (!run.parentToolCallId) continue;\n const list = map.get(run.parentToolCallId) ?? [];\n list.push(run.subagentRunId);\n map.set(run.parentToolCallId, list);\n }\n return map;\n }\n\n // A subagent nests under its spawning tool call only when that call is itself\n // reachable from the root scope. A run with no parentToolCallId, one naming a\n // call this run never saw, or one in a cycle has nowhere to hang; the visited\n // set also keeps a malformed chain to one visit per run instead of branching\n // at every level.\n private reachableSubagentRunIds(\n subagentsByParentToolCallId: Map<string, string[]>,\n ): Set<string> {\n const reachable = new Set<string>();\n const walk = (scope: string, depth: number): void => {\n if (depth > MAX_SUBAGENT_DEPTH) return;\n for (const entry of this.toolCalls.values()) {\n if ((entry.subagentRunId ?? ROOT_SCOPE) !== scope) continue;\n for (const id of subagentsByParentToolCallId.get(entry.toolCallId) ??\n []) {\n if (reachable.has(id)) continue;\n reachable.add(id);\n walk(id, depth + 1);\n }\n }\n };\n walk(ROOT_SCOPE, 0);\n return reachable;\n }\n\n // Nested messages rather than a discovery hook (the shape react-langchain\n // uses): ToolCallMessagePart.messages is the contract core consumers already\n // walk, and SUBAGENT_STARTED.parentToolCallId names the spawning call\n // outright, so the join needs no namespace scheme of its own.\n private materializeSubagentMessage(\n subagentRunId: string,\n depth: number,\n ctx: BuildContext,\n ): ThreadMessage | undefined {\n const run = this.subagentRuns.get(subagentRunId);\n if (!run || depth > MAX_SUBAGENT_DEPTH) return undefined;\n const content = this.buildParts(subagentRunId, depth + 1, ctx);\n return {\n id: run.subagentRunId,\n role: \"assistant\",\n createdAt: run.createdAt,\n status: run.status,\n content,\n metadata: {\n unstable_state: null,\n unstable_annotations: [],\n unstable_data: [],\n steps: [],\n custom: {\n [AG_UI_METADATA_NAMESPACE]: {\n name: run.name,\n ...(run.description !== undefined\n ? { description: run.description }\n : {}),\n ...(run.parentSubagentRunId !== undefined\n ? { parentSubagentRunId: run.parentSubagentRunId }\n : {}),\n ...(run.result !== undefined ? { result: run.result } : {}),\n ...(run.interruptIds !== undefined\n ? { interruptIds: run.interruptIds }\n : {}),\n ...(run.errorCode !== undefined\n ? { errorCode: run.errorCode }\n : {}),\n },\n },\n },\n } as ThreadMessage;\n }\n\n private buildParts(\n scope: string,\n depth: number,\n ctx: BuildContext,\n ): ThreadAssistantMessagePart[] {\n const snapshot: ThreadAssistantMessagePart[] = [];\n const isRoot = scope === ROOT_SCOPE;\n\n for (const { index, part } of ctx.partsByScope.get(scope) ?? []) {\n const materialized = () => {\n if (isRoot && ctx.root) ctx.root.lastMaterializedIndex = index;\n };\n\n if (part.kind === \"tool-call\") {\n const entry = this.toolCalls.get(part.toolCallId);\n if (!entry) continue;\n const approval = ctx.approvals.get(entry.toolCallId);\n const nestedMessages = (\n ctx.subagentsByParentToolCallId.get(entry.toolCallId) ?? []\n )\n .filter((id) => ctx.reachable.has(id))\n .map((id) => this.materializeSubagentMessage(id, depth, ctx))\n .filter((m): m is ThreadMessage => m !== undefined);\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 ...(nestedMessages.length > 0 ? { messages: nestedMessages } : {}),\n } as ToolCallMessagePart & { unstable_toolMessageId?: string };\n snapshot.push(toolPart);\n materialized();\n continue;\n }\n\n const rawScope =\n \"subagentRunId\" in part\n ? (part.subagentRunId ?? ROOT_SCOPE)\n : ROOT_SCOPE;\n\n if (part.kind === \"reasoning\") {\n if (this.showThinking) {\n const buffer = this.reasoningParts.get(part.key) ?? \"\";\n const isActive =\n this.activeReasoningKeyByScope.get(rawScope) === part.key;\n if (buffer.length > 0 || isActive) {\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 materialized();\n } else if (isRoot && ctx.root) {\n // A retracted empty block still carries transport state: without\n // a part to live on, its signature rides the message metadata,\n // matching the shape a snapshot reload produces.\n const encryptedValue = this.reasoningSignatures.get(part.key);\n const id = this.reasoningSignatureIds.get(part.key);\n if (id?.trim() && encryptedValue?.trim()) {\n ctx.root.opaqueCandidates.push({\n id,\n encryptedValue,\n anchor: index + 1,\n });\n }\n }\n }\n continue;\n }\n\n if (part.kind === \"text\") {\n const entry = this.textParts.get(part.key);\n if (entry && entry.buffer.trim().length > 0) {\n snapshot.push({ type: \"text\", text: entry.buffer } as const);\n materialized();\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 materialized();\n continue;\n }\n }\n\n return snapshot;\n }\n\n private emit(): void {\n const { subagentsByParentToolCallId, reachable } = this.nesting();\n const root = {\n opaqueCandidates: Array.from(\n this.hiddenSignatures,\n ([id, encryptedValue]) => ({\n id,\n encryptedValue,\n anchor: this.hiddenSignatureAnchors.get(id)!,\n }),\n ) as (AgUiOpaqueReasoning & { anchor: number })[],\n lastMaterializedIndex: -1,\n };\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. Bound ids cover every call the run\n // produced, root or nested: the approval seams walk\n // ToolCallMessagePart.messages, so a gate naming a subagent-scoped call is\n // answerable and projects onto the nested part.\n const partsByScope = new Map<\n string,\n { index: number; part: PartOrderEntry }[]\n >();\n for (const [index, part] of this.partOrder.entries()) {\n const rawScope =\n part.kind === \"tool-call\"\n ? (this.toolCalls.get(part.toolCallId)?.subagentRunId ?? ROOT_SCOPE)\n : \"subagentRunId\" in part\n ? (part.subagentRunId ?? ROOT_SCOPE)\n : ROOT_SCOPE;\n const bucketScope =\n rawScope === ROOT_SCOPE || reachable.has(rawScope)\n ? rawScope\n : ROOT_SCOPE;\n const bucket = partsByScope.get(bucketScope);\n if (bucket) bucket.push({ index, part });\n else partsByScope.set(bucketScope, [{ index, part }]);\n }\n const ctx: BuildContext = {\n subagentsByParentToolCallId,\n reachable,\n partsByScope,\n approvals: projectAgUiToolApprovals(\n this.status?.type === \"requires-action\" ? this.interrupts : undefined,\n new Set(\n Array.from(this.toolCalls.values()).map((entry) => entry.toolCallId),\n ),\n ),\n root,\n };\n const snapshot = this.buildParts(ROOT_SCOPE, 0, ctx);\n const { opaqueCandidates, lastMaterializedIndex } = root;\n\n // An anonymous claim promotes the signature's entityId to a wire message\n // id; when that id actually names a text message or the adopted assistant\n // message id (which can also arrive via TOOL_CALL_START.parentMessageId),\n // replaying it as its own record would put two wire records under one id,\n // so such entries are dropped instead — the same outcome the visible\n // path's claim guard produces for a signature that is \"not for this\n // block\". Entries with no materialized part after their anchor trail the\n // assistant record, matching the snapshot path's anchor/after\n // bookkeeping.\n const toolMessageIds = new Set<string>();\n for (const call of this.toolCalls.values()) {\n if (call.toolMessageId) toolMessageIds.add(call.toolMessageId);\n }\n opaqueCandidates.sort((a, b) => a.anchor - b.anchor);\n const publishableOpaqueReasoning = opaqueCandidates\n .filter((entry) => {\n const collides =\n this.textParts.has(entry.id) ||\n entry.id === this.reportedServerMessageId ||\n toolMessageIds.has(entry.id);\n if (collides && !this.loggedDroppedOpaqueIds.has(entry.id)) {\n this.loggedDroppedOpaqueIds.add(entry.id);\n this.logger.debug?.(\n \"[agui] aggregator dropped opaque reasoning signature: id collides with a message id\",\n entry.id,\n );\n }\n return !collides;\n })\n .map(({ anchor, ...entry }) =>\n lastMaterializedIndex < anchor ? { ...entry, after: true } : entry,\n );\n if (publishableOpaqueReasoning.length > 0)\n this.hasEmittedOpaqueReasoning = true;\n // Once an opaque entry has been published this run, the key keeps being\n // emitted (empty when withdrawn) so the namespace merge can retract it.\n const includeOpaque = this.hasEmittedOpaqueReasoning;\n const timing = this.getTiming();\n const metadata = {\n ...(timing ? { timing } : {}),\n ...(this.interrupts || includeOpaque\n ? {\n custom: {\n [AG_UI_METADATA_NAMESPACE]: {\n ...(this.interrupts ? { interrupts: this.interrupts } : {}),\n ...(includeOpaque\n ? { opaqueReasoning: publishableOpaqueReasoning }\n : {}),\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(\n scope: string,\n messageId?: string,\n isMessageId = false,\n ): void {\n if (!this.showThinking) {\n // Hidden-reasoning bookkeeping stays root-only for this task — a\n // subagent's opaque/hidden reasoning signatures are not preserved.\n if (scope !== ROOT_SCOPE) return;\n const anchor = this.partOrder.length;\n if (messageId === undefined) {\n this.hiddenActiveReasoning = \"anonymous\";\n this.hiddenAnonymousAnchor = anchor;\n } else {\n this.hiddenReasoningIds.add(messageId);\n this.hiddenBlockAnchors.set(messageId, anchor);\n this.hiddenActiveReasoning = \"identified\";\n }\n return;\n }\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 — scoped.\n this.activeTextMessageIdByScope.delete(scope);\n const key = this.partKey(\n scope,\n messageId ?? `__auto-reasoning-${++this.reasoningPartCounter}`,\n );\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(\n scope === ROOT_SCOPE\n ? { kind: \"reasoning\", key }\n : { kind: \"reasoning\", key, subagentRunId: scope },\n );\n }\n this.activeReasoningKeyByScope.set(scope, key);\n this.emit();\n }\n\n private handleReasoningContent(\n scope: string,\n delta: string,\n messageId?: string,\n isMessageId = false,\n ): void {\n if (!delta) return;\n if (!this.showThinking) {\n // Content without a preceding START still names the block (the visible\n // path opens it lazily); register it so a signature can claim it.\n if (scope !== ROOT_SCOPE) return;\n if (this.hiddenActiveReasoning === \"none\") {\n this.handleReasoningStart(scope, messageId, isMessageId);\n }\n return;\n }\n if (!this.activeReasoningKeyByScope.has(scope)) {\n // Content arrived without a preceding START — create the slot lazily.\n this.handleReasoningStart(scope, messageId, isMessageId);\n }\n const key = this.activeReasoningKeyByScope.get(scope);\n if (!key) return;\n this.reasoningParts.set(key, (this.reasoningParts.get(key) ?? \"\") + delta);\n this.emit();\n }\n\n private handleReasoningEnd(scope: string): void {\n if (!this.showThinking) {\n if (scope === ROOT_SCOPE) this.hiddenActiveReasoning = \"none\";\n return;\n }\n this.activeReasoningKeyByScope.delete(scope);\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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"}
|
|
@@ -84,6 +84,15 @@ type Subscriber = {
|
|
|
84
84
|
onRawEvent?: (payload: {
|
|
85
85
|
event: unknown;
|
|
86
86
|
}) => void;
|
|
87
|
+
onSubagentStartedEvent?: (payload: {
|
|
88
|
+
event: unknown;
|
|
89
|
+
}) => void;
|
|
90
|
+
onSubagentFinishedEvent?: (payload: {
|
|
91
|
+
event: unknown;
|
|
92
|
+
}) => void;
|
|
93
|
+
onSubagentErrorEvent?: (payload: {
|
|
94
|
+
event: unknown;
|
|
95
|
+
}) => void;
|
|
87
96
|
onRunFinishedEvent?: (payload: {
|
|
88
97
|
event: unknown;
|
|
89
98
|
}) => void;
|
|
@@ -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,mBAAmB;IAAW;;EAC9B;EACA,eAAe;IAAW,OAAO;;;KAiC9B;EACH,UAAU;EACV;EACA,SAAS;EACT,eAAe,OAAO;;cAGX,uBACX,SAAS,sBACR;
|
|
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,0BAA0B;IAAW;;EACrC,2BAA2B;IAAW;;EACtC,wBAAwB;IAAW;;EACnC,sBAAsB;IAAW;;EACjC,mBAAmB;IAAW;;EAC9B;EACA,eAAe;IAAW,OAAO;;;KAiC9B;EACH,UAAU;EACV;EACA,SAAS;EACT,eAAe,OAAO;;cAGX,uBACX,SAAS,sBACR;KAsHS,iBAAiB,kBAAkB"}
|
|
@@ -30,7 +30,7 @@ const createAgUiSubscriber = (options) => {
|
|
|
30
30
|
return {
|
|
31
31
|
onEvent: ({ event }) => {
|
|
32
32
|
if (typeof (event && typeof event === "object" ? event.type : void 0) === "string") return;
|
|
33
|
-
const parsed = parseAgUiEvent(event);
|
|
33
|
+
const parsed = parseAgUiEvent(event, logger ? { logger } : void 0);
|
|
34
34
|
if (parsed) dispatch(parsed);
|
|
35
35
|
},
|
|
36
36
|
onTextMessageStartEvent: ({ event }) => dispatchIfValid(event, "TEXT_MESSAGE_START"),
|
|
@@ -59,6 +59,9 @@ const createAgUiSubscriber = (options) => {
|
|
|
59
59
|
onMessagesSnapshotEvent: ({ event }) => dispatchIfValid(event, "MESSAGES_SNAPSHOT"),
|
|
60
60
|
onCustomEvent: ({ event }) => dispatchIfValid(event, "CUSTOM"),
|
|
61
61
|
onRawEvent: ({ event }) => dispatchIfValid(event, "RAW"),
|
|
62
|
+
onSubagentStartedEvent: ({ event }) => dispatchIfValid(event, "SUBAGENT_STARTED"),
|
|
63
|
+
onSubagentFinishedEvent: ({ event }) => dispatchIfValid(event, "SUBAGENT_FINISHED"),
|
|
64
|
+
onSubagentErrorEvent: ({ event }) => dispatchIfValid(event, "SUBAGENT_ERROR"),
|
|
62
65
|
onRunFinishedEvent: ({ event }) => {
|
|
63
66
|
if (runFinishedDispatched) return;
|
|
64
67
|
const parsed = ensureEvent(event, "RUN_FINISHED", logger);
|
|
@@ -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 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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"}
|
|
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 onSubagentStartedEvent?: (payload: { event: unknown }) => void;\n onSubagentFinishedEvent?: (payload: { event: unknown }) => void;\n onSubagentErrorEvent?: (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, logger ? { logger } : undefined);\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 onSubagentStartedEvent: ({ event }) =>\n dispatchIfValid(event, \"SUBAGENT_STARTED\"),\n onSubagentFinishedEvent: ({ event }) =>\n dispatchIfValid(event, \"SUBAGENT_FINISHED\"),\n onSubagentErrorEvent: ({ event }) =>\n dispatchIfValid(event, \"SUBAGENT_ERROR\"),\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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"}
|
|
@@ -15,7 +15,9 @@ type ToolApproval = NonNullable<ToolCallMessagePart["approval"]>;
|
|
|
15
15
|
* render can never be decided through this seam, so the batch it belongs to
|
|
16
16
|
* could never be completed either: the decisions taken on its visible siblings
|
|
17
17
|
* would sit on the message while the run waited for a response that no click
|
|
18
|
-
* can produce. `boundToolCallIds`
|
|
18
|
+
* can produce. `boundToolCallIds` spans every call the run produced — root or
|
|
19
|
+
* nested on ToolCallMessagePart.messages — so only a gate naming a call the
|
|
20
|
+
* message renders nowhere leaves the batch bespoke.
|
|
19
21
|
*/
|
|
20
22
|
declare const projectAgUiToolApprovals: (interrupts: readonly AgUiInterrupt[] | undefined, boundToolCallIds?: ReadonlySet<string>) => ReadonlyMap<string, ToolApproval>;
|
|
21
23
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-approval.d.ts","names":[],"sources":["../../../src/runtime/adapter/tool-approval.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"tool-approval.d.ts","names":[],"sources":["../../../src/runtime/adapter/tool-approval.ts"],"mappings":";;;KAgBK,eAAe,YAAY;;;;;;;;;;;;;;;;;;cA2LnB,2BACX,qBAAqB,6BACrB,mBAAmB,wBAClB,oBAAoB;;;;;;;;;cAgCV,2BACX,kBAAkB,gCAClB,YAAA,UAAA,UAAkC,0CACxB;;;;;;;;cAuBC,0BACX,kBAAkB,8BAClB,qBAAqB,oBACpB;;;;;;;;;;;;;;;;cAuFU,2BACX,kBAAkB,8BAClB,iBAAiB,+BACP"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readRawResponseSchema, withoutRawResponseSchema } from "../interrupt-internals.js";
|
|
2
|
+
import { iterateToolCallParts, mapToolCallPartsDeep } from "@assistant-ui/core/internal";
|
|
2
3
|
import { buildResumeArray } from "@ag-ui/client";
|
|
3
4
|
//#region src/runtime/adapter/tool-approval.ts
|
|
4
5
|
const EMPTY_APPROVALS = /* @__PURE__ */ new Map();
|
|
@@ -121,14 +122,19 @@ const isPending = (approval) => !!approval && approval.approved === void 0 && !a
|
|
|
121
122
|
* render can never be decided through this seam, so the batch it belongs to
|
|
122
123
|
* could never be completed either: the decisions taken on its visible siblings
|
|
123
124
|
* would sit on the message while the run waited for a response that no click
|
|
124
|
-
* can produce. `boundToolCallIds`
|
|
125
|
+
* can produce. `boundToolCallIds` spans every call the run produced — root or
|
|
126
|
+
* nested on ToolCallMessagePart.messages — so only a gate naming a call the
|
|
127
|
+
* message renders nowhere leaves the batch bespoke.
|
|
125
128
|
*/
|
|
126
129
|
const projectAgUiToolApprovals = (interrupts, boundToolCallIds) => {
|
|
127
130
|
if (!interrupts?.length || !interrupts.every(isGate)) return EMPTY_APPROVALS;
|
|
128
131
|
if (boundToolCallIds && !interrupts.every((interrupt) => boundToolCallIds.has(interrupt.toolCallId))) return EMPTY_APPROVALS;
|
|
129
132
|
const toolCallIds = interrupts.map((interrupt) => interrupt.toolCallId);
|
|
130
133
|
if (new Set(toolCallIds).size !== toolCallIds.length) return EMPTY_APPROVALS;
|
|
131
|
-
return new Map(interrupts.map((interrupt) => [interrupt.toolCallId, {
|
|
134
|
+
return new Map(interrupts.map((interrupt) => [interrupt.toolCallId, {
|
|
135
|
+
id: interrupt.id,
|
|
136
|
+
...interrupt.message !== void 0 && { prompt: interrupt.message }
|
|
137
|
+
}]));
|
|
132
138
|
};
|
|
133
139
|
/**
|
|
134
140
|
* Records a decision on the gated tool call. The decision is held on the part
|
|
@@ -139,12 +145,9 @@ const projectAgUiToolApprovals = (interrupts, boundToolCallIds) => {
|
|
|
139
145
|
* reach this seam.
|
|
140
146
|
*/
|
|
141
147
|
const withToolApprovalDecision = (content, { approvalId, approved, reason }) => {
|
|
142
|
-
|
|
143
|
-
const next = content.map((part) => {
|
|
144
|
-
if (part.type !== "tool-call") return part;
|
|
148
|
+
const { content: next, changed } = mapToolCallPartsDeep(content, (part) => {
|
|
145
149
|
const approval = part.approval;
|
|
146
150
|
if (approval?.id !== approvalId || !isPending(approval)) return part;
|
|
147
|
-
changed = true;
|
|
148
151
|
return {
|
|
149
152
|
...part,
|
|
150
153
|
approval: {
|
|
@@ -166,8 +169,7 @@ const withToolApprovalDecision = (content, { approvalId, approved, reason }) =>
|
|
|
166
169
|
const buildToolApprovalResume = (content, interrupts) => {
|
|
167
170
|
const open = new Set(interrupts.map((interrupt) => interrupt.id));
|
|
168
171
|
const responses = /* @__PURE__ */ new Map();
|
|
169
|
-
for (const part of content) {
|
|
170
|
-
if (part.type !== "tool-call") continue;
|
|
172
|
+
for (const part of iterateToolCallParts(content)) {
|
|
171
173
|
const approval = part.approval;
|
|
172
174
|
if (!approval || approval.approved === void 0) continue;
|
|
173
175
|
if (!open.has(approval.id)) continue;
|
|
@@ -221,16 +223,13 @@ const settle = (approval, entry) => {
|
|
|
221
223
|
const withSettledToolApprovals = (content, resume) => {
|
|
222
224
|
if (resume.length === 0) return content;
|
|
223
225
|
const byId = new Map(resume.map((entry) => [entry.interruptId, entry]));
|
|
224
|
-
|
|
225
|
-
const next = content.map((part) => {
|
|
226
|
-
if (part.type !== "tool-call") return part;
|
|
226
|
+
const { content: next, changed } = mapToolCallPartsDeep(content, (part) => {
|
|
227
227
|
const approval = part.approval;
|
|
228
228
|
if (!approval) return part;
|
|
229
229
|
const entry = byId.get(approval.id);
|
|
230
230
|
if (!entry) return part;
|
|
231
231
|
const settled = settle(approval, entry);
|
|
232
232
|
if (!settled) return part;
|
|
233
|
-
changed = true;
|
|
234
233
|
return {
|
|
235
234
|
...part,
|
|
236
235
|
approval: settled
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-approval.js","names":[],"sources":["../../../src/runtime/adapter/tool-approval.ts"],"sourcesContent":["import { buildResumeArray } from \"@ag-ui/client\";\nimport type {\n RespondToToolApprovalOptions,\n ThreadAssistantMessagePart,\n ToolCallMessagePart,\n} from \"@assistant-ui/core\";\nimport type { AgUiInterrupt, AgUiResumeEntry } from \"../types\";\nimport {\n readRawResponseSchema,\n withoutRawResponseSchema,\n} from \"../interrupt-internals\";\n\ntype ToolApproval = NonNullable<ToolCallMessagePart[\"approval\"]>;\n\nconst EMPTY_APPROVALS: ReadonlyMap<string, ToolApproval> = new Map();\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isString = (value: unknown) => typeof value === \"string\";\n\nconst JSON_SCHEMA_TYPES = new Set([\n \"null\",\n \"boolean\",\n \"object\",\n \"array\",\n \"number\",\n \"integer\",\n \"string\",\n]);\n\nconst isUniqueStrings = (value: unknown) =>\n Array.isArray(value) &&\n value.every(isString) &&\n new Set(value).size === value.length;\n\nconst isSchemaType = (type: unknown) =>\n (isString(type) && JSON_SCHEMA_TYPES.has(type as string)) ||\n (Array.isArray(type) &&\n type.length > 0 &&\n isUniqueStrings(type) &&\n type.every((entry) => JSON_SCHEMA_TYPES.has(entry as string)));\n\nconst acceptsType = (type: unknown, primitive: string) =>\n type === primitive ||\n (Array.isArray(type) && isSchemaType(type) && type.includes(primitive));\n\nconst acceptsObject = (type: unknown) => acceptsType(type, \"object\");\n\nconst SUB_SCHEMA_ANNOTATIONS = new Set([\n \"$schema\",\n \"$id\",\n \"title\",\n \"description\",\n]);\n\n/**\n * A field schema accepts a value of `primitive` only where nothing in it can\n * narrow past the type: an annotation carries no constraint, and an absent\n * `type` constrains nothing. Any other keyword — `const`, `enum`, `anyOf`,\n * `format` — can reject the value this seam sends, so it is not interpreted.\n */\nconst acceptsOnly = (schema: unknown, primitive: string) =>\n isPlainObject(schema) &&\n Object.entries(schema).every(([keyword, value]) =>\n keyword === \"type\"\n ? acceptsType(value, primitive)\n : SUB_SCHEMA_ANNOTATIONS.has(keyword) && isString(value),\n );\n\n/**\n * Keywords of a sub-schema whose value shape a validator reads, each paired\n * with that shape: the same shapes the top-level schema is held to, plus\n * `properties` and `items`, which carry schemas, so a malformed one nested\n * under them is as uncompilable as a malformed one at the top. A keyword this\n * seam cannot judge is left alone: unlike the top-level schema, a field the\n * seam never sends needs only to be compilable, not permissive.\n */\nconst SUB_SCHEMA_SHAPES = new Map<string, (value: unknown) => boolean>([\n [\"type\", isSchemaType],\n [\"required\", isUniqueStrings],\n ...[...SUB_SCHEMA_ANNOTATIONS].map(\n (annotation) =>\n [annotation, isString] as [string, (value: unknown) => boolean],\n ),\n [\n \"properties\",\n (value) =>\n isPlainObject(value) &&\n Object.values(value).every((schema) => isWellFormedSubSchema(schema)),\n ],\n [\"items\", (value) => isWellFormedSubSchema(value)],\n]);\n\n/**\n * A schema the server cannot compile fails the run whatever payload follows, so\n * a field this seam never sends is still checked for the shape a validator\n * reads it as. A boolean is a schema in its own right.\n */\nconst isWellFormedSubSchema = (schema: unknown): boolean =>\n typeof schema === \"boolean\" ||\n (isPlainObject(schema) &&\n Object.entries(schema).every(([keyword, value]) => {\n const holds = SUB_SCHEMA_SHAPES.get(keyword);\n return holds === undefined || holds(value);\n }));\n\n/**\n * AG-UI's own tool-approval schema declares the fields it expects under\n * `properties`, so a declaration is read where it cannot reject what the seam\n * sends: `approved` must accept a boolean and `reason` a string. A field the\n * seam never sends is unconstrained by `required` (which admits only\n * `approved`), so only its well-formedness as a sub-schema is checked — the\n * `editedArgs` of the spec's approve-with-edits shape is such a field.\n */\nconst acceptsSeamProperties = (properties: unknown) =>\n isPlainObject(properties) &&\n Object.entries(properties).every(([field, schema]) => {\n if (field === \"approved\") return acceptsOnly(schema, \"boolean\");\n if (field === \"reason\") return acceptsOnly(schema, \"string\");\n return isWellFormedSubSchema(schema);\n });\n\nconst acceptsSeamPayload = (required: unknown) =>\n Array.isArray(required) &&\n required.every((field) => field === \"approved\") &&\n new Set(required).size === required.length;\n\n/**\n * Keywords that cannot reject a payload this seam emits, each paired with the\n * check that its value is the well-formed shape the keyword is read as. A\n * keyword whose value a validator would reject as an invalid schema is not\n * decided here, because a server that compiles the schema fails the run. An\n * unlisted keyword — `additionalProperties`, `allOf`, `$ref` — makes the gate\n * bespoke rather than being interpreted. The map is keyed by\n * `Map` rather than by object property, because a schema key of `constructor`\n * or `toString` would otherwise read an `Object.prototype` member as its\n * validator and `__proto__` would read the prototype itself.\n */\nconst SEAM_SAFE_KEYWORDS = new Map<string, (value: unknown) => boolean>([\n [\"$schema\", isString],\n [\"$id\", isString],\n [\"title\", isString],\n [\"description\", isString],\n [\"type\", acceptsObject],\n [\"required\", acceptsSeamPayload],\n [\"properties\", acceptsSeamProperties],\n]);\n\n/**\n * The seam sends `{ approved }` and may add `reason`, so it may answer a\n * `responseSchema` only where acceptance of those exact payloads is established\n * by construction. AG-UI answers a resume payload its schema rejects with\n * `RunError`, and the host can always build the exact payload through the\n * bespoke interrupt hooks, so an undecidable schema is left to them. Only an\n * absent schema and a plain object of well-formed allowed keywords establish\n * that: a boolean schema (`false` rejects every payload), a malformed one, and\n * a keyword whose value is not the shape it interprets are all left bespoke.\n */\nconst isSeamAnswerable = (schema: unknown) => {\n if (schema === undefined) return true;\n if (!isPlainObject(schema)) return false;\n return Object.entries(schema).every(([keyword, value]) => {\n const accepts = SEAM_SAFE_KEYWORDS.get(keyword);\n return accepts !== undefined && accepts(value);\n });\n};\n\nconst isGate = (\n interrupt: AgUiInterrupt,\n): interrupt is AgUiInterrupt & { toolCallId: string } =>\n interrupt.reason === \"tool_call\" &&\n !!interrupt.id &&\n !!interrupt.toolCallId &&\n // A restored interrupt is untyped, so `responseSchema` may hold any persisted\n // shape; a non-object schema received live arrives on the internal carrier.\n readRawResponseSchema(interrupt) === undefined &&\n isSeamAnswerable(interrupt.responseSchema);\n\nconst isPending = (approval: ToolCallMessagePart[\"approval\"]) =>\n !!approval && approval.approved === undefined && !approval.resolution;\n\n/**\n * AG-UI binds a decision to a tool call with `reason: \"tool_call\"`;\n * `confirmation` is a free-standing yes/no and `input_required` wants structured\n * input, so both stay on the bespoke interrupt hooks, as does a `tool_call`\n * whose `responseSchema` asks for more than this seam can send. A batch mixing a\n * gate with any other interrupt projects nothing: AG-UI resumes with one\n * response per open interrupt, so a half-owned batch cannot be completed from\n * either side alone.\n *\n * The same rule covers binding. A gate naming a tool call the message does not\n * render can never be decided through this seam, so the batch it belongs to\n * could never be completed either: the decisions taken on its visible siblings\n * would sit on the message while the run waited for a response that no click\n * can produce. `boundToolCallIds` leaves such a batch bespoke instead.\n */\nexport const projectAgUiToolApprovals = (\n interrupts: readonly AgUiInterrupt[] | undefined,\n boundToolCallIds?: ReadonlySet<string>,\n): ReadonlyMap<string, ToolApproval> => {\n if (!interrupts?.length || !interrupts.every(isGate)) return EMPTY_APPROVALS;\n if (\n boundToolCallIds &&\n !interrupts.every((interrupt) => boundToolCallIds.has(interrupt.toolCallId))\n ) {\n return EMPTY_APPROVALS;\n }\n // Two gates naming one tool call would collapse into a single approval, so\n // the batch could never be completed: the surviving gate takes one decision\n // while the resume array still waits on the id that no part carries.\n const toolCallIds = interrupts.map((interrupt) => interrupt.toolCallId);\n if (new Set(toolCallIds).size !== toolCallIds.length) return EMPTY_APPROVALS;\n return new Map(\n interrupts.map((interrupt) => [interrupt.toolCallId, { id: interrupt.id }]),\n );\n};\n\n/**\n * Records a decision on the gated tool call. The decision is held on the part\n * until every gate in the batch is answered, because AG-UI resumes a run with\n * one response per open interrupt rather than one response per gate. A chosen\n * `optionId` is not recorded: the projection exposes an approval without\n * options, and core rejects an `optionId` absent from them, so no option can\n * reach this seam.\n */\nexport const withToolApprovalDecision = (\n content: readonly ThreadAssistantMessagePart[],\n { approvalId, approved, reason }: RespondToToolApprovalOptions,\n): readonly ThreadAssistantMessagePart[] => {\n let changed = false;\n const next = content.map((part) => {\n if (part.type !== \"tool-call\") return part;\n const approval = part.approval;\n if (approval?.id !== approvalId || !isPending(approval)) return part;\n changed = true;\n return {\n ...part,\n approval: {\n ...approval,\n approved,\n ...(reason !== undefined && { reason }),\n },\n };\n });\n return changed ? next : content;\n};\n\n/**\n * Maps the recorded decisions onto AG-UI resume entries, or `null` while the\n * batch is incomplete. A denial is a resolved response carrying\n * `approved: false`, not a cancellation: `cancelled` means the user abandoned\n * the interrupt without meaningful input and carries no payload, which would\n * drop the denial reason on the wire.\n */\nexport const buildToolApprovalResume = (\n content: readonly ThreadAssistantMessagePart[],\n interrupts: readonly AgUiInterrupt[],\n): AgUiResumeEntry[] | null => {\n const open = new Set(interrupts.map((interrupt) => interrupt.id));\n // Decisions accumulate in a `Map` rather than in an object literal, because an\n // interrupt id of `__proto__` would otherwise write to the prototype chain\n // instead of recording a response, and `constructor` or `toString` would\n // report themselves as answered while undecided.\n const responses = new Map<string, { status: \"resolved\"; payload: unknown }>();\n for (const part of content) {\n if (part.type !== \"tool-call\") continue;\n const approval = part.approval;\n if (!approval || approval.approved === undefined) continue;\n if (!open.has(approval.id)) continue;\n responses.set(approval.id, {\n status: \"resolved\",\n payload: {\n approved: approval.approved,\n ...(approval.reason !== undefined && { reason: approval.reason }),\n },\n });\n }\n if (interrupts.some((interrupt) => !responses.has(interrupt.id))) return null;\n // The schema is dropped rather than cast: AG-UI's own interrupt type cannot\n // express a boolean schema, and `buildResumeArray` reads only the ids.\n return buildResumeArray(\n interrupts.map((interrupt) => {\n const { responseSchema: _schema, ...rest } =\n withoutRawResponseSchema(interrupt);\n return rest;\n }),\n Object.fromEntries(responses),\n );\n};\n\nconst settle = (\n approval: ToolApproval,\n entry: AgUiResumeEntry,\n): ToolApproval | undefined => {\n // A decision this gate recorded locally but never sent is dropped rather than\n // displayed beside the settlement it contradicts: a submitted entry carries\n // the whole outcome, so no field of an earlier one may survive it.\n const {\n approved: _approved,\n reason: _reason,\n resolution: _resolution,\n ...rest\n } = approval;\n if (entry.status === \"cancelled\") {\n return { ...rest, resolution: \"cancelled\" };\n }\n const payload = entry.payload;\n if (\n typeof payload !== \"object\" ||\n payload === null ||\n typeof (payload as { approved?: unknown }).approved !== \"boolean\"\n ) {\n // The entry settles the gate with no decision. Nothing is fabricated, and a\n // decision recorded locally is dropped: the payload that went out carried\n // none, so displaying one would show what was never sent. The gate is still\n // marked terminal, because the interrupt it belonged to is closed and a\n // click on it afterwards would find nothing pending.\n return { ...rest, resolution: \"cancelled\" };\n }\n const { approved, reason } = payload as {\n approved: boolean;\n reason?: unknown;\n };\n return {\n ...rest,\n approved,\n ...(typeof reason === \"string\" && { reason }),\n };\n};\n\n/**\n * Settles every gate the submitted resume array names, so the displayed outcome\n * cannot contradict the wire. A decision recorded locally is not exempt: a gate\n * approved while the batch waited on its sibling is still cancelled when\n * `steerAway` submits a cancellation for it. A gate is shown as cancelled only\n * where its own entry is cancelled; a resolved entry supplies the decision it\n * carries, and one that carries none settles the gate with no decision rather\n * than fabricating one or keeping an unsent local decision beside it.\n *\n * A settlement always overrides an earlier one, in either direction. A resolved\n * settlement leaves no state a locally recorded decision does not also leave —\n * core's `resolution` covers only `cancelled` and `expired` — so honoring the\n * first settlement instead would mean treating an unsent local decision as\n * terminal, which is exactly what the cancellation case must override.\n */\nexport const withSettledToolApprovals = (\n content: readonly ThreadAssistantMessagePart[],\n resume: readonly AgUiResumeEntry[],\n): readonly ThreadAssistantMessagePart[] => {\n if (resume.length === 0) return content;\n const byId = new Map(resume.map((entry) => [entry.interruptId, entry]));\n let changed = false;\n const next = content.map((part) => {\n if (part.type !== \"tool-call\") return part;\n const approval = part.approval;\n if (!approval) return part;\n const entry = byId.get(approval.id);\n if (!entry) return part;\n const settled = settle(approval, entry);\n if (!settled) return part;\n changed = true;\n return { ...part, approval: settled };\n });\n return changed ? next : content;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool-approval.js","names":[],"sources":["../../../src/runtime/adapter/tool-approval.ts"],"sourcesContent":["import {\n iterateToolCallParts,\n mapToolCallPartsDeep,\n} from \"@assistant-ui/core/internal\";\nimport { buildResumeArray } from \"@ag-ui/client\";\nimport type {\n RespondToToolApprovalOptions,\n ThreadAssistantMessagePart,\n ToolCallMessagePart,\n} from \"@assistant-ui/core\";\nimport type { AgUiInterrupt, AgUiResumeEntry } from \"../types\";\nimport {\n readRawResponseSchema,\n withoutRawResponseSchema,\n} from \"../interrupt-internals\";\n\ntype ToolApproval = NonNullable<ToolCallMessagePart[\"approval\"]>;\n\nconst EMPTY_APPROVALS: ReadonlyMap<string, ToolApproval> = new Map();\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isString = (value: unknown) => typeof value === \"string\";\n\nconst JSON_SCHEMA_TYPES = new Set([\n \"null\",\n \"boolean\",\n \"object\",\n \"array\",\n \"number\",\n \"integer\",\n \"string\",\n]);\n\nconst isUniqueStrings = (value: unknown) =>\n Array.isArray(value) &&\n value.every(isString) &&\n new Set(value).size === value.length;\n\nconst isSchemaType = (type: unknown) =>\n (isString(type) && JSON_SCHEMA_TYPES.has(type as string)) ||\n (Array.isArray(type) &&\n type.length > 0 &&\n isUniqueStrings(type) &&\n type.every((entry) => JSON_SCHEMA_TYPES.has(entry as string)));\n\nconst acceptsType = (type: unknown, primitive: string) =>\n type === primitive ||\n (Array.isArray(type) && isSchemaType(type) && type.includes(primitive));\n\nconst acceptsObject = (type: unknown) => acceptsType(type, \"object\");\n\nconst SUB_SCHEMA_ANNOTATIONS = new Set([\n \"$schema\",\n \"$id\",\n \"title\",\n \"description\",\n]);\n\n/**\n * A field schema accepts a value of `primitive` only where nothing in it can\n * narrow past the type: an annotation carries no constraint, and an absent\n * `type` constrains nothing. Any other keyword — `const`, `enum`, `anyOf`,\n * `format` — can reject the value this seam sends, so it is not interpreted.\n */\nconst acceptsOnly = (schema: unknown, primitive: string) =>\n isPlainObject(schema) &&\n Object.entries(schema).every(([keyword, value]) =>\n keyword === \"type\"\n ? acceptsType(value, primitive)\n : SUB_SCHEMA_ANNOTATIONS.has(keyword) && isString(value),\n );\n\n/**\n * Keywords of a sub-schema whose value shape a validator reads, each paired\n * with that shape: the same shapes the top-level schema is held to, plus\n * `properties` and `items`, which carry schemas, so a malformed one nested\n * under them is as uncompilable as a malformed one at the top. A keyword this\n * seam cannot judge is left alone: unlike the top-level schema, a field the\n * seam never sends needs only to be compilable, not permissive.\n */\nconst SUB_SCHEMA_SHAPES = new Map<string, (value: unknown) => boolean>([\n [\"type\", isSchemaType],\n [\"required\", isUniqueStrings],\n ...[...SUB_SCHEMA_ANNOTATIONS].map(\n (annotation) =>\n [annotation, isString] as [string, (value: unknown) => boolean],\n ),\n [\n \"properties\",\n (value) =>\n isPlainObject(value) &&\n Object.values(value).every((schema) => isWellFormedSubSchema(schema)),\n ],\n [\"items\", (value) => isWellFormedSubSchema(value)],\n]);\n\n/**\n * A schema the server cannot compile fails the run whatever payload follows, so\n * a field this seam never sends is still checked for the shape a validator\n * reads it as. A boolean is a schema in its own right.\n */\nconst isWellFormedSubSchema = (schema: unknown): boolean =>\n typeof schema === \"boolean\" ||\n (isPlainObject(schema) &&\n Object.entries(schema).every(([keyword, value]) => {\n const holds = SUB_SCHEMA_SHAPES.get(keyword);\n return holds === undefined || holds(value);\n }));\n\n/**\n * AG-UI's own tool-approval schema declares the fields it expects under\n * `properties`, so a declaration is read where it cannot reject what the seam\n * sends: `approved` must accept a boolean and `reason` a string. A field the\n * seam never sends is unconstrained by `required` (which admits only\n * `approved`), so only its well-formedness as a sub-schema is checked — the\n * `editedArgs` of the spec's approve-with-edits shape is such a field.\n */\nconst acceptsSeamProperties = (properties: unknown) =>\n isPlainObject(properties) &&\n Object.entries(properties).every(([field, schema]) => {\n if (field === \"approved\") return acceptsOnly(schema, \"boolean\");\n if (field === \"reason\") return acceptsOnly(schema, \"string\");\n return isWellFormedSubSchema(schema);\n });\n\nconst acceptsSeamPayload = (required: unknown) =>\n Array.isArray(required) &&\n required.every((field) => field === \"approved\") &&\n new Set(required).size === required.length;\n\n/**\n * Keywords that cannot reject a payload this seam emits, each paired with the\n * check that its value is the well-formed shape the keyword is read as. A\n * keyword whose value a validator would reject as an invalid schema is not\n * decided here, because a server that compiles the schema fails the run. An\n * unlisted keyword — `additionalProperties`, `allOf`, `$ref` — makes the gate\n * bespoke rather than being interpreted. The map is keyed by\n * `Map` rather than by object property, because a schema key of `constructor`\n * or `toString` would otherwise read an `Object.prototype` member as its\n * validator and `__proto__` would read the prototype itself.\n */\nconst SEAM_SAFE_KEYWORDS = new Map<string, (value: unknown) => boolean>([\n [\"$schema\", isString],\n [\"$id\", isString],\n [\"title\", isString],\n [\"description\", isString],\n [\"type\", acceptsObject],\n [\"required\", acceptsSeamPayload],\n [\"properties\", acceptsSeamProperties],\n]);\n\n/**\n * The seam sends `{ approved }` and may add `reason`, so it may answer a\n * `responseSchema` only where acceptance of those exact payloads is established\n * by construction. AG-UI answers a resume payload its schema rejects with\n * `RunError`, and the host can always build the exact payload through the\n * bespoke interrupt hooks, so an undecidable schema is left to them. Only an\n * absent schema and a plain object of well-formed allowed keywords establish\n * that: a boolean schema (`false` rejects every payload), a malformed one, and\n * a keyword whose value is not the shape it interprets are all left bespoke.\n */\nconst isSeamAnswerable = (schema: unknown) => {\n if (schema === undefined) return true;\n if (!isPlainObject(schema)) return false;\n return Object.entries(schema).every(([keyword, value]) => {\n const accepts = SEAM_SAFE_KEYWORDS.get(keyword);\n return accepts !== undefined && accepts(value);\n });\n};\n\nconst isGate = (\n interrupt: AgUiInterrupt,\n): interrupt is AgUiInterrupt & { toolCallId: string } =>\n interrupt.reason === \"tool_call\" &&\n !!interrupt.id &&\n !!interrupt.toolCallId &&\n // A restored interrupt is untyped, so `responseSchema` may hold any persisted\n // shape; a non-object schema received live arrives on the internal carrier.\n readRawResponseSchema(interrupt) === undefined &&\n isSeamAnswerable(interrupt.responseSchema);\n\nconst isPending = (approval: ToolCallMessagePart[\"approval\"]) =>\n !!approval && approval.approved === undefined && !approval.resolution;\n\n/**\n * AG-UI binds a decision to a tool call with `reason: \"tool_call\"`;\n * `confirmation` is a free-standing yes/no and `input_required` wants structured\n * input, so both stay on the bespoke interrupt hooks, as does a `tool_call`\n * whose `responseSchema` asks for more than this seam can send. A batch mixing a\n * gate with any other interrupt projects nothing: AG-UI resumes with one\n * response per open interrupt, so a half-owned batch cannot be completed from\n * either side alone.\n *\n * The same rule covers binding. A gate naming a tool call the message does not\n * render can never be decided through this seam, so the batch it belongs to\n * could never be completed either: the decisions taken on its visible siblings\n * would sit on the message while the run waited for a response that no click\n * can produce. `boundToolCallIds` spans every call the run produced — root or\n * nested on ToolCallMessagePart.messages — so only a gate naming a call the\n * message renders nowhere leaves the batch bespoke.\n */\nexport const projectAgUiToolApprovals = (\n interrupts: readonly AgUiInterrupt[] | undefined,\n boundToolCallIds?: ReadonlySet<string>,\n): ReadonlyMap<string, ToolApproval> => {\n if (!interrupts?.length || !interrupts.every(isGate)) return EMPTY_APPROVALS;\n if (\n boundToolCallIds &&\n !interrupts.every((interrupt) => boundToolCallIds.has(interrupt.toolCallId))\n ) {\n return EMPTY_APPROVALS;\n }\n // Two gates naming one tool call would collapse into a single approval, so\n // the batch could never be completed: the surviving gate takes one decision\n // while the resume array still waits on the id that no part carries.\n const toolCallIds = interrupts.map((interrupt) => interrupt.toolCallId);\n if (new Set(toolCallIds).size !== toolCallIds.length) return EMPTY_APPROVALS;\n return new Map(\n interrupts.map((interrupt) => [\n interrupt.toolCallId,\n {\n id: interrupt.id,\n ...(interrupt.message !== undefined && { prompt: interrupt.message }),\n },\n ]),\n );\n};\n\n/**\n * Records a decision on the gated tool call. The decision is held on the part\n * until every gate in the batch is answered, because AG-UI resumes a run with\n * one response per open interrupt rather than one response per gate. A chosen\n * `optionId` is not recorded: the projection exposes an approval without\n * options, and core rejects an `optionId` absent from them, so no option can\n * reach this seam.\n */\nexport const withToolApprovalDecision = (\n content: readonly ThreadAssistantMessagePart[],\n { approvalId, approved, reason }: RespondToToolApprovalOptions,\n): readonly ThreadAssistantMessagePart[] => {\n const { content: next, changed } = mapToolCallPartsDeep(content, (part) => {\n const approval = part.approval;\n if (approval?.id !== approvalId || !isPending(approval)) return part;\n return {\n ...part,\n approval: {\n ...approval,\n approved,\n ...(reason !== undefined && { reason }),\n },\n };\n });\n return changed ? next : content;\n};\n\n/**\n * Maps the recorded decisions onto AG-UI resume entries, or `null` while the\n * batch is incomplete. A denial is a resolved response carrying\n * `approved: false`, not a cancellation: `cancelled` means the user abandoned\n * the interrupt without meaningful input and carries no payload, which would\n * drop the denial reason on the wire.\n */\nexport const buildToolApprovalResume = (\n content: readonly ThreadAssistantMessagePart[],\n interrupts: readonly AgUiInterrupt[],\n): AgUiResumeEntry[] | null => {\n const open = new Set(interrupts.map((interrupt) => interrupt.id));\n // Decisions accumulate in a `Map` rather than in an object literal, because an\n // interrupt id of `__proto__` would otherwise write to the prototype chain\n // instead of recording a response, and `constructor` or `toString` would\n // report themselves as answered while undecided.\n const responses = new Map<string, { status: \"resolved\"; payload: unknown }>();\n for (const part of iterateToolCallParts(content)) {\n const approval = part.approval;\n if (!approval || approval.approved === undefined) continue;\n if (!open.has(approval.id)) continue;\n responses.set(approval.id, {\n status: \"resolved\",\n payload: {\n approved: approval.approved,\n ...(approval.reason !== undefined && { reason: approval.reason }),\n },\n });\n }\n if (interrupts.some((interrupt) => !responses.has(interrupt.id))) return null;\n // The schema is dropped rather than cast: AG-UI's own interrupt type cannot\n // express a boolean schema, and `buildResumeArray` reads only the ids.\n return buildResumeArray(\n interrupts.map((interrupt) => {\n const { responseSchema: _schema, ...rest } =\n withoutRawResponseSchema(interrupt);\n return rest;\n }),\n Object.fromEntries(responses),\n );\n};\n\nconst settle = (\n approval: ToolApproval,\n entry: AgUiResumeEntry,\n): ToolApproval | undefined => {\n // A decision this gate recorded locally but never sent is dropped rather than\n // displayed beside the settlement it contradicts: a submitted entry carries\n // the whole outcome, so no field of an earlier one may survive it.\n const {\n approved: _approved,\n reason: _reason,\n resolution: _resolution,\n ...rest\n } = approval;\n if (entry.status === \"cancelled\") {\n return { ...rest, resolution: \"cancelled\" };\n }\n const payload = entry.payload;\n if (\n typeof payload !== \"object\" ||\n payload === null ||\n typeof (payload as { approved?: unknown }).approved !== \"boolean\"\n ) {\n // The entry settles the gate with no decision. Nothing is fabricated, and a\n // decision recorded locally is dropped: the payload that went out carried\n // none, so displaying one would show what was never sent. The gate is still\n // marked terminal, because the interrupt it belonged to is closed and a\n // click on it afterwards would find nothing pending.\n return { ...rest, resolution: \"cancelled\" };\n }\n const { approved, reason } = payload as {\n approved: boolean;\n reason?: unknown;\n };\n return {\n ...rest,\n approved,\n ...(typeof reason === \"string\" && { reason }),\n };\n};\n\n/**\n * Settles every gate the submitted resume array names, so the displayed outcome\n * cannot contradict the wire. A decision recorded locally is not exempt: a gate\n * approved while the batch waited on its sibling is still cancelled when\n * `steerAway` submits a cancellation for it. A gate is shown as cancelled only\n * where its own entry is cancelled; a resolved entry supplies the decision it\n * carries, and one that carries none settles the gate with no decision rather\n * than fabricating one or keeping an unsent local decision beside it.\n *\n * A settlement always overrides an earlier one, in either direction. A resolved\n * settlement leaves no state a locally recorded decision does not also leave —\n * core's `resolution` covers only `cancelled` and `expired` — so honoring the\n * first settlement instead would mean treating an unsent local decision as\n * terminal, which is exactly what the cancellation case must override.\n */\nexport const withSettledToolApprovals = (\n content: readonly ThreadAssistantMessagePart[],\n resume: readonly AgUiResumeEntry[],\n): readonly ThreadAssistantMessagePart[] => {\n if (resume.length === 0) return content;\n const byId = new Map(resume.map((entry) => [entry.interruptId, entry]));\n const { content: next, changed } = mapToolCallPartsDeep(content, (part) => {\n const approval = part.approval;\n if (!approval) return part;\n const entry = byId.get(approval.id);\n if (!entry) return part;\n const settled = settle(approval, entry);\n if (!settled) return part;\n return { ...part, approval: settled };\n });\n return changed ? next : content;\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-parser.d.ts","names":[],"sources":["../../src/runtime/event-parser.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"event-parser.d.ts","names":[],"sources":["../../src/runtime/event-parser.ts"],"mappings":";;;KAUY;EACV,SAAS;;cAmHE,iBACX,gBACA,UAAU,0BACT"}
|