@assistant-ui/ai-sdk 0.0.8 → 0.0.9
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/LICENSE +1 -1
- package/dist/converters/convertMessage.d.ts +2 -0
- package/dist/converters/convertMessage.d.ts.map +1 -1
- package/dist/converters/convertMessage.js +14 -4
- package/dist/converters/convertMessage.js.map +1 -1
- package/dist/runtime/AISDKChat.d.ts.map +1 -1
- package/dist/runtime/AISDKChat.js.map +1 -1
- package/dist/runtime/sdkIdentity.js +1 -1
- package/dist/runtime/useAISDKRuntime.d.ts +8 -1
- package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
- package/dist/runtime/useAISDKRuntime.js +212 -34
- package/dist/runtime/useAISDKRuntime.js.map +1 -1
- package/dist/runtime/useChatRuntime.js +1 -1
- package/dist/runtime/useChatThread.d.ts.map +1 -1
- package/dist/runtime/useChatThread.js +40 -10
- package/dist/runtime/useChatThread.js.map +1 -1
- package/dist/runtime/useExternalHistory.d.ts.map +1 -1
- package/dist/runtime/useExternalHistory.js +2 -1
- package/dist/runtime/useExternalHistory.js.map +1 -1
- package/dist/transport/AssistantChatTransport.d.ts.map +1 -1
- package/dist/transport/AssistantChatTransport.js +9 -2
- package/dist/transport/AssistantChatTransport.js.map +1 -1
- package/package.json +10 -7
- package/src/converters/convertMessage.test.ts +63 -0
- package/src/converters/convertMessage.ts +24 -2
- package/src/runtime/AISDKChat.ts +0 -4
- package/src/runtime/AISDKThreads.test.ts +26 -0
- package/src/runtime/useAISDKRuntime.approval.integration.test.tsx +1588 -19
- package/src/runtime/useAISDKRuntime.approval.test.tsx +27 -0
- package/src/runtime/useAISDKRuntime.fast-refresh.test.tsx +182 -0
- package/src/runtime/useAISDKRuntime.ts +397 -28
- package/src/runtime/useChatRuntime.fast-refresh.test.tsx +94 -0
- package/src/runtime/useChatRuntime.integration.test.tsx +134 -112
- package/src/runtime/useChatRuntime.test.ts +7 -7
- package/src/runtime/useChatThread.test.ts +166 -2
- package/src/runtime/useChatThread.transport.test.tsx +5 -2
- package/src/runtime/useChatThread.ts +56 -17
- package/src/runtime/useExternalHistory.ts +12 -1
- package/src/transport/AssistantChatTransport.test.ts +164 -0
- package/src/transport/AssistantChatTransport.ts +22 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useExternalHistory.js","names":["AssistantRuntime","ThreadHistoryAdapter","ThreadMessage","MessageFormatAdapter","MessageFormatItem","MessageFormatRepository","ExportedMessageRepository","RespondToToolApprovalOptions","Unstable_ToolInteractionLog","getExternalStoreMessages","MessageRepository","readToolInteractionLog","useAui","useRef","useEffect","useState","RefObject","useCallback","useMemo","toExportedMessageRepository","toThreadMessages","messages","TMessage","survivingIds","Set","survivors","flatMap","m","message","console","warn","parentId","has","add","id","headId","isAwaitingToolApproval","status","type","reason","isTerminalMessage","undefined","TOOL_ARTIFACTS_METADATA_KEY","TOOL_INTERACTIONS_METADATA_KEY","TOOL_APPROVAL_RESPONSES_METADATA_KEY","StoredToolApprovalResponse","Omit","isRecord","value","Record","Array","isArray","collectToolArtifacts","toolArtifacts","ReadonlyMap","entries","content","part","artifact","get","toolCallId","const","length","Object","fromEntries","collectToolInteractions","toolInteractions","interactions","collectToolApprovalResponses","toolApprovalResponses","approval","response","approved","optionId","text","addToolData","parts","toolCallIds","artifacts","hasOwn","approvalIds","approvalId","approvalResponses","hasArtifacts","keys","hasInteractions","hasApprovalResponses","metadata","restoreToolData","Map","set","log","_","__","___","restMetadata","_metadata","restMessage","encodeContent","storageFormatAdapter","item","JSON","stringify","encode","useExternalHistory","runtimeRef","historyAdapter","onSetMessages","onToolArtifactsRestored","onToolInteractionsRestored","onToolApprovalResponsesRestored","loadedRef","itemEpoch","setItemEpoch","aui","optionalThreadListItem","threadListItem","source","hasLoaded","setHasLoaded","historyIds","deferredTelemetryIds","persistedInnerMessages","onSetMessagesRef","current","formatAdapter","withFormat","Error","isLoading","loadHistory","repo","load","clear","restoredMessages","map","restoredRepo","index","getId","converted","thread","import","tempRepo","getMessages","error","pin","remoteId","getState","subscribe","n","threadState","isRunning","runStartRef","persistTimerRef","ReturnType","setTimeout","persistInFlightRef","Promise","resolve","stepBoundariesRef","wasRunningRef","toolCallCountRef","persistToolInteractions","messageId","persistence","then","update","find","some","innerMessage","previousMessages","slice","indexOf","at","storedToolArtifacts","storedToolInteractions","storedToolApprovalResponses","innerId","persisted","catch","adapter","idleScheduledInnerIds","unpersistedInnerIds","filter","unsubscribe","wasRunning","lastMsg","role","currentToolCallCount","p","push","Date","now","clearTimeout","pending","every","persistSettled","initialThreadState","ignoreRunning","latest","boundaries","durationMs","lastAssistant","findLast","tcCount","totalSteps","stepDur","i","Math","round","stepTimestamps","endMs","start_ms","end_ms","telemetryOptions","lastInnerMessageId","getLastInnerId","msgs","toBatchItems","msg","idx","innerMessages","isTerminal","isAwaitingToolCalls","isReady","batchItems","append","delete","reportTelemetry","deleteMessage","deleteMessages","bind","messageIndex","findIndex","previousInnerMessages","itemsToDelete","deletion","persistToolApprovalResponses"],"sources":["../../src/runtime/useExternalHistory.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n AssistantRuntime,\n ThreadHistoryAdapter,\n ThreadMessage,\n MessageFormatAdapter,\n MessageFormatItem,\n MessageFormatRepository,\n ExportedMessageRepository,\n RespondToToolApprovalOptions,\n Unstable_ToolInteractionLog,\n} from \"@assistant-ui/core\";\nimport { getExternalStoreMessages } from \"@assistant-ui/core\";\nimport {\n MessageRepository,\n readToolInteractionLog,\n} from \"@assistant-ui/core/internal\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n useRef,\n useEffect,\n useState,\n type RefObject,\n useCallback,\n useMemo,\n} from \"react\";\n\nexport const toExportedMessageRepository = <TMessage>(\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n messages: MessageFormatRepository<TMessage>,\n): ExportedMessageRepository => {\n const survivingIds = new Set<string>();\n const survivors = messages.messages.flatMap((m) => {\n const message = toThreadMessages([m.message])[0];\n if (!message) {\n console.warn(\"Skipping a stored message that could not be loaded.\");\n return [];\n }\n if (m.parentId && !survivingIds.has(m.parentId)) return [];\n survivingIds.add(message.id);\n return [{ ...m, message }];\n });\n\n return {\n headId:\n messages.headId && survivingIds.has(messages.headId)\n ? messages.headId\n : null,\n messages: survivors,\n };\n};\n\nconst isAwaitingToolApproval = (message: ThreadMessage) =>\n message.status?.type === \"requires-action\" &&\n message.status.reason === \"tool-calls\";\n\nconst isTerminalMessage = (message: ThreadMessage) =>\n message.status === undefined ||\n message.status.type === \"complete\" ||\n message.status.type === \"incomplete\";\n\nconst TOOL_ARTIFACTS_METADATA_KEY = \"__aui_toolArtifacts\";\nconst TOOL_INTERACTIONS_METADATA_KEY = \"__aui_toolInteractions\";\nconst TOOL_APPROVAL_RESPONSES_METADATA_KEY = \"__aui_toolApprovalResponses\";\n\ntype StoredToolApprovalResponse = Omit<\n RespondToToolApprovalOptions,\n \"approvalId\"\n>;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value);\n\nconst collectToolArtifacts = (\n message: ThreadMessage,\n toolArtifacts: ReadonlyMap<string, unknown> | undefined,\n) => {\n if (!toolArtifacts) return undefined;\n const entries = message.content.flatMap((part) => {\n if (part.type !== \"tool-call\") return [];\n const artifact = toolArtifacts.get(part.toolCallId);\n return artifact === undefined ? [] : [[part.toolCallId, artifact] as const];\n });\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n};\n\nconst collectToolInteractions = (\n message: ThreadMessage,\n toolInteractions:\n | ReadonlyMap<string, Unstable_ToolInteractionLog>\n | undefined,\n) => {\n if (!toolInteractions) return undefined;\n const entries = message.content.flatMap((part) => {\n if (part.type !== \"tool-call\") return [];\n const interactions = toolInteractions.get(part.toolCallId);\n return interactions === undefined\n ? []\n : [[part.toolCallId, interactions] as const];\n });\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n};\n\nconst collectToolApprovalResponses = (\n message: ThreadMessage,\n toolApprovalResponses:\n | ReadonlyMap<string, RespondToToolApprovalOptions>\n | undefined,\n) => {\n if (!toolApprovalResponses) return undefined;\n const entries = message.content.flatMap((part) => {\n if (part.type !== \"tool-call\" || !part.approval) return [];\n const response = toolApprovalResponses.get(part.approval.id);\n if (!response) return [];\n return [\n [\n part.approval.id,\n {\n approved: response.approved,\n ...(response.optionId != null && { optionId: response.optionId }),\n ...(response.text != null && { text: response.text }),\n ...(response.reason != null && { reason: response.reason }),\n },\n ] as const,\n ];\n });\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n};\n\nconst addToolData = <TMessage>(\n message: TMessage,\n toolArtifacts: Record<string, unknown> | undefined,\n toolInteractions: Record<string, Unstable_ToolInteractionLog> | undefined,\n toolApprovalResponses: Record<string, StoredToolApprovalResponse> | undefined,\n): TMessage => {\n if (\n (!toolArtifacts && !toolInteractions && !toolApprovalResponses) ||\n !isRecord(message) ||\n !Array.isArray(message.parts)\n )\n return message;\n const toolCallIds = message.parts.flatMap((part) => {\n if (!isRecord(part) || typeof part.toolCallId !== \"string\") return [];\n return [part.toolCallId];\n });\n const artifacts = toolArtifacts\n ? Object.fromEntries(\n toolCallIds.flatMap((toolCallId) =>\n Object.hasOwn(toolArtifacts, toolCallId)\n ? [[toolCallId, toolArtifacts[toolCallId]] as const]\n : [],\n ),\n )\n : undefined;\n const interactions = toolInteractions\n ? Object.fromEntries(\n toolCallIds.flatMap((toolCallId) =>\n Object.hasOwn(toolInteractions, toolCallId)\n ? [[toolCallId, toolInteractions[toolCallId]] as const]\n : [],\n ),\n )\n : undefined;\n const approvalIds = message.parts.flatMap((part) => {\n if (!isRecord(part) || !isRecord(part.approval)) return [];\n const approvalId = part.approval.id;\n return typeof approvalId === \"string\" ? [approvalId] : [];\n });\n const approvalResponses = toolApprovalResponses\n ? Object.fromEntries(\n approvalIds.flatMap((approvalId) =>\n Object.hasOwn(toolApprovalResponses, approvalId)\n ? [[approvalId, toolApprovalResponses[approvalId]] as const]\n : [],\n ),\n )\n : undefined;\n const hasArtifacts = !!artifacts && Object.keys(artifacts).length > 0;\n const hasInteractions =\n !!interactions && Object.keys(interactions).length > 0;\n const hasApprovalResponses =\n !!approvalResponses && Object.keys(approvalResponses).length > 0;\n if (!hasArtifacts && !hasInteractions && !hasApprovalResponses)\n return message;\n const metadata = isRecord(message.metadata) ? message.metadata : {};\n return {\n ...message,\n metadata: {\n ...metadata,\n ...(hasArtifacts && { [TOOL_ARTIFACTS_METADATA_KEY]: artifacts }),\n ...(hasInteractions && {\n [TOOL_INTERACTIONS_METADATA_KEY]: interactions,\n }),\n ...(hasApprovalResponses && {\n [TOOL_APPROVAL_RESPONSES_METADATA_KEY]: approvalResponses,\n }),\n },\n } as TMessage;\n};\n\nconst restoreToolData = <TMessage>(\n message: TMessage,\n toolArtifacts: Map<string, unknown> | undefined,\n toolInteractions: Map<string, Unstable_ToolInteractionLog> | undefined,\n toolApprovalResponses: Map<string, RespondToToolApprovalOptions> | undefined,\n): TMessage => {\n if (!isRecord(message) || !isRecord(message.metadata)) return message;\n const metadata = message.metadata;\n const hasArtifacts = Object.hasOwn(metadata, TOOL_ARTIFACTS_METADATA_KEY);\n const hasInteractions = Object.hasOwn(\n metadata,\n TOOL_INTERACTIONS_METADATA_KEY,\n );\n const hasApprovalResponses = Object.hasOwn(\n metadata,\n TOOL_APPROVAL_RESPONSES_METADATA_KEY,\n );\n if (!hasArtifacts && !hasInteractions && !hasApprovalResponses)\n return message;\n const artifacts = metadata[TOOL_ARTIFACTS_METADATA_KEY];\n if (toolArtifacts && isRecord(artifacts)) {\n for (const [toolCallId, artifact] of Object.entries(artifacts)) {\n toolArtifacts.set(toolCallId, artifact);\n }\n }\n const interactions = metadata[TOOL_INTERACTIONS_METADATA_KEY];\n if (toolInteractions && isRecord(interactions)) {\n for (const [toolCallId, value] of Object.entries(interactions)) {\n const log = readToolInteractionLog(value);\n if (log) toolInteractions.set(toolCallId, log);\n }\n }\n const approvalResponses = metadata[TOOL_APPROVAL_RESPONSES_METADATA_KEY];\n if (toolApprovalResponses && isRecord(approvalResponses)) {\n for (const [approvalId, value] of Object.entries(approvalResponses)) {\n if (!isRecord(value) || typeof value.approved !== \"boolean\") continue;\n toolApprovalResponses.set(approvalId, {\n approvalId,\n approved: value.approved,\n ...(typeof value.optionId === \"string\" && {\n optionId: value.optionId,\n }),\n ...(typeof value.text === \"string\" && { text: value.text }),\n ...(typeof value.reason === \"string\" && { reason: value.reason }),\n });\n }\n }\n const {\n [TOOL_ARTIFACTS_METADATA_KEY]: _,\n [TOOL_INTERACTIONS_METADATA_KEY]: __,\n [TOOL_APPROVAL_RESPONSES_METADATA_KEY]: ___,\n ...restMetadata\n } = metadata;\n const { metadata: _metadata, ...restMessage } = message;\n return (\n Object.keys(restMetadata).length === 0\n ? restMessage\n : { ...restMessage, metadata: restMetadata }\n ) as TMessage;\n};\n\nconst encodeContent = <TMessage>(\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n item: MessageFormatItem<TMessage>,\n) => JSON.stringify(storageFormatAdapter.encode(item));\n\nexport const useExternalHistory = <TMessage>(\n runtimeRef: RefObject<AssistantRuntime>,\n historyAdapter: ThreadHistoryAdapter | undefined,\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n onSetMessages: (messages: TMessage[]) => void,\n toolArtifacts?: Map<string, unknown>,\n onToolArtifactsRestored?: () => void,\n toolInteractions?: Map<string, Unstable_ToolInteractionLog>,\n onToolInteractionsRestored?: () => void,\n toolApprovalResponses?: Map<string, RespondToToolApprovalOptions>,\n onToolApprovalResponsesRestored?: () => void,\n) => {\n const loadedRef = useRef(false);\n const [itemEpoch, setItemEpoch] = useState(0);\n\n const aui = useAui();\n const optionalThreadListItem = useCallback(\n () => (aui.threadListItem.source ? aui.threadListItem : null),\n [aui],\n );\n\n const [hasLoaded, setHasLoaded] = useState(false);\n\n const historyIds = useRef(new Set<string>());\n const deferredTelemetryIds = useRef(new Set<string>());\n // `content` is a snapshot taken at write time rather than a re-encode of `source`, because a retained message object can be mutated in place by the runtime that produced it.\n const persistedInnerMessages = useRef(\n new Map<string, { source: TMessage; content: string }>(),\n );\n\n const onSetMessagesRef = useRef(onSetMessages);\n useEffect(() => {\n onSetMessagesRef.current = onSetMessages;\n });\n\n const formatAdapter = useMemo(() => {\n if (!historyAdapter) return undefined;\n if (!historyAdapter.withFormat) {\n throw new Error(\n \"useAISDKRuntime: ThreadHistoryAdapter is missing the required `withFormat` method.\",\n );\n }\n return historyAdapter.withFormat<TMessage, any>(storageFormatAdapter);\n }, [historyAdapter, storageFormatAdapter]);\n\n const isLoading = formatAdapter != null && !hasLoaded;\n\n useEffect(() => {\n if (!formatAdapter || loadedRef.current) return undefined;\n\n const loadHistory = async () => {\n try {\n const repo = await formatAdapter.load();\n toolArtifacts?.clear();\n toolInteractions?.clear();\n toolApprovalResponses?.clear();\n const restoredMessages =\n repo?.messages.map((item) => ({\n ...item,\n message: restoreToolData(\n item.message,\n toolArtifacts,\n toolInteractions,\n toolApprovalResponses,\n ),\n })) ?? [];\n onToolArtifactsRestored?.();\n onToolInteractionsRestored?.();\n onToolApprovalResponsesRestored?.();\n if (repo && restoredMessages.length > 0) {\n const restoredRepo = { ...repo, messages: restoredMessages };\n for (const [index, m] of repo.messages.entries()) {\n persistedInnerMessages.current.set(\n storageFormatAdapter.getId(m.message),\n {\n source: restoredMessages[index]!.message,\n content: encodeContent(storageFormatAdapter, m),\n },\n );\n }\n const converted = toExportedMessageRepository(\n toThreadMessages,\n restoredRepo,\n );\n runtimeRef.current.thread.import(converted);\n\n const tempRepo = new MessageRepository();\n tempRepo.import(converted);\n const messages = tempRepo.getMessages();\n\n onSetMessagesRef.current(\n messages.flatMap(getExternalStoreMessages<TMessage>),\n );\n\n historyIds.current = new Set();\n for (const m of converted.messages) {\n historyIds.current.add(m.message.id);\n if (isAwaitingToolApproval(m.message)) {\n deferredTelemetryIds.current.add(m.message.id);\n }\n }\n }\n } catch (error) {\n console.error(\"Failed to load message history:\", error);\n } finally {\n setHasLoaded(true);\n }\n };\n\n formatAdapter.pin?.();\n\n const remoteId = optionalThreadListItem()?.getState().remoteId;\n if (!remoteId) {\n // History loads asynchronously against the thread list item; without a\n // remote id there is nothing to await, so the flag settles here.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setHasLoaded(true);\n return aui.subscribe(() => {\n if (optionalThreadListItem()?.getState().remoteId) {\n setItemEpoch((n) => n + 1);\n }\n });\n }\n\n const threadState = runtimeRef.current.thread.getState();\n if (threadState.isRunning || threadState.messages.length > 0) {\n loadedRef.current = true;\n setHasLoaded(true);\n return undefined;\n }\n\n loadedRef.current = true;\n void loadHistory();\n return undefined;\n }, [\n formatAdapter,\n toThreadMessages,\n runtimeRef,\n optionalThreadListItem,\n aui,\n itemEpoch,\n storageFormatAdapter,\n toolArtifacts,\n onToolArtifactsRestored,\n toolInteractions,\n onToolInteractionsRestored,\n toolApprovalResponses,\n onToolApprovalResponsesRestored,\n ]);\n\n const runStartRef = useRef<number | null>(null);\n const persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const persistInFlightRef = useRef<Promise<void>>(Promise.resolve());\n const stepBoundariesRef = useRef<number[]>([]);\n const wasRunningRef = useRef(false);\n const toolCallCountRef = useRef(0);\n\n const persistToolInteractions = useCallback(\n (messageId: string) => {\n const persistence = persistInFlightRef.current.then(async () => {\n if (!formatAdapter?.update) return;\n const messages = runtimeRef.current.thread.getState().messages;\n const message = messages.find(\n (item) =>\n item.id === messageId ||\n getExternalStoreMessages<TMessage>(item).some(\n (innerMessage) =>\n storageFormatAdapter.getId(innerMessage) === messageId,\n ),\n );\n if (!message) return;\n\n const previousMessages = messages\n .slice(0, messages.indexOf(message))\n .flatMap(getExternalStoreMessages<TMessage>);\n let parentId = previousMessages.at(-1)\n ? storageFormatAdapter.getId(previousMessages.at(-1)!)\n : null;\n const storedToolArtifacts = collectToolArtifacts(\n message,\n toolArtifacts,\n );\n const storedToolInteractions = collectToolInteractions(\n message,\n toolInteractions,\n );\n const storedToolApprovalResponses = collectToolApprovalResponses(\n message,\n toolApprovalResponses,\n );\n\n for (const innerMessage of getExternalStoreMessages<TMessage>(\n message,\n )) {\n const item = {\n parentId,\n message: addToolData(\n innerMessage,\n storedToolArtifacts,\n storedToolInteractions,\n storedToolApprovalResponses,\n ),\n };\n const innerId = storageFormatAdapter.getId(item.message);\n const persisted = persistedInnerMessages.current.get(innerId);\n if (persisted) {\n const content = encodeContent(storageFormatAdapter, item);\n if (content === persisted.content) {\n persisted.source = item.message;\n } else {\n await formatAdapter.update(item, innerId);\n persistedInnerMessages.current.set(innerId, {\n source: item.message,\n content,\n });\n }\n }\n parentId = innerId;\n }\n });\n persistInFlightRef.current = persistence.catch(() => {});\n return persistence.catch((error) => {\n console.error(\"Failed to persist tool data:\", error);\n });\n },\n [\n formatAdapter,\n runtimeRef,\n storageFormatAdapter,\n toolArtifacts,\n toolInteractions,\n toolApprovalResponses,\n ],\n );\n\n useEffect(() => {\n if (!formatAdapter) return;\n const adapter = formatAdapter;\n\n const idleScheduledInnerIds = new Set<string>();\n const unpersistedInnerIds = (message: ThreadMessage) =>\n isTerminalMessage(message)\n ? getExternalStoreMessages<TMessage>(message)\n .map((innerMessage) => storageFormatAdapter.getId(innerMessage))\n .filter((innerId) => !persistedInnerMessages.current.has(innerId))\n : [];\n\n const unsubscribe = runtimeRef.current.thread.subscribe(() => {\n const threadState = runtimeRef.current.thread.getState();\n const { isRunning } = threadState;\n const wasRunning = wasRunningRef.current;\n wasRunningRef.current = isRunning;\n\n // Track step boundaries by content changes (more reliable than isRunning)\n if (runStartRef.current != null) {\n const lastMsg = threadState.messages.at(-1);\n if (lastMsg?.role === \"assistant\") {\n const currentToolCallCount = lastMsg.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n while (toolCallCountRef.current < currentToolCallCount) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n toolCallCountRef.current++;\n }\n }\n }\n\n if (isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n adapter.pin?.();\n }\n // Cancel any pending persist — isRunning went back to true\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n }\n return;\n }\n\n if (wasRunning) {\n // Record step boundary offset (synchronous for accuracy)\n if (runStartRef.current != null) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n }\n } else {\n const pending = threadState.messages.flatMap(unpersistedInnerIds);\n if (pending.every((innerId) => idleScheduledInnerIds.has(innerId)))\n return;\n for (const innerId of pending) idleScheduledInnerIds.add(innerId);\n }\n\n // Debounce: wait one macrotask so agentic step flickers are absorbed\n if (persistTimerRef.current) clearTimeout(persistTimerRef.current);\n persistTimerRef.current = setTimeout(() => {\n persistSettled(false);\n }, 0);\n });\n\n const initialThreadState = runtimeRef.current.thread.getState();\n wasRunningRef.current = initialThreadState.isRunning;\n if (initialThreadState.isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n adapter.pin?.();\n }\n } else if (initialThreadState.messages.length > 0) {\n persistSettled(true);\n }\n\n function persistSettled(ignoreRunning: boolean) {\n persistTimerRef.current = null;\n const latest = runtimeRef.current.thread.getState();\n if (!ignoreRunning && latest.isRunning) return;\n\n const boundaries = stepBoundariesRef.current;\n const durationMs = boundaries.length > 0 ? boundaries.at(-1) : undefined;\n\n // Fallback: if only 1 boundary but message has multiple steps, distribute evenly\n if (boundaries.length === 1 && durationMs != null) {\n const lastAssistant = latest.messages.findLast(\n (m) => m.role === \"assistant\",\n );\n if (lastAssistant) {\n const tcCount = lastAssistant.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n if (tcCount > 0) {\n const totalSteps = tcCount + 1;\n const stepDur = durationMs / totalSteps;\n boundaries.length = 0;\n for (let i = 0; i < totalSteps; i++) {\n boundaries.push(Math.round((i + 1) * stepDur));\n }\n }\n }\n }\n\n // Build per-step timestamps when there are multiple steps\n const stepTimestamps =\n boundaries.length > 1\n ? boundaries.map((endMs, i) => ({\n start_ms: i === 0 ? 0 : boundaries[i - 1]!,\n end_ms: endMs,\n }))\n : undefined;\n\n runStartRef.current = null;\n stepBoundariesRef.current = [];\n\n const telemetryOptions = {\n ...(durationMs != null ? { durationMs } : undefined),\n ...(stepTimestamps != null ? { stepTimestamps } : undefined),\n };\n\n persistInFlightRef.current = persistInFlightRef.current\n .then(async () => {\n const { messages } = latest;\n let lastInnerMessageId: string | null = null;\n\n const getLastInnerId = (msgs: TMessage[]): string | null =>\n msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)!) : null;\n\n const toBatchItems = (\n msgs: TMessage[],\n toolArtifacts: Record<string, unknown> | undefined,\n toolInteractions:\n | Record<string, Unstable_ToolInteractionLog>\n | undefined,\n toolApprovalResponses:\n | Record<string, StoredToolApprovalResponse>\n | undefined,\n ) =>\n msgs.map((msg, idx) => ({\n parentId:\n idx === 0\n ? lastInnerMessageId\n : storageFormatAdapter.getId(msgs[idx - 1]!),\n message: addToolData(\n msg,\n toolArtifacts,\n toolInteractions,\n toolApprovalResponses,\n ),\n }));\n\n for (const message of messages) {\n const innerMessages = getExternalStoreMessages<TMessage>(message);\n\n const isTerminal = isTerminalMessage(message);\n const isAwaitingToolCalls = isAwaitingToolApproval(message);\n // A paused message's later content can only reach storage via update, so it is persisted early only when the adapter supports update.\n const isReady =\n isTerminal ||\n (isAwaitingToolCalls && adapter.update !== undefined);\n\n if (!isReady) {\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n\n if (!historyIds.current.has(message.id)) {\n historyIds.current.add(message.id);\n deferredTelemetryIds.current.add(message.id);\n }\n\n const batchItems = toBatchItems(\n innerMessages,\n collectToolArtifacts(message, toolArtifacts),\n collectToolInteractions(message, toolInteractions),\n collectToolApprovalResponses(message, toolApprovalResponses),\n );\n for (const item of batchItems) {\n const innerId = storageFormatAdapter.getId(item.message);\n const persisted = persistedInnerMessages.current.get(innerId);\n if (!persisted) {\n await adapter.append(item);\n persistedInnerMessages.current.set(innerId, {\n source: item.message,\n content: encodeContent(storageFormatAdapter, item),\n });\n } else if (\n persisted.source !== item.message &&\n durationMs !== undefined &&\n adapter.update\n ) {\n const content = encodeContent(storageFormatAdapter, item);\n if (content === persisted.content) {\n persisted.source = item.message;\n } else {\n try {\n await adapter.update(item, innerId);\n persistedInnerMessages.current.set(innerId, {\n source: item.message,\n content,\n });\n } catch {\n // A failed update leaves the stale baseline behind so the next run stop retries it.\n }\n }\n }\n }\n\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n\n if (deferredTelemetryIds.current.has(message.id) && isTerminal) {\n deferredTelemetryIds.current.delete(message.id);\n adapter.reportTelemetry?.(\n batchItems.map((item, index) => ({\n ...item,\n message: innerMessages[index]!,\n })),\n {\n ...telemetryOptions,\n message,\n },\n );\n }\n }\n })\n .catch((error) => {\n console.error(\"Failed to persist message history:\", error);\n });\n }\n\n return () => {\n unsubscribe();\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n persistSettled(false);\n }\n };\n }, [\n formatAdapter,\n storageFormatAdapter,\n runtimeRef,\n toolArtifacts,\n toolInteractions,\n toolApprovalResponses,\n ]);\n\n const deleteMessage = useCallback(\n async (messageId: string) => {\n const deleteMessages = formatAdapter?.delete?.bind(formatAdapter);\n if (!deleteMessages) return;\n\n const messages = runtimeRef.current.thread.getState().messages;\n const messageIndex = messages.findIndex((m) => m.id === messageId);\n if (messageIndex === -1) return;\n\n const previousInnerMessages = messages\n .slice(0, messageIndex)\n .flatMap(getExternalStoreMessages<TMessage>);\n let parentId = previousInnerMessages.at(-1)\n ? storageFormatAdapter.getId(previousInnerMessages.at(-1)!)\n : null;\n const itemsToDelete = getExternalStoreMessages<TMessage>(\n messages[messageIndex]!,\n ).map((message) => {\n const item = { parentId, message };\n parentId = storageFormatAdapter.getId(message);\n return item;\n });\n\n const deletion = persistInFlightRef.current.then(async () => {\n await deleteMessages(itemsToDelete);\n\n historyIds.current.delete(messageId);\n deferredTelemetryIds.current.delete(messageId);\n for (const item of itemsToDelete) {\n persistedInnerMessages.current.delete(\n storageFormatAdapter.getId(item.message),\n );\n }\n });\n\n persistInFlightRef.current = deletion.catch(() => {});\n await deletion;\n },\n [formatAdapter, runtimeRef, storageFormatAdapter],\n );\n\n return {\n isLoading,\n deleteMessage,\n persistToolInteractions,\n persistToolApprovalResponses: persistToolInteractions,\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useExternalHistory.js","names":["AssistantRuntime","ThreadHistoryAdapter","ThreadMessage","MessageFormatAdapter","MessageFormatItem","MessageFormatRepository","ExportedMessageRepository","RespondToToolApprovalOptions","Unstable_ToolInteractionLog","getExternalStoreMessages","MessageRepository","readToolInteractionLog","useAui","useRef","useEffect","useState","RefObject","useCallback","useMemo","toExportedMessageRepository","toThreadMessages","messages","TMessage","survivingIds","Set","survivors","flatMap","m","message","console","warn","parentId","has","add","id","headId","hasUnansweredApproval","content","some","part","type","approval","approved","undefined","resolution","isAwaitingToolApproval","status","reason","isTerminalMessage","TOOL_ARTIFACTS_METADATA_KEY","TOOL_INTERACTIONS_METADATA_KEY","TOOL_APPROVAL_RESPONSES_METADATA_KEY","StoredToolApprovalResponse","Omit","isRecord","value","Record","Array","isArray","collectToolArtifacts","toolArtifacts","ReadonlyMap","entries","artifact","get","toolCallId","const","length","Object","fromEntries","collectToolInteractions","toolInteractions","interactions","collectToolApprovalResponses","toolApprovalResponses","response","optionId","text","addToolData","parts","toolCallIds","artifacts","hasOwn","approvalIds","approvalId","approvalResponses","hasArtifacts","keys","hasInteractions","hasApprovalResponses","metadata","restoreToolData","Map","set","log","_","__","___","restMetadata","_metadata","restMessage","encodeContent","storageFormatAdapter","item","JSON","stringify","encode","useExternalHistory","runtimeRef","historyAdapter","onSetMessages","onToolArtifactsRestored","onToolInteractionsRestored","onToolApprovalResponsesRestored","loadedRef","itemEpoch","setItemEpoch","aui","optionalThreadListItem","threadListItem","source","hasLoaded","setHasLoaded","historyIds","deferredTelemetryIds","persistedInnerMessages","onSetMessagesRef","current","formatAdapter","withFormat","Error","isLoading","loadHistory","repo","load","clear","restoredMessages","map","restoredRepo","index","getId","converted","thread","import","tempRepo","getMessages","error","pin","remoteId","getState","subscribe","n","threadState","isRunning","runStartRef","persistTimerRef","ReturnType","setTimeout","persistInFlightRef","Promise","resolve","stepBoundariesRef","wasRunningRef","toolCallCountRef","persistToolInteractions","messageId","persistence","then","update","find","innerMessage","previousMessages","slice","indexOf","at","storedToolArtifacts","storedToolInteractions","storedToolApprovalResponses","innerId","persisted","catch","adapter","idleScheduledInnerIds","unpersistedInnerIds","filter","unsubscribe","wasRunning","lastMsg","role","currentToolCallCount","p","push","Date","now","clearTimeout","pending","every","persistSettled","initialThreadState","ignoreRunning","latest","boundaries","durationMs","lastAssistant","findLast","tcCount","totalSteps","stepDur","i","Math","round","stepTimestamps","endMs","start_ms","end_ms","telemetryOptions","lastInnerMessageId","getLastInnerId","msgs","toBatchItems","msg","idx","innerMessages","isTerminal","isAwaitingToolCalls","isReady","batchItems","append","delete","reportTelemetry","deleteMessage","deleteMessages","bind","messageIndex","findIndex","previousInnerMessages","itemsToDelete","deletion","persistToolApprovalResponses"],"sources":["../../src/runtime/useExternalHistory.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n AssistantRuntime,\n ThreadHistoryAdapter,\n ThreadMessage,\n MessageFormatAdapter,\n MessageFormatItem,\n MessageFormatRepository,\n ExportedMessageRepository,\n RespondToToolApprovalOptions,\n Unstable_ToolInteractionLog,\n} from \"@assistant-ui/core\";\nimport { getExternalStoreMessages } from \"@assistant-ui/core\";\nimport {\n MessageRepository,\n readToolInteractionLog,\n} from \"@assistant-ui/core/internal\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n useRef,\n useEffect,\n useState,\n type RefObject,\n useCallback,\n useMemo,\n} from \"react\";\n\nexport const toExportedMessageRepository = <TMessage>(\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n messages: MessageFormatRepository<TMessage>,\n): ExportedMessageRepository => {\n const survivingIds = new Set<string>();\n const survivors = messages.messages.flatMap((m) => {\n const message = toThreadMessages([m.message])[0];\n if (!message) {\n console.warn(\"Skipping a stored message that could not be loaded.\");\n return [];\n }\n if (m.parentId && !survivingIds.has(m.parentId)) return [];\n survivingIds.add(message.id);\n return [{ ...m, message }];\n });\n\n return {\n headId:\n messages.headId && survivingIds.has(messages.headId)\n ? messages.headId\n : null,\n messages: survivors,\n };\n};\n\nconst hasUnansweredApproval = (message: ThreadMessage) =>\n message.content.some(\n (part) =>\n part.type === \"tool-call\" &&\n part.approval != null &&\n part.approval.approved === undefined &&\n part.approval.resolution === undefined,\n );\n\n// Core reports a run paused on an unanswered approval as an interrupt, not as tool calls.\nconst isAwaitingToolApproval = (message: ThreadMessage) =>\n message.status?.type === \"requires-action\" &&\n (message.status.reason === \"tool-calls\" ||\n (message.status.reason === \"interrupt\" && hasUnansweredApproval(message)));\n\nconst isTerminalMessage = (message: ThreadMessage) =>\n message.status === undefined ||\n message.status.type === \"complete\" ||\n message.status.type === \"incomplete\";\n\nconst TOOL_ARTIFACTS_METADATA_KEY = \"__aui_toolArtifacts\";\nconst TOOL_INTERACTIONS_METADATA_KEY = \"__aui_toolInteractions\";\nconst TOOL_APPROVAL_RESPONSES_METADATA_KEY = \"__aui_toolApprovalResponses\";\n\ntype StoredToolApprovalResponse = Omit<\n RespondToToolApprovalOptions,\n \"approvalId\"\n>;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value);\n\nconst collectToolArtifacts = (\n message: ThreadMessage,\n toolArtifacts: ReadonlyMap<string, unknown> | undefined,\n) => {\n if (!toolArtifacts) return undefined;\n const entries = message.content.flatMap((part) => {\n if (part.type !== \"tool-call\") return [];\n const artifact = toolArtifacts.get(part.toolCallId);\n return artifact === undefined ? [] : [[part.toolCallId, artifact] as const];\n });\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n};\n\nconst collectToolInteractions = (\n message: ThreadMessage,\n toolInteractions:\n | ReadonlyMap<string, Unstable_ToolInteractionLog>\n | undefined,\n) => {\n if (!toolInteractions) return undefined;\n const entries = message.content.flatMap((part) => {\n if (part.type !== \"tool-call\") return [];\n const interactions = toolInteractions.get(part.toolCallId);\n return interactions === undefined\n ? []\n : [[part.toolCallId, interactions] as const];\n });\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n};\n\nconst collectToolApprovalResponses = (\n message: ThreadMessage,\n toolApprovalResponses:\n | ReadonlyMap<string, RespondToToolApprovalOptions>\n | undefined,\n) => {\n if (!toolApprovalResponses) return undefined;\n const entries = message.content.flatMap((part) => {\n if (part.type !== \"tool-call\" || !part.approval) return [];\n const response = toolApprovalResponses.get(part.approval.id);\n if (!response) return [];\n return [\n [\n part.approval.id,\n {\n approved: response.approved,\n ...(response.optionId != null && { optionId: response.optionId }),\n ...(response.text != null && { text: response.text }),\n ...(response.reason != null && { reason: response.reason }),\n },\n ] as const,\n ];\n });\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n};\n\nconst addToolData = <TMessage>(\n message: TMessage,\n toolArtifacts: Record<string, unknown> | undefined,\n toolInteractions: Record<string, Unstable_ToolInteractionLog> | undefined,\n toolApprovalResponses: Record<string, StoredToolApprovalResponse> | undefined,\n): TMessage => {\n if (\n (!toolArtifacts && !toolInteractions && !toolApprovalResponses) ||\n !isRecord(message) ||\n !Array.isArray(message.parts)\n )\n return message;\n const toolCallIds = message.parts.flatMap((part) => {\n if (!isRecord(part) || typeof part.toolCallId !== \"string\") return [];\n return [part.toolCallId];\n });\n const artifacts = toolArtifacts\n ? Object.fromEntries(\n toolCallIds.flatMap((toolCallId) =>\n Object.hasOwn(toolArtifacts, toolCallId)\n ? [[toolCallId, toolArtifacts[toolCallId]] as const]\n : [],\n ),\n )\n : undefined;\n const interactions = toolInteractions\n ? Object.fromEntries(\n toolCallIds.flatMap((toolCallId) =>\n Object.hasOwn(toolInteractions, toolCallId)\n ? [[toolCallId, toolInteractions[toolCallId]] as const]\n : [],\n ),\n )\n : undefined;\n const approvalIds = message.parts.flatMap((part) => {\n if (!isRecord(part) || !isRecord(part.approval)) return [];\n const approvalId = part.approval.id;\n return typeof approvalId === \"string\" ? [approvalId] : [];\n });\n const approvalResponses = toolApprovalResponses\n ? Object.fromEntries(\n approvalIds.flatMap((approvalId) =>\n Object.hasOwn(toolApprovalResponses, approvalId)\n ? [[approvalId, toolApprovalResponses[approvalId]] as const]\n : [],\n ),\n )\n : undefined;\n const hasArtifacts = !!artifacts && Object.keys(artifacts).length > 0;\n const hasInteractions =\n !!interactions && Object.keys(interactions).length > 0;\n const hasApprovalResponses =\n !!approvalResponses && Object.keys(approvalResponses).length > 0;\n if (!hasArtifacts && !hasInteractions && !hasApprovalResponses)\n return message;\n const metadata = isRecord(message.metadata) ? message.metadata : {};\n return {\n ...message,\n metadata: {\n ...metadata,\n ...(hasArtifacts && { [TOOL_ARTIFACTS_METADATA_KEY]: artifacts }),\n ...(hasInteractions && {\n [TOOL_INTERACTIONS_METADATA_KEY]: interactions,\n }),\n ...(hasApprovalResponses && {\n [TOOL_APPROVAL_RESPONSES_METADATA_KEY]: approvalResponses,\n }),\n },\n } as TMessage;\n};\n\nconst restoreToolData = <TMessage>(\n message: TMessage,\n toolArtifacts: Map<string, unknown> | undefined,\n toolInteractions: Map<string, Unstable_ToolInteractionLog> | undefined,\n toolApprovalResponses: Map<string, RespondToToolApprovalOptions> | undefined,\n): TMessage => {\n if (!isRecord(message) || !isRecord(message.metadata)) return message;\n const metadata = message.metadata;\n const hasArtifacts = Object.hasOwn(metadata, TOOL_ARTIFACTS_METADATA_KEY);\n const hasInteractions = Object.hasOwn(\n metadata,\n TOOL_INTERACTIONS_METADATA_KEY,\n );\n const hasApprovalResponses = Object.hasOwn(\n metadata,\n TOOL_APPROVAL_RESPONSES_METADATA_KEY,\n );\n if (!hasArtifacts && !hasInteractions && !hasApprovalResponses)\n return message;\n const artifacts = metadata[TOOL_ARTIFACTS_METADATA_KEY];\n if (toolArtifacts && isRecord(artifacts)) {\n for (const [toolCallId, artifact] of Object.entries(artifacts)) {\n toolArtifacts.set(toolCallId, artifact);\n }\n }\n const interactions = metadata[TOOL_INTERACTIONS_METADATA_KEY];\n if (toolInteractions && isRecord(interactions)) {\n for (const [toolCallId, value] of Object.entries(interactions)) {\n const log = readToolInteractionLog(value);\n if (log) toolInteractions.set(toolCallId, log);\n }\n }\n const approvalResponses = metadata[TOOL_APPROVAL_RESPONSES_METADATA_KEY];\n if (toolApprovalResponses && isRecord(approvalResponses)) {\n for (const [approvalId, value] of Object.entries(approvalResponses)) {\n if (!isRecord(value) || typeof value.approved !== \"boolean\") continue;\n toolApprovalResponses.set(approvalId, {\n approvalId,\n approved: value.approved,\n ...(typeof value.optionId === \"string\" && {\n optionId: value.optionId,\n }),\n ...(typeof value.text === \"string\" && { text: value.text }),\n ...(typeof value.reason === \"string\" && { reason: value.reason }),\n });\n }\n }\n const {\n [TOOL_ARTIFACTS_METADATA_KEY]: _,\n [TOOL_INTERACTIONS_METADATA_KEY]: __,\n [TOOL_APPROVAL_RESPONSES_METADATA_KEY]: ___,\n ...restMetadata\n } = metadata;\n const { metadata: _metadata, ...restMessage } = message;\n return (\n Object.keys(restMetadata).length === 0\n ? restMessage\n : { ...restMessage, metadata: restMetadata }\n ) as TMessage;\n};\n\nconst encodeContent = <TMessage>(\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n item: MessageFormatItem<TMessage>,\n) => JSON.stringify(storageFormatAdapter.encode(item));\n\nexport const useExternalHistory = <TMessage>(\n runtimeRef: RefObject<AssistantRuntime>,\n historyAdapter: ThreadHistoryAdapter | undefined,\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n onSetMessages: (messages: TMessage[]) => void,\n toolArtifacts?: Map<string, unknown>,\n onToolArtifactsRestored?: () => void,\n toolInteractions?: Map<string, Unstable_ToolInteractionLog>,\n onToolInteractionsRestored?: () => void,\n toolApprovalResponses?: Map<string, RespondToToolApprovalOptions>,\n onToolApprovalResponsesRestored?: () => void,\n) => {\n const loadedRef = useRef(false);\n const [itemEpoch, setItemEpoch] = useState(0);\n\n const aui = useAui();\n const optionalThreadListItem = useCallback(\n () => (aui.threadListItem.source ? aui.threadListItem : null),\n [aui],\n );\n\n const [hasLoaded, setHasLoaded] = useState(false);\n\n const historyIds = useRef(new Set<string>());\n const deferredTelemetryIds = useRef(new Set<string>());\n // `content` is a snapshot taken at write time rather than a re-encode of `source`, because a retained message object can be mutated in place by the runtime that produced it.\n const persistedInnerMessages = useRef(\n new Map<string, { source: TMessage; content: string }>(),\n );\n\n const onSetMessagesRef = useRef(onSetMessages);\n useEffect(() => {\n onSetMessagesRef.current = onSetMessages;\n });\n\n const formatAdapter = useMemo(() => {\n if (!historyAdapter) return undefined;\n if (!historyAdapter.withFormat) {\n throw new Error(\n \"useAISDKRuntime: ThreadHistoryAdapter is missing the required `withFormat` method.\",\n );\n }\n return historyAdapter.withFormat<TMessage, any>(storageFormatAdapter);\n }, [historyAdapter, storageFormatAdapter]);\n\n const isLoading = formatAdapter != null && !hasLoaded;\n\n useEffect(() => {\n if (!formatAdapter || loadedRef.current) return undefined;\n\n const loadHistory = async () => {\n try {\n const repo = await formatAdapter.load();\n toolArtifacts?.clear();\n toolInteractions?.clear();\n toolApprovalResponses?.clear();\n const restoredMessages =\n repo?.messages.map((item) => ({\n ...item,\n message: restoreToolData(\n item.message,\n toolArtifacts,\n toolInteractions,\n toolApprovalResponses,\n ),\n })) ?? [];\n onToolArtifactsRestored?.();\n onToolInteractionsRestored?.();\n onToolApprovalResponsesRestored?.();\n if (repo && restoredMessages.length > 0) {\n const restoredRepo = { ...repo, messages: restoredMessages };\n for (const [index, m] of repo.messages.entries()) {\n persistedInnerMessages.current.set(\n storageFormatAdapter.getId(m.message),\n {\n source: restoredMessages[index]!.message,\n content: encodeContent(storageFormatAdapter, m),\n },\n );\n }\n const converted = toExportedMessageRepository(\n toThreadMessages,\n restoredRepo,\n );\n runtimeRef.current.thread.import(converted);\n\n const tempRepo = new MessageRepository();\n tempRepo.import(converted);\n const messages = tempRepo.getMessages();\n\n onSetMessagesRef.current(\n messages.flatMap(getExternalStoreMessages<TMessage>),\n );\n\n historyIds.current = new Set();\n for (const m of converted.messages) {\n historyIds.current.add(m.message.id);\n if (isAwaitingToolApproval(m.message)) {\n deferredTelemetryIds.current.add(m.message.id);\n }\n }\n }\n } catch (error) {\n console.error(\"Failed to load message history:\", error);\n } finally {\n setHasLoaded(true);\n }\n };\n\n formatAdapter.pin?.();\n\n const remoteId = optionalThreadListItem()?.getState().remoteId;\n if (!remoteId) {\n // History loads asynchronously against the thread list item; without a\n // remote id there is nothing to await, so the flag settles here.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setHasLoaded(true);\n return aui.subscribe(() => {\n if (optionalThreadListItem()?.getState().remoteId) {\n setItemEpoch((n) => n + 1);\n }\n });\n }\n\n const threadState = runtimeRef.current.thread.getState();\n if (threadState.isRunning || threadState.messages.length > 0) {\n loadedRef.current = true;\n setHasLoaded(true);\n return undefined;\n }\n\n loadedRef.current = true;\n void loadHistory();\n return undefined;\n }, [\n formatAdapter,\n toThreadMessages,\n runtimeRef,\n optionalThreadListItem,\n aui,\n itemEpoch,\n storageFormatAdapter,\n toolArtifacts,\n onToolArtifactsRestored,\n toolInteractions,\n onToolInteractionsRestored,\n toolApprovalResponses,\n onToolApprovalResponsesRestored,\n ]);\n\n const runStartRef = useRef<number | null>(null);\n const persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const persistInFlightRef = useRef<Promise<void>>(Promise.resolve());\n const stepBoundariesRef = useRef<number[]>([]);\n const wasRunningRef = useRef(false);\n const toolCallCountRef = useRef(0);\n\n const persistToolInteractions = useCallback(\n (messageId: string) => {\n const persistence = persistInFlightRef.current.then(async () => {\n if (!formatAdapter?.update) return;\n const messages = runtimeRef.current.thread.getState().messages;\n const message = messages.find(\n (item) =>\n item.id === messageId ||\n getExternalStoreMessages<TMessage>(item).some(\n (innerMessage) =>\n storageFormatAdapter.getId(innerMessage) === messageId,\n ),\n );\n if (!message) return;\n\n const previousMessages = messages\n .slice(0, messages.indexOf(message))\n .flatMap(getExternalStoreMessages<TMessage>);\n let parentId = previousMessages.at(-1)\n ? storageFormatAdapter.getId(previousMessages.at(-1)!)\n : null;\n const storedToolArtifacts = collectToolArtifacts(\n message,\n toolArtifacts,\n );\n const storedToolInteractions = collectToolInteractions(\n message,\n toolInteractions,\n );\n const storedToolApprovalResponses = collectToolApprovalResponses(\n message,\n toolApprovalResponses,\n );\n\n for (const innerMessage of getExternalStoreMessages<TMessage>(\n message,\n )) {\n const item = {\n parentId,\n message: addToolData(\n innerMessage,\n storedToolArtifacts,\n storedToolInteractions,\n storedToolApprovalResponses,\n ),\n };\n const innerId = storageFormatAdapter.getId(item.message);\n const persisted = persistedInnerMessages.current.get(innerId);\n if (persisted) {\n const content = encodeContent(storageFormatAdapter, item);\n if (content === persisted.content) {\n persisted.source = item.message;\n } else {\n await formatAdapter.update(item, innerId);\n persistedInnerMessages.current.set(innerId, {\n source: item.message,\n content,\n });\n }\n }\n parentId = innerId;\n }\n });\n persistInFlightRef.current = persistence.catch(() => {});\n return persistence.catch((error) => {\n console.error(\"Failed to persist tool data:\", error);\n });\n },\n [\n formatAdapter,\n runtimeRef,\n storageFormatAdapter,\n toolArtifacts,\n toolInteractions,\n toolApprovalResponses,\n ],\n );\n\n useEffect(() => {\n if (!formatAdapter) return;\n const adapter = formatAdapter;\n\n const idleScheduledInnerIds = new Set<string>();\n const unpersistedInnerIds = (message: ThreadMessage) =>\n isTerminalMessage(message)\n ? getExternalStoreMessages<TMessage>(message)\n .map((innerMessage) => storageFormatAdapter.getId(innerMessage))\n .filter((innerId) => !persistedInnerMessages.current.has(innerId))\n : [];\n\n const unsubscribe = runtimeRef.current.thread.subscribe(() => {\n const threadState = runtimeRef.current.thread.getState();\n const { isRunning } = threadState;\n const wasRunning = wasRunningRef.current;\n wasRunningRef.current = isRunning;\n\n // Track step boundaries by content changes (more reliable than isRunning)\n if (runStartRef.current != null) {\n const lastMsg = threadState.messages.at(-1);\n if (lastMsg?.role === \"assistant\") {\n const currentToolCallCount = lastMsg.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n while (toolCallCountRef.current < currentToolCallCount) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n toolCallCountRef.current++;\n }\n }\n }\n\n if (isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n adapter.pin?.();\n }\n // Cancel any pending persist — isRunning went back to true\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n }\n return;\n }\n\n if (wasRunning) {\n // Record step boundary offset (synchronous for accuracy)\n if (runStartRef.current != null) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n }\n } else {\n const pending = threadState.messages.flatMap(unpersistedInnerIds);\n if (pending.every((innerId) => idleScheduledInnerIds.has(innerId)))\n return;\n for (const innerId of pending) idleScheduledInnerIds.add(innerId);\n }\n\n // Debounce: wait one macrotask so agentic step flickers are absorbed\n if (persistTimerRef.current) clearTimeout(persistTimerRef.current);\n persistTimerRef.current = setTimeout(() => {\n persistSettled(false);\n }, 0);\n });\n\n const initialThreadState = runtimeRef.current.thread.getState();\n wasRunningRef.current = initialThreadState.isRunning;\n if (initialThreadState.isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n adapter.pin?.();\n }\n } else if (initialThreadState.messages.length > 0) {\n persistSettled(true);\n }\n\n function persistSettled(ignoreRunning: boolean) {\n persistTimerRef.current = null;\n const latest = runtimeRef.current.thread.getState();\n if (!ignoreRunning && latest.isRunning) return;\n\n const boundaries = stepBoundariesRef.current;\n const durationMs = boundaries.length > 0 ? boundaries.at(-1) : undefined;\n\n // Fallback: if only 1 boundary but message has multiple steps, distribute evenly\n if (boundaries.length === 1 && durationMs != null) {\n const lastAssistant = latest.messages.findLast(\n (m) => m.role === \"assistant\",\n );\n if (lastAssistant) {\n const tcCount = lastAssistant.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n if (tcCount > 0) {\n const totalSteps = tcCount + 1;\n const stepDur = durationMs / totalSteps;\n boundaries.length = 0;\n for (let i = 0; i < totalSteps; i++) {\n boundaries.push(Math.round((i + 1) * stepDur));\n }\n }\n }\n }\n\n // Build per-step timestamps when there are multiple steps\n const stepTimestamps =\n boundaries.length > 1\n ? boundaries.map((endMs, i) => ({\n start_ms: i === 0 ? 0 : boundaries[i - 1]!,\n end_ms: endMs,\n }))\n : undefined;\n\n runStartRef.current = null;\n stepBoundariesRef.current = [];\n\n const telemetryOptions = {\n ...(durationMs != null ? { durationMs } : undefined),\n ...(stepTimestamps != null ? { stepTimestamps } : undefined),\n };\n\n persistInFlightRef.current = persistInFlightRef.current\n .then(async () => {\n const { messages } = latest;\n let lastInnerMessageId: string | null = null;\n\n const getLastInnerId = (msgs: TMessage[]): string | null =>\n msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)!) : null;\n\n const toBatchItems = (\n msgs: TMessage[],\n toolArtifacts: Record<string, unknown> | undefined,\n toolInteractions:\n | Record<string, Unstable_ToolInteractionLog>\n | undefined,\n toolApprovalResponses:\n | Record<string, StoredToolApprovalResponse>\n | undefined,\n ) =>\n msgs.map((msg, idx) => ({\n parentId:\n idx === 0\n ? lastInnerMessageId\n : storageFormatAdapter.getId(msgs[idx - 1]!),\n message: addToolData(\n msg,\n toolArtifacts,\n toolInteractions,\n toolApprovalResponses,\n ),\n }));\n\n for (const message of messages) {\n const innerMessages = getExternalStoreMessages<TMessage>(message);\n\n const isTerminal = isTerminalMessage(message);\n const isAwaitingToolCalls = isAwaitingToolApproval(message);\n // A paused message's later content can only reach storage via update, so it is persisted early only when the adapter supports update.\n const isReady =\n isTerminal ||\n (isAwaitingToolCalls && adapter.update !== undefined);\n\n if (!isReady) {\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n\n if (!historyIds.current.has(message.id)) {\n historyIds.current.add(message.id);\n deferredTelemetryIds.current.add(message.id);\n }\n\n const batchItems = toBatchItems(\n innerMessages,\n collectToolArtifacts(message, toolArtifacts),\n collectToolInteractions(message, toolInteractions),\n collectToolApprovalResponses(message, toolApprovalResponses),\n );\n for (const item of batchItems) {\n const innerId = storageFormatAdapter.getId(item.message);\n const persisted = persistedInnerMessages.current.get(innerId);\n if (!persisted) {\n await adapter.append(item);\n persistedInnerMessages.current.set(innerId, {\n source: item.message,\n content: encodeContent(storageFormatAdapter, item),\n });\n } else if (\n persisted.source !== item.message &&\n durationMs !== undefined &&\n adapter.update\n ) {\n const content = encodeContent(storageFormatAdapter, item);\n if (content === persisted.content) {\n persisted.source = item.message;\n } else {\n try {\n await adapter.update(item, innerId);\n persistedInnerMessages.current.set(innerId, {\n source: item.message,\n content,\n });\n } catch {\n // A failed update leaves the stale baseline behind so the next run stop retries it.\n }\n }\n }\n }\n\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n\n if (deferredTelemetryIds.current.has(message.id) && isTerminal) {\n deferredTelemetryIds.current.delete(message.id);\n adapter.reportTelemetry?.(\n batchItems.map((item, index) => ({\n ...item,\n message: innerMessages[index]!,\n })),\n {\n ...telemetryOptions,\n message,\n },\n );\n }\n }\n })\n .catch((error) => {\n console.error(\"Failed to persist message history:\", error);\n });\n }\n\n return () => {\n unsubscribe();\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n persistSettled(false);\n }\n };\n }, [\n formatAdapter,\n storageFormatAdapter,\n runtimeRef,\n toolArtifacts,\n toolInteractions,\n toolApprovalResponses,\n ]);\n\n const deleteMessage = useCallback(\n async (messageId: string) => {\n const deleteMessages = formatAdapter?.delete?.bind(formatAdapter);\n if (!deleteMessages) return;\n\n const messages = runtimeRef.current.thread.getState().messages;\n const messageIndex = messages.findIndex((m) => m.id === messageId);\n if (messageIndex === -1) return;\n\n const previousInnerMessages = messages\n .slice(0, messageIndex)\n .flatMap(getExternalStoreMessages<TMessage>);\n let parentId = previousInnerMessages.at(-1)\n ? storageFormatAdapter.getId(previousInnerMessages.at(-1)!)\n : null;\n const itemsToDelete = getExternalStoreMessages<TMessage>(\n messages[messageIndex]!,\n ).map((message) => {\n const item = { parentId, message };\n parentId = storageFormatAdapter.getId(message);\n return item;\n });\n\n const deletion = persistInFlightRef.current.then(async () => {\n await deleteMessages(itemsToDelete);\n\n historyIds.current.delete(messageId);\n deferredTelemetryIds.current.delete(messageId);\n for (const item of itemsToDelete) {\n persistedInnerMessages.current.delete(\n storageFormatAdapter.getId(item.message),\n );\n }\n });\n\n persistInFlightRef.current = deletion.catch(() => {});\n await deletion;\n },\n [formatAdapter, runtimeRef, storageFormatAdapter],\n );\n\n return {\n isLoading,\n deleteMessage,\n persistToolInteractions,\n persistToolApprovalResponses: persistToolInteractions,\n };\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantChatTransport.d.ts","sourceRoot":"","sources":["../../src/transport/AssistantChatTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,oBAAoB,EACpB,KAAK,4BAA4B,EACjC,KAAK,SAAS,EACf,MAAM,IAAI,CAAC;AAEZ,OAAO,EAEL,KAAK,6BAA6B,EACnC,uBAAoB;AAErB,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAC5C,qBAAqB,EACrB,YAAY,CACb,CAAC;
|
|
1
|
+
{"version":3,"file":"AssistantChatTransport.d.ts","sourceRoot":"","sources":["../../src/transport/AssistantChatTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,oBAAoB,EACpB,KAAK,4BAA4B,EACjC,KAAK,SAAS,EACf,MAAM,IAAI,CAAC;AAEZ,OAAO,EAEL,KAAK,6BAA6B,EACnC,uBAAoB;AAErB,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAC5C,qBAAqB,EACrB,YAAY,CACb,CAAC;AAYF,MAAM,MAAM,iCAAiC,CAAC,UAAU,SAAS,SAAS,IACxE,4BAA4B,CAAC,UAAU,CAAC,GAAG;IACzC,SAAS,CAAC,EAAE,6BAA6B,CAAC;CAC3C,CAAC;AAEJ,qBAAa,sBAAsB,CACjC,UAAU,SAAS,SAAS,CAC5B,SAAQ,oBAAoB,CAAC,UAAU,CAAC;IACxC,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,iBAAiB,CAEX;IACd,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4C;IAEtE,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAEzB;IAEd,6DAA6D;IACtD,gBAAgB,IAAI,sBAAsB,CAAC,UAAU,CAAC;gBAOjD,WAAW,CAAC,EAAE,iCAAiC,CAAC,UAAU,CAAC;IA0DvE,UAAU,CAAC,OAAO,EAAE,gBAAgB;IAIpC,mBAAmB,IAAI,6BAA6B,GAAG,SAAS;IAIhE,+BAA+B,CAC7B,MAAM,EAAE,MAAM,2BAA2B,GAAG,SAAS;CAIxD"}
|
|
@@ -5,6 +5,7 @@ import { toToolsJSONSchema } from "assistant-stream";
|
|
|
5
5
|
const FINISH_MARKER = "\"type\":\"finish\"";
|
|
6
6
|
const FINISH_BUFFER_LIMIT = 4096;
|
|
7
7
|
const RESUMABLE_THREAD_ID_HEADER = "x-assistant-ui-resumable-thread-id";
|
|
8
|
+
const RESUMABLE_RECONNECT_ID_HEADER = "x-assistant-ui-resumable-reconnect-id";
|
|
8
9
|
const NULL_BODY_STATUSES = /* @__PURE__ */ new Set([
|
|
9
10
|
101,
|
|
10
11
|
204,
|
|
@@ -81,13 +82,18 @@ function wrapFetchWithResumable(resumable, userFetch) {
|
|
|
81
82
|
return async (input, init) => {
|
|
82
83
|
const headers = new Headers(init?.headers);
|
|
83
84
|
const threadId = headers.get(RESUMABLE_THREAD_ID_HEADER) ?? void 0;
|
|
85
|
+
const reconnectingStreamId = headers.get(RESUMABLE_RECONNECT_ID_HEADER);
|
|
86
|
+
const checkpointAtRequest = reconnectingStreamId ?? resumable.storage.getStreamId(threadId);
|
|
84
87
|
headers.delete(RESUMABLE_THREAD_ID_HEADER);
|
|
88
|
+
headers.delete(RESUMABLE_RECONNECT_ID_HEADER);
|
|
85
89
|
const res = await baseFetch(input, {
|
|
86
90
|
...init,
|
|
87
91
|
headers
|
|
88
92
|
});
|
|
89
93
|
const id = res.headers.get(RESUMABLE_STREAM_ID_HEADER);
|
|
90
|
-
|
|
94
|
+
const ownsCheckpoint = !reconnectingStreamId || resumable.storage.getStreamId(threadId) === reconnectingStreamId;
|
|
95
|
+
if (id && ownsCheckpoint) resumable.storage.setStreamId(id, threadId);
|
|
96
|
+
if ((res.status === 204 || res.status === 404) && reconnectingStreamId && resumable.storage.getStreamId(threadId) === reconnectingStreamId) resumable.storage.clear(threadId);
|
|
91
97
|
if (!res.body || NULL_BODY_STATUSES.has(res.status)) return res;
|
|
92
98
|
const detectFinish = resumable.isFinishEvent ?? defaultIsFinishEvent;
|
|
93
99
|
const decoder = new TextDecoder();
|
|
@@ -96,7 +102,7 @@ function wrapFetchWithResumable(resumable, userFetch) {
|
|
|
96
102
|
controller.enqueue(chunk);
|
|
97
103
|
accumulator += decoder.decode(chunk, { stream: true });
|
|
98
104
|
if (detectFinish(chunk, accumulator)) {
|
|
99
|
-
if (
|
|
105
|
+
if (ownsCheckpoint && resumable.storage.getStreamId(threadId) === (id ?? checkpointAtRequest)) resumable.storage.clear(threadId);
|
|
100
106
|
accumulator = "";
|
|
101
107
|
} else if (accumulator.length > FINISH_BUFFER_LIMIT) accumulator = accumulator.slice(-1024);
|
|
102
108
|
} });
|
|
@@ -121,6 +127,7 @@ function wrapPrepareReconnect(resumable, userPrepareReconnect) {
|
|
|
121
127
|
});
|
|
122
128
|
const headers = new Headers(userPrepared?.headers ?? options.headers);
|
|
123
129
|
headers.set(RESUMABLE_THREAD_ID_HEADER, options.id);
|
|
130
|
+
headers.set(RESUMABLE_RECONNECT_ID_HEADER, streamId);
|
|
124
131
|
return {
|
|
125
132
|
...userPrepared,
|
|
126
133
|
headers,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantChatTransport.js","names":["AssistantRuntime","ThreadListItemRuntime","DefaultChatTransport","HttpChatTransportInitOptions","UIMessage","toToolsJSONSchema","RESUMABLE_STREAM_ID_HEADER","AssistantChatResumableOptions","InitializableThreadListItem","Pick","FINISH_MARKER","FINISH_BUFFER_LIMIT","FINISH_BUFFER_TAIL","RESUMABLE_THREAD_ID_HEADER","NULL_BODY_STATUSES","Set","AssistantChatTransportInitOptions","UI_MESSAGE","resumable","AssistantChatTransport","runtime","getThreadListItem","__internal_initOptions","__internal_clone","Constructor","constructor","initOptions","rest","userFetch","fetch","userPrepareReconnect","prepareReconnectToStreamRequest","wrapFetchWithResumable","wrapPrepareReconnect","prepareSendMessagesRequest","options","threadId","id","context","thread","getModelContext","threadListItem","threads","mainItem","initialize","remoteId","optionsEx","body","callSettings","system","config","tools","preparedRequest","headers","Headers","undefined","set","messages","trigger","messageId","metadata","requestMetadata","setRuntime","getResumableAdapter","__internal_setGetThreadListItem","getter","NonNullable","baseFetch","globalThis","input","init","RequestInfo","URL","bind","get","delete","res","storage","setStreamId","has","status","detectFinish","isFinishEvent","defaultIsFinishEvent","decoder","TextDecoder","accumulator","tap","TransformStream","Uint8Array","transform","chunk","controller","enqueue","decode","stream","getStreamId","clear","length","slice","Response","pipeThrough","statusText","_chunk","includes","streamId","Error","api","resumeApi","userPrepared"],"sources":["../../src/transport/AssistantChatTransport.ts"],"sourcesContent":["import type {\n AssistantRuntime,\n ThreadListItemRuntime,\n} from \"@assistant-ui/core\";\nimport {\n DefaultChatTransport,\n type HttpChatTransportInitOptions,\n type UIMessage,\n} from \"ai\";\nimport { toToolsJSONSchema } from \"assistant-stream\";\nimport {\n RESUMABLE_STREAM_ID_HEADER,\n type AssistantChatResumableOptions,\n} from \"./resumable\";\n\nexport type InitializableThreadListItem = Pick<\n ThreadListItemRuntime,\n \"initialize\"\n>;\n\nconst FINISH_MARKER = '\"type\":\"finish\"';\nconst FINISH_BUFFER_LIMIT = 4096;\nconst FINISH_BUFFER_TAIL = 1024;\nconst RESUMABLE_THREAD_ID_HEADER = \"x-assistant-ui-resumable-thread-id\";\n\n// 101/204/205/304 are null-body statuses per the fetch spec: `new Response(body, { status })`\n// throws for them, and WebKit returns a non-null empty body, so the body check alone does not guard it.\nconst NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);\n\nexport type AssistantChatTransportInitOptions<UI_MESSAGE extends UIMessage> =\n HttpChatTransportInitOptions<UI_MESSAGE> & {\n resumable?: AssistantChatResumableOptions;\n };\n\nexport class AssistantChatTransport<\n UI_MESSAGE extends UIMessage,\n> extends DefaultChatTransport<UI_MESSAGE> {\n private runtime: AssistantRuntime | undefined;\n private getThreadListItem:\n | (() => InitializableThreadListItem | undefined)\n | undefined;\n private readonly resumable: AssistantChatResumableOptions | undefined;\n\n private readonly __internal_initOptions:\n | AssistantChatTransportInitOptions<UI_MESSAGE>\n | undefined;\n\n /** Constructs an unwired copy with the same init options. */\n public __internal_clone(): AssistantChatTransport<UI_MESSAGE> {\n const Constructor = this.constructor as new (\n initOptions?: AssistantChatTransportInitOptions<UI_MESSAGE>,\n ) => AssistantChatTransport<UI_MESSAGE>;\n return new Constructor(this.__internal_initOptions);\n }\n\n constructor(initOptions?: AssistantChatTransportInitOptions<UI_MESSAGE>) {\n const { resumable, ...rest } = initOptions ?? {};\n const userFetch = rest.fetch;\n const userPrepareReconnect = rest.prepareReconnectToStreamRequest;\n\n super({\n ...rest,\n ...(resumable && {\n fetch: wrapFetchWithResumable(resumable, userFetch),\n prepareReconnectToStreamRequest: wrapPrepareReconnect(\n resumable,\n userPrepareReconnect,\n ),\n }),\n prepareSendMessagesRequest: async (options) => {\n const threadId = options.id;\n const context = this.runtime?.thread.getModelContext();\n const threadListItem =\n this.getThreadListItem?.() ?? this.runtime?.threads.mainItem;\n const id = (await threadListItem?.initialize())?.remoteId ?? options.id;\n\n const optionsEx = {\n ...options,\n id,\n body: {\n callSettings: context?.callSettings,\n system: context?.system,\n config: context?.config,\n tools: toToolsJSONSchema(context?.tools ?? {}),\n ...options?.body,\n },\n };\n const preparedRequest =\n await rest.prepareSendMessagesRequest?.(optionsEx);\n const headers = resumable\n ? new Headers(preparedRequest?.headers ?? options.headers)\n : undefined;\n headers?.set(RESUMABLE_THREAD_ID_HEADER, threadId);\n\n return {\n ...preparedRequest,\n ...(headers && { headers }),\n body: preparedRequest?.body ?? {\n ...optionsEx.body,\n id,\n messages: options.messages,\n trigger: options.trigger,\n messageId: options.messageId,\n metadata: options.requestMetadata,\n },\n };\n },\n });\n\n this.resumable = resumable;\n this.__internal_initOptions = initOptions;\n }\n\n setRuntime(runtime: AssistantRuntime) {\n this.runtime = runtime;\n }\n\n getResumableAdapter(): AssistantChatResumableOptions | undefined {\n return this.resumable;\n }\n\n __internal_setGetThreadListItem(\n getter: () => InitializableThreadListItem | undefined,\n ) {\n this.getThreadListItem = getter;\n }\n}\n\nfunction wrapFetchWithResumable(\n resumable: AssistantChatResumableOptions,\n userFetch: HttpChatTransportInitOptions<UIMessage>[\"fetch\"],\n): NonNullable<HttpChatTransportInitOptions<UIMessage>[\"fetch\"]> {\n const baseFetch: typeof globalThis.fetch = userFetch\n ? (input, init) => userFetch(input as RequestInfo | URL, init)\n : globalThis.fetch.bind(globalThis);\n\n return async (input, init) => {\n const headers = new Headers(init?.headers);\n const threadId = headers.get(RESUMABLE_THREAD_ID_HEADER) ?? undefined;\n headers.delete(RESUMABLE_THREAD_ID_HEADER);\n const res = await baseFetch(input, { ...init, headers });\n const id = res.headers.get(RESUMABLE_STREAM_ID_HEADER);\n if (id) resumable.storage.setStreamId(id, threadId);\n if (!res.body || NULL_BODY_STATUSES.has(res.status)) return res;\n\n const detectFinish = resumable.isFinishEvent ?? defaultIsFinishEvent;\n // a single decoder is required so multi-byte sequences split across\n // chunks buffer via stream: true rather than getting dropped.\n const decoder = new TextDecoder();\n let accumulator = \"\";\n const tap = new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n controller.enqueue(chunk);\n accumulator += decoder.decode(chunk, { stream: true });\n if (detectFinish(chunk, accumulator)) {\n if (!id || resumable.storage.getStreamId(threadId) === id) {\n resumable.storage.clear(threadId);\n }\n accumulator = \"\";\n } else if (accumulator.length > FINISH_BUFFER_LIMIT) {\n accumulator = accumulator.slice(-FINISH_BUFFER_TAIL);\n }\n },\n });\n\n return new Response(res.body.pipeThrough(tap), {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n };\n}\n\nfunction defaultIsFinishEvent(_chunk: Uint8Array, accumulator: string) {\n return accumulator.includes(FINISH_MARKER);\n}\n\nfunction wrapPrepareReconnect(\n resumable: AssistantChatResumableOptions,\n userPrepareReconnect: HttpChatTransportInitOptions<UIMessage>[\"prepareReconnectToStreamRequest\"],\n): NonNullable<\n HttpChatTransportInitOptions<UIMessage>[\"prepareReconnectToStreamRequest\"]\n> {\n return async (options) => {\n const streamId = resumable.storage.getStreamId(options.id);\n if (!streamId) {\n throw new Error(\n \"AssistantChatTransport: no resumable stream id available; nothing to resume\",\n );\n }\n const api =\n typeof resumable.resumeApi === \"function\"\n ? resumable.resumeApi(streamId)\n : resumable.resumeApi;\n const userPrepared = await userPrepareReconnect?.({ ...options, api });\n const headers = new Headers(userPrepared?.headers ?? options.headers);\n headers.set(RESUMABLE_THREAD_ID_HEADER, options.id);\n return {\n ...userPrepared,\n headers,\n api: userPrepared?.api ?? api,\n };\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AssistantChatTransport.js","names":["AssistantRuntime","ThreadListItemRuntime","DefaultChatTransport","HttpChatTransportInitOptions","UIMessage","toToolsJSONSchema","RESUMABLE_STREAM_ID_HEADER","AssistantChatResumableOptions","InitializableThreadListItem","Pick","FINISH_MARKER","FINISH_BUFFER_LIMIT","FINISH_BUFFER_TAIL","RESUMABLE_THREAD_ID_HEADER","RESUMABLE_RECONNECT_ID_HEADER","NULL_BODY_STATUSES","Set","AssistantChatTransportInitOptions","UI_MESSAGE","resumable","AssistantChatTransport","runtime","getThreadListItem","__internal_initOptions","__internal_clone","Constructor","constructor","initOptions","rest","userFetch","fetch","userPrepareReconnect","prepareReconnectToStreamRequest","wrapFetchWithResumable","wrapPrepareReconnect","prepareSendMessagesRequest","options","threadId","id","context","thread","getModelContext","threadListItem","threads","mainItem","initialize","remoteId","optionsEx","body","callSettings","system","config","tools","preparedRequest","headers","Headers","undefined","set","messages","trigger","messageId","metadata","requestMetadata","setRuntime","getResumableAdapter","__internal_setGetThreadListItem","getter","NonNullable","baseFetch","globalThis","input","init","RequestInfo","URL","bind","get","reconnectingStreamId","checkpointAtRequest","storage","getStreamId","delete","res","ownsCheckpoint","setStreamId","status","clear","has","detectFinish","isFinishEvent","defaultIsFinishEvent","decoder","TextDecoder","accumulator","tap","TransformStream","Uint8Array","transform","chunk","controller","enqueue","decode","stream","length","slice","Response","pipeThrough","statusText","_chunk","includes","streamId","Error","api","resumeApi","userPrepared"],"sources":["../../src/transport/AssistantChatTransport.ts"],"sourcesContent":["import type {\n AssistantRuntime,\n ThreadListItemRuntime,\n} from \"@assistant-ui/core\";\nimport {\n DefaultChatTransport,\n type HttpChatTransportInitOptions,\n type UIMessage,\n} from \"ai\";\nimport { toToolsJSONSchema } from \"assistant-stream\";\nimport {\n RESUMABLE_STREAM_ID_HEADER,\n type AssistantChatResumableOptions,\n} from \"./resumable\";\n\nexport type InitializableThreadListItem = Pick<\n ThreadListItemRuntime,\n \"initialize\"\n>;\n\nconst FINISH_MARKER = '\"type\":\"finish\"';\nconst FINISH_BUFFER_LIMIT = 4096;\nconst FINISH_BUFFER_TAIL = 1024;\nconst RESUMABLE_THREAD_ID_HEADER = \"x-assistant-ui-resumable-thread-id\";\nconst RESUMABLE_RECONNECT_ID_HEADER = \"x-assistant-ui-resumable-reconnect-id\";\n\n// 101/204/205/304 are null-body statuses per the fetch spec: `new Response(body, { status })`\n// throws for them, and WebKit returns a non-null empty body, so the body check alone does not guard it.\nconst NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);\n\nexport type AssistantChatTransportInitOptions<UI_MESSAGE extends UIMessage> =\n HttpChatTransportInitOptions<UI_MESSAGE> & {\n resumable?: AssistantChatResumableOptions;\n };\n\nexport class AssistantChatTransport<\n UI_MESSAGE extends UIMessage,\n> extends DefaultChatTransport<UI_MESSAGE> {\n private runtime: AssistantRuntime | undefined;\n private getThreadListItem:\n | (() => InitializableThreadListItem | undefined)\n | undefined;\n private readonly resumable: AssistantChatResumableOptions | undefined;\n\n private readonly __internal_initOptions:\n | AssistantChatTransportInitOptions<UI_MESSAGE>\n | undefined;\n\n /** Constructs an unwired copy with the same init options. */\n public __internal_clone(): AssistantChatTransport<UI_MESSAGE> {\n const Constructor = this.constructor as new (\n initOptions?: AssistantChatTransportInitOptions<UI_MESSAGE>,\n ) => AssistantChatTransport<UI_MESSAGE>;\n return new Constructor(this.__internal_initOptions);\n }\n\n constructor(initOptions?: AssistantChatTransportInitOptions<UI_MESSAGE>) {\n const { resumable, ...rest } = initOptions ?? {};\n const userFetch = rest.fetch;\n const userPrepareReconnect = rest.prepareReconnectToStreamRequest;\n\n super({\n ...rest,\n ...(resumable && {\n fetch: wrapFetchWithResumable(resumable, userFetch),\n prepareReconnectToStreamRequest: wrapPrepareReconnect(\n resumable,\n userPrepareReconnect,\n ),\n }),\n prepareSendMessagesRequest: async (options) => {\n const threadId = options.id;\n const context = this.runtime?.thread.getModelContext();\n const threadListItem =\n this.getThreadListItem?.() ?? this.runtime?.threads.mainItem;\n const id = (await threadListItem?.initialize())?.remoteId ?? options.id;\n\n const optionsEx = {\n ...options,\n id,\n body: {\n callSettings: context?.callSettings,\n system: context?.system,\n config: context?.config,\n tools: toToolsJSONSchema(context?.tools ?? {}),\n ...options?.body,\n },\n };\n const preparedRequest =\n await rest.prepareSendMessagesRequest?.(optionsEx);\n const headers = resumable\n ? new Headers(preparedRequest?.headers ?? options.headers)\n : undefined;\n headers?.set(RESUMABLE_THREAD_ID_HEADER, threadId);\n\n return {\n ...preparedRequest,\n ...(headers && { headers }),\n body: preparedRequest?.body ?? {\n ...optionsEx.body,\n id,\n messages: options.messages,\n trigger: options.trigger,\n messageId: options.messageId,\n metadata: options.requestMetadata,\n },\n };\n },\n });\n\n this.resumable = resumable;\n this.__internal_initOptions = initOptions;\n }\n\n setRuntime(runtime: AssistantRuntime) {\n this.runtime = runtime;\n }\n\n getResumableAdapter(): AssistantChatResumableOptions | undefined {\n return this.resumable;\n }\n\n __internal_setGetThreadListItem(\n getter: () => InitializableThreadListItem | undefined,\n ) {\n this.getThreadListItem = getter;\n }\n}\n\nfunction wrapFetchWithResumable(\n resumable: AssistantChatResumableOptions,\n userFetch: HttpChatTransportInitOptions<UIMessage>[\"fetch\"],\n): NonNullable<HttpChatTransportInitOptions<UIMessage>[\"fetch\"]> {\n const baseFetch: typeof globalThis.fetch = userFetch\n ? (input, init) => userFetch(input as RequestInfo | URL, init)\n : globalThis.fetch.bind(globalThis);\n\n return async (input, init) => {\n const headers = new Headers(init?.headers);\n const threadId = headers.get(RESUMABLE_THREAD_ID_HEADER) ?? undefined;\n const reconnectingStreamId = headers.get(RESUMABLE_RECONNECT_ID_HEADER);\n const checkpointAtRequest =\n reconnectingStreamId ?? resumable.storage.getStreamId(threadId);\n headers.delete(RESUMABLE_THREAD_ID_HEADER);\n headers.delete(RESUMABLE_RECONNECT_ID_HEADER);\n const res = await baseFetch(input, { ...init, headers });\n const id = res.headers.get(RESUMABLE_STREAM_ID_HEADER);\n const ownsCheckpoint =\n !reconnectingStreamId ||\n resumable.storage.getStreamId(threadId) === reconnectingStreamId;\n if (id && ownsCheckpoint) resumable.storage.setStreamId(id, threadId);\n if (\n (res.status === 204 || res.status === 404) &&\n reconnectingStreamId &&\n resumable.storage.getStreamId(threadId) === reconnectingStreamId\n ) {\n resumable.storage.clear(threadId);\n }\n if (!res.body || NULL_BODY_STATUSES.has(res.status)) return res;\n\n const detectFinish = resumable.isFinishEvent ?? defaultIsFinishEvent;\n // a single decoder is required so multi-byte sequences split across\n // chunks buffer via stream: true rather than getting dropped.\n const decoder = new TextDecoder();\n let accumulator = \"\";\n const tap = new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n controller.enqueue(chunk);\n accumulator += decoder.decode(chunk, { stream: true });\n if (detectFinish(chunk, accumulator)) {\n if (\n ownsCheckpoint &&\n resumable.storage.getStreamId(threadId) ===\n (id ?? checkpointAtRequest)\n ) {\n resumable.storage.clear(threadId);\n }\n accumulator = \"\";\n } else if (accumulator.length > FINISH_BUFFER_LIMIT) {\n accumulator = accumulator.slice(-FINISH_BUFFER_TAIL);\n }\n },\n });\n\n return new Response(res.body.pipeThrough(tap), {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n };\n}\n\nfunction defaultIsFinishEvent(_chunk: Uint8Array, accumulator: string) {\n return accumulator.includes(FINISH_MARKER);\n}\n\nfunction wrapPrepareReconnect(\n resumable: AssistantChatResumableOptions,\n userPrepareReconnect: HttpChatTransportInitOptions<UIMessage>[\"prepareReconnectToStreamRequest\"],\n): NonNullable<\n HttpChatTransportInitOptions<UIMessage>[\"prepareReconnectToStreamRequest\"]\n> {\n return async (options) => {\n const streamId = resumable.storage.getStreamId(options.id);\n if (!streamId) {\n throw new Error(\n \"AssistantChatTransport: no resumable stream id available; nothing to resume\",\n );\n }\n const api =\n typeof resumable.resumeApi === \"function\"\n ? resumable.resumeApi(streamId)\n : resumable.resumeApi;\n const userPrepared = await userPrepareReconnect?.({ ...options, api });\n const headers = new Headers(userPrepared?.headers ?? options.headers);\n headers.set(RESUMABLE_THREAD_ID_HEADER, options.id);\n headers.set(RESUMABLE_RECONNECT_ID_HEADER, streamId);\n return {\n ...userPrepared,\n headers,\n api: userPrepared?.api ?? api,\n };\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/ai-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "Vercel AI SDK adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-sdk",
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@ai-sdk/mcp": "^2.0.49",
|
|
50
50
|
"@ai-sdk/react": "^4.0.104",
|
|
51
|
-
"@assistant-ui/core": "^0.3.
|
|
52
|
-
"@assistant-ui/store": "^0.3.
|
|
53
|
-
"@assistant-ui/tap": "^0.9.
|
|
54
|
-
"assistant-cloud": "^0.2.
|
|
55
|
-
"assistant-stream": "^0.3.
|
|
51
|
+
"@assistant-ui/core": "^0.3.22",
|
|
52
|
+
"@assistant-ui/store": "^0.3.16",
|
|
53
|
+
"@assistant-ui/tap": "^0.9.20",
|
|
54
|
+
"assistant-cloud": "^0.2.4",
|
|
55
|
+
"assistant-stream": "^0.3.46"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"@types/react": "*",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@assistant-ui/x-buildutils": "0.0.29",
|
|
72
|
+
"@assistant-ui/x-react18": "0.0.0",
|
|
72
73
|
"@testing-library/react": "^16.3.3",
|
|
73
74
|
"@types/json-schema": "^7.0.15",
|
|
74
75
|
"@types/react": "^19.3.0",
|
|
@@ -95,8 +96,10 @@
|
|
|
95
96
|
"build": "aui-build",
|
|
96
97
|
"test": "vitest run",
|
|
97
98
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
|
|
99
|
+
"test:peer-react18": "vitest run --config vitest.peer-react18.config.ts",
|
|
98
100
|
"test:react-compiler": "vitest run --config vitest.react-compiler.config.ts",
|
|
99
101
|
"test:standalone": "vitest run --project standalone",
|
|
100
|
-
"test:watch": "vitest"
|
|
102
|
+
"test:watch": "vitest",
|
|
103
|
+
"test:types:peer-react18": "aui-react18-types"
|
|
101
104
|
}
|
|
102
105
|
}
|
|
@@ -970,6 +970,69 @@ describe("AISDKMessageConverter", () => {
|
|
|
970
970
|
expect(toolCall?.argsText).toBe('{"city":"NYC');
|
|
971
971
|
});
|
|
972
972
|
|
|
973
|
+
it("strips exactly the trailing run of closing characters", () => {
|
|
974
|
+
const convertArgsText = (text: string) => {
|
|
975
|
+
stableStringifySpy.mockReturnValueOnce(text);
|
|
976
|
+
const converted = AISDKMessageConverter.toThreadMessages([
|
|
977
|
+
{
|
|
978
|
+
id: "a1",
|
|
979
|
+
role: "assistant",
|
|
980
|
+
parts: [
|
|
981
|
+
{
|
|
982
|
+
type: "tool-weather",
|
|
983
|
+
toolCallId: "tc-1",
|
|
984
|
+
state: "input-streaming",
|
|
985
|
+
input: {},
|
|
986
|
+
},
|
|
987
|
+
],
|
|
988
|
+
},
|
|
989
|
+
] as any);
|
|
990
|
+
return (converted[0]?.content[0] as any)?.argsText;
|
|
991
|
+
};
|
|
992
|
+
const alphabet = ["}", "]", '"', "a", "\\", "{", "[", "\n", " ", "😀"];
|
|
993
|
+
let seed = 7;
|
|
994
|
+
const next = () => (seed = (seed * 48271) % 0x7fffffff);
|
|
995
|
+
const texts = ["", "}", '"]}', '{"a":"x"}', '{"a":"}}x"}', "\uD800}"];
|
|
996
|
+
for (let i = 0; i < 300; i++) {
|
|
997
|
+
texts.push(
|
|
998
|
+
Array.from(
|
|
999
|
+
{ length: next() % 12 },
|
|
1000
|
+
() => alphabet[next() % alphabet.length],
|
|
1001
|
+
).join(""),
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
for (const text of texts) {
|
|
1006
|
+
expect(convertArgsText(text)).toBe(text.replace(/[}\]"]+$/, ""));
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
it(
|
|
1011
|
+
"strips a long run of closing characters inside streaming input",
|
|
1012
|
+
{ timeout: 5_000 },
|
|
1013
|
+
() => {
|
|
1014
|
+
const run = "}".repeat(200_000);
|
|
1015
|
+
const converted = AISDKMessageConverter.toThreadMessages([
|
|
1016
|
+
{
|
|
1017
|
+
id: "a1",
|
|
1018
|
+
role: "assistant",
|
|
1019
|
+
parts: [
|
|
1020
|
+
{
|
|
1021
|
+
type: "tool-weather",
|
|
1022
|
+
toolCallId: "tc-1",
|
|
1023
|
+
state: "input-streaming",
|
|
1024
|
+
input: { code: `${run}x` },
|
|
1025
|
+
},
|
|
1026
|
+
],
|
|
1027
|
+
},
|
|
1028
|
+
] as any);
|
|
1029
|
+
|
|
1030
|
+
expect((converted[0]?.content[0] as any)?.argsText).toBe(
|
|
1031
|
+
`{"code":"${run}x`,
|
|
1032
|
+
);
|
|
1033
|
+
},
|
|
1034
|
+
);
|
|
1035
|
+
|
|
973
1036
|
it("attaches partial-JSON meta marking the trailing streaming field", () => {
|
|
974
1037
|
const converted = AISDKMessageConverter.toThreadMessages([
|
|
975
1038
|
{
|
|
@@ -76,12 +76,16 @@ export type AISDKMessageConverterMetadata =
|
|
|
76
76
|
toolInteractions?: ReadonlyMap<string, Unstable_ToolInteractionLog>;
|
|
77
77
|
supportsRichToolApprovalResponses?: boolean;
|
|
78
78
|
toolApprovalResponses?: ReadonlyMap<string, RespondToToolApprovalOptions>;
|
|
79
|
+
cancelledToolApprovalIds?: ReadonlySet<string>;
|
|
80
|
+
cancelledStatusMessageIds?: ReadonlySet<string>;
|
|
79
81
|
/** Id of the currently-streaming message, flagged optimistic (#4037). */
|
|
80
82
|
optimisticMessageId?: string | undefined;
|
|
81
83
|
};
|
|
82
84
|
|
|
83
85
|
function stripClosingDelimiters(json: string): string {
|
|
84
|
-
|
|
86
|
+
let end = json.length;
|
|
87
|
+
while (end > 0 && '}]"'.includes(json[end - 1]!)) end--;
|
|
88
|
+
return json.slice(0, end);
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
const MCP_APP_METADATA_CACHE_MAX = 100;
|
|
@@ -258,15 +262,23 @@ function getToolApprovalAndInterrupt(
|
|
|
258
262
|
toolApprovalResponses:
|
|
259
263
|
| ReadonlyMap<string, RespondToToolApprovalOptions>
|
|
260
264
|
| undefined,
|
|
265
|
+
cancelledToolApprovalIds: ReadonlySet<string> | undefined,
|
|
261
266
|
): {
|
|
262
267
|
approval?: NonNullable<ToolCallMessagePart["approval"]>;
|
|
263
268
|
interrupt?: NonNullable<ToolCallMessagePart["interrupt"]>;
|
|
264
269
|
} {
|
|
265
270
|
if (part.approval) {
|
|
266
|
-
const
|
|
271
|
+
const rawApproval = {
|
|
267
272
|
...readApprovalDescriptor(part.approval.descriptor),
|
|
268
273
|
...part.approval,
|
|
269
274
|
};
|
|
275
|
+
const approval =
|
|
276
|
+
typeof rawApproval.id === "string" &&
|
|
277
|
+
rawApproval.approved === undefined &&
|
|
278
|
+
rawApproval.resolution === undefined &&
|
|
279
|
+
cancelledToolApprovalIds?.has(rawApproval.id)
|
|
280
|
+
? { ...rawApproval, resolution: "cancelled" as const }
|
|
281
|
+
: rawApproval;
|
|
270
282
|
const response =
|
|
271
283
|
typeof approval.id === "string" &&
|
|
272
284
|
approval.approved === undefined &&
|
|
@@ -523,6 +535,7 @@ function convertParts(
|
|
|
523
535
|
toolStatus,
|
|
524
536
|
metadata.supportsRichToolApprovalResponses === true,
|
|
525
537
|
metadata.toolApprovalResponses,
|
|
538
|
+
metadata.cancelledToolApprovalIds,
|
|
526
539
|
),
|
|
527
540
|
} satisfies ToolCallMessagePart;
|
|
528
541
|
}
|
|
@@ -661,6 +674,15 @@ export const AISDKMessageConverter = unstable_createMessageConverter(
|
|
|
661
674
|
id: message.id,
|
|
662
675
|
createdAt,
|
|
663
676
|
content,
|
|
677
|
+
...(message.role === "assistant" &&
|
|
678
|
+
metadata.cancelledStatusMessageIds?.has(message.id)
|
|
679
|
+
? {
|
|
680
|
+
status: {
|
|
681
|
+
type: "incomplete" as const,
|
|
682
|
+
reason: "cancelled" as const,
|
|
683
|
+
},
|
|
684
|
+
}
|
|
685
|
+
: undefined),
|
|
664
686
|
metadata: {
|
|
665
687
|
...toThreadMetadata(message.metadata),
|
|
666
688
|
...(timing && { timing }),
|
package/src/runtime/AISDKChat.ts
CHANGED
|
@@ -18,10 +18,6 @@ const useAISDKChat = <UI_MESSAGE extends UIMessage = UIMessage>(
|
|
|
18
18
|
options?: AISDKChatOptions<UI_MESSAGE>,
|
|
19
19
|
) => {
|
|
20
20
|
const [id] = useState(() => options?.id ?? generateId());
|
|
21
|
-
// The transport resolves the request id from the thread list item, falling
|
|
22
|
-
// back to the runtime's main item, whose id here is the external store's
|
|
23
|
-
// placeholder constant. The single thread of this entry is the chat itself,
|
|
24
|
-
// so the handed-over item initializes to the chat id.
|
|
25
21
|
const [threadListItem] = useState(() => ({
|
|
26
22
|
initialize: async () => ({ remoteId: id, externalId: undefined }),
|
|
27
23
|
}));
|
|
@@ -56,6 +56,32 @@ const chatCallbacks = () => ({
|
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
describe("AISDKThreads", () => {
|
|
59
|
+
it("emits the selected thread id when its composer sends", async () => {
|
|
60
|
+
const { transport } = createControlledTransport();
|
|
61
|
+
const handle = createAssistantClient(
|
|
62
|
+
AuiConfig({ threads: AISDKThreads({ transport }) }),
|
|
63
|
+
);
|
|
64
|
+
try {
|
|
65
|
+
handle.subscribe(() => {});
|
|
66
|
+
const aui = handle.getClient();
|
|
67
|
+
const sent = vi.fn();
|
|
68
|
+
aui.on({ scope: "thread", event: "composer.send" }, sent);
|
|
69
|
+
|
|
70
|
+
flushTapSync(() => aui.composer.setText("hello"));
|
|
71
|
+
flushTapSync(() => aui.composer.send());
|
|
72
|
+
|
|
73
|
+
await vi.waitFor(() => {
|
|
74
|
+
expect(sent).toHaveBeenCalledWith(
|
|
75
|
+
expect.objectContaining({
|
|
76
|
+
threadId: aui.threads.getState().mainThreadId,
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
} finally {
|
|
81
|
+
handle.destroy();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
59
85
|
it("runs one chat per thread and keeps histories isolated across switches", async () => {
|
|
60
86
|
const { transport, emit, close } = createControlledTransport();
|
|
61
87
|
const handle = createAssistantClient(
|