@assistant-ui/ai-sdk 0.0.1 → 0.0.3
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/dist/converters/convertMessage.d.ts.map +1 -1
- package/dist/converters/toCreateMessage.d.ts.map +1 -1
- package/dist/converters/toCreateMessage.js +6 -2
- package/dist/converters/toCreateMessage.js.map +1 -1
- package/dist/model-context/injectInteractableContext.d.ts +1 -1
- package/dist/model-context/injectInteractableContext.js +1 -1
- package/dist/model-context/injectInteractableContext.js.map +1 -1
- package/dist/model-context/injectQuoteContext.d.ts +1 -1
- package/dist/model-context/injectQuoteContext.js +1 -1
- package/dist/model-context/injectQuoteContext.js.map +1 -1
- package/dist/runtime/useAISDKRuntime.d.ts +16 -3
- package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
- package/dist/runtime/useAISDKRuntime.js +29 -6
- package/dist/runtime/useAISDKRuntime.js.map +1 -1
- package/dist/runtime/useChatThread.d.ts +7 -1
- package/dist/runtime/useChatThread.d.ts.map +1 -1
- package/dist/runtime/useChatThread.js +9 -3
- package/dist/runtime/useChatThread.js.map +1 -1
- package/dist/runtime/useExternalHistory.d.ts.map +1 -1
- package/dist/runtime/useExternalHistory.js +10 -0
- package/dist/runtime/useExternalHistory.js.map +1 -1
- package/package.json +11 -11
- package/src/converters/toCreateMessage.ts +6 -5
- package/src/model-context/injectInteractableContext.ts +1 -1
- package/src/model-context/injectQuoteContext.ts +1 -1
- package/src/runtime/useAISDKRuntime.denied-tool.test.tsx +122 -0
- package/src/runtime/useAISDKRuntime.test.ts +191 -7
- package/src/runtime/useAISDKRuntime.ts +110 -51
- package/src/runtime/useChatRuntime.test.ts +16 -0
- package/src/runtime/useChatThread.ts +15 -0
- package/src/runtime/useExternalHistory.test.ts +79 -1
- package/src/runtime/useExternalHistory.ts +13 -0
|
@@ -140,6 +140,16 @@ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storag
|
|
|
140
140
|
persistSettled(false);
|
|
141
141
|
}, 0);
|
|
142
142
|
});
|
|
143
|
+
const initialThreadState = runtimeRef.current.thread.getState();
|
|
144
|
+
wasRunningRef.current = initialThreadState.isRunning;
|
|
145
|
+
if (initialThreadState.isRunning) {
|
|
146
|
+
if (runStartRef.current == null) {
|
|
147
|
+
runStartRef.current = Date.now();
|
|
148
|
+
stepBoundariesRef.current = [];
|
|
149
|
+
toolCallCountRef.current = 0;
|
|
150
|
+
adapter.pin?.();
|
|
151
|
+
}
|
|
152
|
+
} else if (initialThreadState.messages.length > 0) persistSettled(true);
|
|
143
153
|
function persistSettled(ignoreRunning) {
|
|
144
154
|
persistTimerRef.current = null;
|
|
145
155
|
const latest = runtimeRef.current.thread.getState();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useExternalHistory.js","names":["AssistantRuntime","ThreadHistoryAdapter","ThreadMessage","MessageFormatAdapter","MessageFormatRepository","ExportedMessageRepository","getExternalStoreMessages","MessageRepository","useAui","useRef","useEffect","useState","RefObject","useCallback","useMemo","toExportedMessageRepository","toThreadMessages","messages","TMessage","survivingIds","Set","survivors","flatMap","m","message","console","warn","parentId","has","add","id","headId","isAwaitingToolApproval","status","type","reason","snapshotExternalMessages","Map","map","useExternalHistory","runtimeRef","historyAdapter","storageFormatAdapter","onSetMessages","loadedRef","itemEpoch","setItemEpoch","aui","optionalThreadListItem","threadListItem","source","hasLoaded","setHasLoaded","historyIds","persistedInnerIds","deferredTelemetryIds","persistedExternalMessages","onSetMessagesRef","current","formatAdapter","undefined","withFormat","Error","isLoading","loadHistory","repo","load","length","getId","converted","thread","import","tempRepo","getMessages","error","pin","remoteId","getState","subscribe","n","threadState","isRunning","runStartRef","persistTimerRef","ReturnType","setTimeout","persistInFlightRef","Promise","resolve","stepBoundariesRef","wasRunningRef","toolCallCountRef","adapter","unsubscribe","wasRunning","lastMsg","at","role","currentToolCallCount","content","filter","p","push","Date","now","clearTimeout","persistSettled","ignoreRunning","latest","boundaries","durationMs","lastAssistant","findLast","tcCount","totalSteps","stepDur","i","Math","round","stepTimestamps","endMs","start_ms","end_ms","telemetryOptions","then","changedRunMessageIds","externalMessages","previous","get","some","item","index","lastInnerMessageId","failedUpdateIds","getLastInnerId","msgs","toBatchItems","msg","idx","innerMessages","isTerminal","isAwaitingToolCalls","isReady","update","isPersistedMessage","batchItems","innerId","append","delete","reportTelemetry","nextSnapshot","catch","deleteMessage","messageId","deleteMessages","bind","messageIndex","findIndex","previousInnerMessages","slice","itemsToDelete","deletion"],"sources":["../../src/runtime/useExternalHistory.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n AssistantRuntime,\n ThreadHistoryAdapter,\n ThreadMessage,\n MessageFormatAdapter,\n MessageFormatRepository,\n ExportedMessageRepository,\n} from \"@assistant-ui/core\";\nimport { getExternalStoreMessages } from \"@assistant-ui/core\";\nimport { MessageRepository } from \"@assistant-ui/core/internal\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n useRef,\n useEffect,\n useState,\n type RefObject,\n useCallback,\n useMemo,\n} from \"react\";\n\nexport const toExportedMessageRepository = <TMessage>(\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n messages: MessageFormatRepository<TMessage>,\n): ExportedMessageRepository => {\n const survivingIds = new Set<string>();\n const survivors = messages.messages.flatMap((m) => {\n const message = toThreadMessages([m.message])[0];\n if (!message) {\n console.warn(\"Skipping a stored message that could not be loaded.\");\n return [];\n }\n if (m.parentId && !survivingIds.has(m.parentId)) return [];\n survivingIds.add(message.id);\n return [{ ...m, message }];\n });\n\n return {\n headId:\n messages.headId && survivingIds.has(messages.headId)\n ? messages.headId\n : null,\n messages: survivors,\n };\n};\n\nconst isAwaitingToolApproval = (message: ThreadMessage) =>\n message.status?.type === \"requires-action\" &&\n message.status.reason === \"tool-calls\";\n\nconst snapshotExternalMessages = <TMessage>(\n messages: readonly ThreadMessage[],\n) =>\n new Map<string, TMessage[]>(\n messages.map((message) => [\n message.id,\n [...getExternalStoreMessages<TMessage>(message)],\n ]),\n );\n\nexport const useExternalHistory = <TMessage>(\n runtimeRef: RefObject<AssistantRuntime>,\n historyAdapter: ThreadHistoryAdapter | undefined,\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n onSetMessages: (messages: TMessage[]) => void,\n) => {\n const loadedRef = useRef(false);\n const [itemEpoch, setItemEpoch] = useState(0);\n\n const aui = useAui();\n const optionalThreadListItem = useCallback(\n () => (aui.threadListItem.source ? aui.threadListItem : null),\n [aui],\n );\n\n const [hasLoaded, setHasLoaded] = useState(false);\n\n const historyIds = useRef(new Set<string>());\n const persistedInnerIds = useRef(new Set<string>());\n const deferredTelemetryIds = useRef(new Set<string>());\n const persistedExternalMessages = useRef(new Map<string, TMessage[]>());\n\n const onSetMessagesRef = useRef(onSetMessages);\n useEffect(() => {\n onSetMessagesRef.current = onSetMessages;\n });\n\n const formatAdapter = useMemo(() => {\n if (!historyAdapter) return undefined;\n if (!historyAdapter.withFormat) {\n throw new Error(\n \"useAISDKRuntime: ThreadHistoryAdapter is missing the required `withFormat` method.\",\n );\n }\n return historyAdapter.withFormat<TMessage, any>(storageFormatAdapter);\n }, [historyAdapter, storageFormatAdapter]);\n\n const isLoading = formatAdapter != null && !hasLoaded;\n\n useEffect(() => {\n if (!formatAdapter || loadedRef.current) return undefined;\n\n const loadHistory = async () => {\n try {\n const repo = await formatAdapter.load();\n if (repo && repo.messages.length > 0) {\n for (const m of repo.messages) {\n persistedInnerIds.current.add(\n storageFormatAdapter.getId(m.message),\n );\n }\n const converted = toExportedMessageRepository(toThreadMessages, repo);\n runtimeRef.current.thread.import(converted);\n\n const tempRepo = new MessageRepository();\n tempRepo.import(converted);\n const messages = tempRepo.getMessages();\n\n onSetMessagesRef.current(\n messages.flatMap(getExternalStoreMessages<TMessage>),\n );\n\n historyIds.current = new Set();\n for (const m of converted.messages) {\n historyIds.current.add(m.message.id);\n if (isAwaitingToolApproval(m.message)) {\n deferredTelemetryIds.current.add(m.message.id);\n }\n }\n persistedExternalMessages.current =\n snapshotExternalMessages<TMessage>(\n converted.messages.map((m) => m.message),\n );\n }\n } catch (error) {\n console.error(\"Failed to load message history:\", error);\n } finally {\n setHasLoaded(true);\n }\n };\n\n formatAdapter.pin?.();\n\n const remoteId = optionalThreadListItem()?.getState().remoteId;\n if (!remoteId) {\n setHasLoaded(true);\n return aui.subscribe(() => {\n if (optionalThreadListItem()?.getState().remoteId) {\n setItemEpoch((n) => n + 1);\n }\n });\n }\n\n const threadState = runtimeRef.current.thread.getState();\n if (threadState.isRunning || threadState.messages.length > 0) {\n loadedRef.current = true;\n setHasLoaded(true);\n return undefined;\n }\n\n loadedRef.current = true;\n void loadHistory();\n return undefined;\n }, [\n formatAdapter,\n toThreadMessages,\n runtimeRef,\n optionalThreadListItem,\n aui,\n itemEpoch,\n storageFormatAdapter,\n ]);\n\n const runStartRef = useRef<number | null>(null);\n const persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const persistInFlightRef = useRef<Promise<void>>(Promise.resolve());\n const stepBoundariesRef = useRef<number[]>([]);\n const wasRunningRef = useRef(false);\n const toolCallCountRef = useRef(0);\n\n useEffect(() => {\n if (!formatAdapter) return;\n const adapter = formatAdapter;\n\n const unsubscribe = runtimeRef.current.thread.subscribe(() => {\n const threadState = runtimeRef.current.thread.getState();\n const { isRunning } = threadState;\n const wasRunning = wasRunningRef.current;\n wasRunningRef.current = isRunning;\n\n // Track step boundaries by content changes (more reliable than isRunning)\n if (runStartRef.current != null) {\n const lastMsg = threadState.messages.at(-1);\n if (lastMsg?.role === \"assistant\") {\n const currentToolCallCount = lastMsg.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n while (toolCallCountRef.current < currentToolCallCount) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n toolCallCountRef.current++;\n }\n }\n }\n\n if (isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n adapter.pin?.();\n }\n // Cancel any pending persist — isRunning went back to true\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n }\n return;\n }\n\n // Only act on the true→false transition\n if (!wasRunning) return;\n\n // Record step boundary offset (synchronous for accuracy)\n if (runStartRef.current != null) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n }\n\n // Debounce: wait one macrotask so agentic step flickers are absorbed\n if (persistTimerRef.current) clearTimeout(persistTimerRef.current);\n persistTimerRef.current = setTimeout(() => {\n persistSettled(false);\n }, 0);\n });\n\n 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 changedRunMessageIds = new Set<string>();\n for (const message of latest.messages) {\n const externalMessages =\n getExternalStoreMessages<TMessage>(message);\n const previous = persistedExternalMessages.current.get(message.id);\n if (\n previous === undefined ||\n previous.length !== externalMessages.length ||\n externalMessages.some((item, index) => item !== previous[index])\n ) {\n changedRunMessageIds.add(message.id);\n }\n }\n\n const { messages } = latest;\n let lastInnerMessageId: string | null = null;\n const failedUpdateIds = new Set<string>();\n\n const getLastInnerId = (msgs: TMessage[]): string | null =>\n msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)!) : null;\n\n const toBatchItems = (msgs: TMessage[]) =>\n msgs.map((msg, idx) => ({\n parentId:\n idx === 0\n ? lastInnerMessageId\n : storageFormatAdapter.getId(msgs[idx - 1]!),\n message: msg,\n }));\n\n for (const message of messages) {\n const innerMessages = getExternalStoreMessages<TMessage>(message);\n\n const isTerminal =\n message.status === undefined ||\n message.status.type === \"complete\" ||\n message.status.type === \"incomplete\";\n const isAwaitingToolCalls = isAwaitingToolApproval(message);\n // A paused message's later content can only reach storage via update, so it is persisted early only when the adapter supports update.\n const isReady =\n isTerminal ||\n (isAwaitingToolCalls && adapter.update !== undefined);\n\n if (!isReady) {\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n\n const isPersistedMessage = historyIds.current.has(message.id);\n if (isPersistedMessage && !changedRunMessageIds.has(message.id)) {\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n if (!isPersistedMessage) {\n historyIds.current.add(message.id);\n deferredTelemetryIds.current.add(message.id);\n }\n\n const batchItems = toBatchItems(innerMessages);\n for (const item of batchItems) {\n const innerId = storageFormatAdapter.getId(item.message);\n if (!persistedInnerIds.current.has(innerId)) {\n await adapter.append(item);\n persistedInnerIds.current.add(innerId);\n } else if (durationMs !== undefined) {\n try {\n await adapter.update?.(item, innerId);\n } catch {\n // A failed update drops the message from the refreshed baseline so it retries on the next run stop.\n failedUpdateIds.add(message.id);\n }\n }\n }\n\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n\n if (deferredTelemetryIds.current.has(message.id) && isTerminal) {\n deferredTelemetryIds.current.delete(message.id);\n adapter.reportTelemetry?.(batchItems, telemetryOptions);\n }\n }\n\n const nextSnapshot = snapshotExternalMessages<TMessage>(\n latest.messages,\n );\n for (const id of failedUpdateIds) {\n nextSnapshot.delete(id);\n }\n persistedExternalMessages.current = nextSnapshot;\n })\n .catch((error) => {\n console.error(\"Failed to persist message history:\", error);\n });\n }\n\n return () => {\n unsubscribe();\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n persistSettled(false);\n }\n };\n }, [formatAdapter, storageFormatAdapter, runtimeRef]);\n\n const deleteMessage = useCallback(\n async (messageId: string) => {\n const deleteMessages = formatAdapter?.delete?.bind(formatAdapter);\n if (!deleteMessages) return;\n\n const messages = runtimeRef.current.thread.getState().messages;\n const messageIndex = messages.findIndex((m) => m.id === messageId);\n if (messageIndex === -1) return;\n\n const previousInnerMessages = messages\n .slice(0, messageIndex)\n .flatMap(getExternalStoreMessages<TMessage>);\n let parentId = previousInnerMessages.at(-1)\n ? storageFormatAdapter.getId(previousInnerMessages.at(-1)!)\n : null;\n const itemsToDelete = getExternalStoreMessages<TMessage>(\n messages[messageIndex]!,\n ).map((message) => {\n const item = { parentId, message };\n parentId = storageFormatAdapter.getId(message);\n return item;\n });\n\n const deletion = persistInFlightRef.current.then(async () => {\n await deleteMessages(itemsToDelete);\n\n historyIds.current.delete(messageId);\n deferredTelemetryIds.current.delete(messageId);\n persistedExternalMessages.current.delete(messageId);\n for (const item of itemsToDelete) {\n persistedInnerIds.current.delete(\n storageFormatAdapter.getId(item.message),\n );\n }\n });\n\n persistInFlightRef.current = deletion.catch(() => {});\n await deletion;\n },\n [formatAdapter, runtimeRef, storageFormatAdapter],\n );\n\n return { isLoading, deleteMessage };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useExternalHistory.js","names":["AssistantRuntime","ThreadHistoryAdapter","ThreadMessage","MessageFormatAdapter","MessageFormatRepository","ExportedMessageRepository","getExternalStoreMessages","MessageRepository","useAui","useRef","useEffect","useState","RefObject","useCallback","useMemo","toExportedMessageRepository","toThreadMessages","messages","TMessage","survivingIds","Set","survivors","flatMap","m","message","console","warn","parentId","has","add","id","headId","isAwaitingToolApproval","status","type","reason","snapshotExternalMessages","Map","map","useExternalHistory","runtimeRef","historyAdapter","storageFormatAdapter","onSetMessages","loadedRef","itemEpoch","setItemEpoch","aui","optionalThreadListItem","threadListItem","source","hasLoaded","setHasLoaded","historyIds","persistedInnerIds","deferredTelemetryIds","persistedExternalMessages","onSetMessagesRef","current","formatAdapter","undefined","withFormat","Error","isLoading","loadHistory","repo","load","length","getId","converted","thread","import","tempRepo","getMessages","error","pin","remoteId","getState","subscribe","n","threadState","isRunning","runStartRef","persistTimerRef","ReturnType","setTimeout","persistInFlightRef","Promise","resolve","stepBoundariesRef","wasRunningRef","toolCallCountRef","adapter","unsubscribe","wasRunning","lastMsg","at","role","currentToolCallCount","content","filter","p","push","Date","now","clearTimeout","persistSettled","initialThreadState","ignoreRunning","latest","boundaries","durationMs","lastAssistant","findLast","tcCount","totalSteps","stepDur","i","Math","round","stepTimestamps","endMs","start_ms","end_ms","telemetryOptions","then","changedRunMessageIds","externalMessages","previous","get","some","item","index","lastInnerMessageId","failedUpdateIds","getLastInnerId","msgs","toBatchItems","msg","idx","innerMessages","isTerminal","isAwaitingToolCalls","isReady","update","isPersistedMessage","batchItems","innerId","append","delete","reportTelemetry","nextSnapshot","catch","deleteMessage","messageId","deleteMessages","bind","messageIndex","findIndex","previousInnerMessages","slice","itemsToDelete","deletion"],"sources":["../../src/runtime/useExternalHistory.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n AssistantRuntime,\n ThreadHistoryAdapter,\n ThreadMessage,\n MessageFormatAdapter,\n MessageFormatRepository,\n ExportedMessageRepository,\n} from \"@assistant-ui/core\";\nimport { getExternalStoreMessages } from \"@assistant-ui/core\";\nimport { MessageRepository } from \"@assistant-ui/core/internal\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n useRef,\n useEffect,\n useState,\n type RefObject,\n useCallback,\n useMemo,\n} from \"react\";\n\nexport const toExportedMessageRepository = <TMessage>(\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n messages: MessageFormatRepository<TMessage>,\n): ExportedMessageRepository => {\n const survivingIds = new Set<string>();\n const survivors = messages.messages.flatMap((m) => {\n const message = toThreadMessages([m.message])[0];\n if (!message) {\n console.warn(\"Skipping a stored message that could not be loaded.\");\n return [];\n }\n if (m.parentId && !survivingIds.has(m.parentId)) return [];\n survivingIds.add(message.id);\n return [{ ...m, message }];\n });\n\n return {\n headId:\n messages.headId && survivingIds.has(messages.headId)\n ? messages.headId\n : null,\n messages: survivors,\n };\n};\n\nconst isAwaitingToolApproval = (message: ThreadMessage) =>\n message.status?.type === \"requires-action\" &&\n message.status.reason === \"tool-calls\";\n\nconst snapshotExternalMessages = <TMessage>(\n messages: readonly ThreadMessage[],\n) =>\n new Map<string, TMessage[]>(\n messages.map((message) => [\n message.id,\n [...getExternalStoreMessages<TMessage>(message)],\n ]),\n );\n\nexport const useExternalHistory = <TMessage>(\n runtimeRef: RefObject<AssistantRuntime>,\n historyAdapter: ThreadHistoryAdapter | undefined,\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n onSetMessages: (messages: TMessage[]) => void,\n) => {\n const loadedRef = useRef(false);\n const [itemEpoch, setItemEpoch] = useState(0);\n\n const aui = useAui();\n const optionalThreadListItem = useCallback(\n () => (aui.threadListItem.source ? aui.threadListItem : null),\n [aui],\n );\n\n const [hasLoaded, setHasLoaded] = useState(false);\n\n const historyIds = useRef(new Set<string>());\n const persistedInnerIds = useRef(new Set<string>());\n const deferredTelemetryIds = useRef(new Set<string>());\n const persistedExternalMessages = useRef(new Map<string, TMessage[]>());\n\n const onSetMessagesRef = useRef(onSetMessages);\n useEffect(() => {\n onSetMessagesRef.current = onSetMessages;\n });\n\n const formatAdapter = useMemo(() => {\n if (!historyAdapter) return undefined;\n if (!historyAdapter.withFormat) {\n throw new Error(\n \"useAISDKRuntime: ThreadHistoryAdapter is missing the required `withFormat` method.\",\n );\n }\n return historyAdapter.withFormat<TMessage, any>(storageFormatAdapter);\n }, [historyAdapter, storageFormatAdapter]);\n\n const isLoading = formatAdapter != null && !hasLoaded;\n\n useEffect(() => {\n if (!formatAdapter || loadedRef.current) return undefined;\n\n const loadHistory = async () => {\n try {\n const repo = await formatAdapter.load();\n if (repo && repo.messages.length > 0) {\n for (const m of repo.messages) {\n persistedInnerIds.current.add(\n storageFormatAdapter.getId(m.message),\n );\n }\n const converted = toExportedMessageRepository(toThreadMessages, repo);\n runtimeRef.current.thread.import(converted);\n\n const tempRepo = new MessageRepository();\n tempRepo.import(converted);\n const messages = tempRepo.getMessages();\n\n onSetMessagesRef.current(\n messages.flatMap(getExternalStoreMessages<TMessage>),\n );\n\n historyIds.current = new Set();\n for (const m of converted.messages) {\n historyIds.current.add(m.message.id);\n if (isAwaitingToolApproval(m.message)) {\n deferredTelemetryIds.current.add(m.message.id);\n }\n }\n persistedExternalMessages.current =\n snapshotExternalMessages<TMessage>(\n converted.messages.map((m) => m.message),\n );\n }\n } catch (error) {\n console.error(\"Failed to load message history:\", error);\n } finally {\n setHasLoaded(true);\n }\n };\n\n formatAdapter.pin?.();\n\n const remoteId = optionalThreadListItem()?.getState().remoteId;\n if (!remoteId) {\n setHasLoaded(true);\n return aui.subscribe(() => {\n if (optionalThreadListItem()?.getState().remoteId) {\n setItemEpoch((n) => n + 1);\n }\n });\n }\n\n const threadState = runtimeRef.current.thread.getState();\n if (threadState.isRunning || threadState.messages.length > 0) {\n loadedRef.current = true;\n setHasLoaded(true);\n return undefined;\n }\n\n loadedRef.current = true;\n void loadHistory();\n return undefined;\n }, [\n formatAdapter,\n toThreadMessages,\n runtimeRef,\n optionalThreadListItem,\n aui,\n itemEpoch,\n storageFormatAdapter,\n ]);\n\n const runStartRef = useRef<number | null>(null);\n const persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const persistInFlightRef = useRef<Promise<void>>(Promise.resolve());\n const stepBoundariesRef = useRef<number[]>([]);\n const wasRunningRef = useRef(false);\n const toolCallCountRef = useRef(0);\n\n useEffect(() => {\n if (!formatAdapter) return;\n const adapter = formatAdapter;\n\n const unsubscribe = runtimeRef.current.thread.subscribe(() => {\n const threadState = runtimeRef.current.thread.getState();\n const { isRunning } = threadState;\n const wasRunning = wasRunningRef.current;\n wasRunningRef.current = isRunning;\n\n // Track step boundaries by content changes (more reliable than isRunning)\n if (runStartRef.current != null) {\n const lastMsg = threadState.messages.at(-1);\n if (lastMsg?.role === \"assistant\") {\n const currentToolCallCount = lastMsg.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n while (toolCallCountRef.current < currentToolCallCount) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n toolCallCountRef.current++;\n }\n }\n }\n\n if (isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n adapter.pin?.();\n }\n // Cancel any pending persist — isRunning went back to true\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n }\n return;\n }\n\n // Only act on the true→false transition\n if (!wasRunning) return;\n\n // Record step boundary offset (synchronous for accuracy)\n if (runStartRef.current != null) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n }\n\n // Debounce: wait one macrotask so agentic step flickers are absorbed\n if (persistTimerRef.current) clearTimeout(persistTimerRef.current);\n persistTimerRef.current = setTimeout(() => {\n persistSettled(false);\n }, 0);\n });\n\n const initialThreadState = runtimeRef.current.thread.getState();\n wasRunningRef.current = initialThreadState.isRunning;\n if (initialThreadState.isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n adapter.pin?.();\n }\n } else if (initialThreadState.messages.length > 0) {\n persistSettled(true);\n }\n\n function persistSettled(ignoreRunning: boolean) {\n persistTimerRef.current = null;\n const latest = runtimeRef.current.thread.getState();\n if (!ignoreRunning && latest.isRunning) return;\n\n const boundaries = stepBoundariesRef.current;\n const durationMs = boundaries.length > 0 ? boundaries.at(-1) : undefined;\n\n // Fallback: if only 1 boundary but message has multiple steps, distribute evenly\n if (boundaries.length === 1 && durationMs != null) {\n const lastAssistant = latest.messages.findLast(\n (m) => m.role === \"assistant\",\n );\n if (lastAssistant) {\n const tcCount = lastAssistant.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n if (tcCount > 0) {\n const totalSteps = tcCount + 1;\n const stepDur = durationMs / totalSteps;\n boundaries.length = 0;\n for (let i = 0; i < totalSteps; i++) {\n boundaries.push(Math.round((i + 1) * stepDur));\n }\n }\n }\n }\n\n // Build per-step timestamps when there are multiple steps\n const stepTimestamps =\n boundaries.length > 1\n ? boundaries.map((endMs, i) => ({\n start_ms: i === 0 ? 0 : boundaries[i - 1]!,\n end_ms: endMs,\n }))\n : undefined;\n\n runStartRef.current = null;\n stepBoundariesRef.current = [];\n\n const telemetryOptions = {\n ...(durationMs != null ? { durationMs } : undefined),\n ...(stepTimestamps != null ? { stepTimestamps } : undefined),\n };\n\n persistInFlightRef.current = persistInFlightRef.current\n .then(async () => {\n const changedRunMessageIds = new Set<string>();\n for (const message of latest.messages) {\n const externalMessages =\n getExternalStoreMessages<TMessage>(message);\n const previous = persistedExternalMessages.current.get(message.id);\n if (\n previous === undefined ||\n previous.length !== externalMessages.length ||\n externalMessages.some((item, index) => item !== previous[index])\n ) {\n changedRunMessageIds.add(message.id);\n }\n }\n\n const { messages } = latest;\n let lastInnerMessageId: string | null = null;\n const failedUpdateIds = new Set<string>();\n\n const getLastInnerId = (msgs: TMessage[]): string | null =>\n msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)!) : null;\n\n const toBatchItems = (msgs: TMessage[]) =>\n msgs.map((msg, idx) => ({\n parentId:\n idx === 0\n ? lastInnerMessageId\n : storageFormatAdapter.getId(msgs[idx - 1]!),\n message: msg,\n }));\n\n for (const message of messages) {\n const innerMessages = getExternalStoreMessages<TMessage>(message);\n\n const isTerminal =\n message.status === undefined ||\n message.status.type === \"complete\" ||\n message.status.type === \"incomplete\";\n const isAwaitingToolCalls = isAwaitingToolApproval(message);\n // A paused message's later content can only reach storage via update, so it is persisted early only when the adapter supports update.\n const isReady =\n isTerminal ||\n (isAwaitingToolCalls && adapter.update !== undefined);\n\n if (!isReady) {\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n\n const isPersistedMessage = historyIds.current.has(message.id);\n if (isPersistedMessage && !changedRunMessageIds.has(message.id)) {\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n if (!isPersistedMessage) {\n historyIds.current.add(message.id);\n deferredTelemetryIds.current.add(message.id);\n }\n\n const batchItems = toBatchItems(innerMessages);\n for (const item of batchItems) {\n const innerId = storageFormatAdapter.getId(item.message);\n if (!persistedInnerIds.current.has(innerId)) {\n await adapter.append(item);\n persistedInnerIds.current.add(innerId);\n } else if (durationMs !== undefined) {\n try {\n await adapter.update?.(item, innerId);\n } catch {\n // A failed update drops the message from the refreshed baseline so it retries on the next run stop.\n failedUpdateIds.add(message.id);\n }\n }\n }\n\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n\n if (deferredTelemetryIds.current.has(message.id) && isTerminal) {\n deferredTelemetryIds.current.delete(message.id);\n adapter.reportTelemetry?.(batchItems, telemetryOptions);\n }\n }\n\n const nextSnapshot = snapshotExternalMessages<TMessage>(\n latest.messages,\n );\n for (const id of failedUpdateIds) {\n nextSnapshot.delete(id);\n }\n persistedExternalMessages.current = nextSnapshot;\n })\n .catch((error) => {\n console.error(\"Failed to persist message history:\", error);\n });\n }\n\n return () => {\n unsubscribe();\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n persistSettled(false);\n }\n };\n }, [formatAdapter, storageFormatAdapter, runtimeRef]);\n\n const deleteMessage = useCallback(\n async (messageId: string) => {\n const deleteMessages = formatAdapter?.delete?.bind(formatAdapter);\n if (!deleteMessages) return;\n\n const messages = runtimeRef.current.thread.getState().messages;\n const messageIndex = messages.findIndex((m) => m.id === messageId);\n if (messageIndex === -1) return;\n\n const previousInnerMessages = messages\n .slice(0, messageIndex)\n .flatMap(getExternalStoreMessages<TMessage>);\n let parentId = previousInnerMessages.at(-1)\n ? storageFormatAdapter.getId(previousInnerMessages.at(-1)!)\n : null;\n const itemsToDelete = getExternalStoreMessages<TMessage>(\n messages[messageIndex]!,\n ).map((message) => {\n const item = { parentId, message };\n parentId = storageFormatAdapter.getId(message);\n return item;\n });\n\n const deletion = persistInFlightRef.current.then(async () => {\n await deleteMessages(itemsToDelete);\n\n historyIds.current.delete(messageId);\n deferredTelemetryIds.current.delete(messageId);\n persistedExternalMessages.current.delete(messageId);\n for (const item of itemsToDelete) {\n persistedInnerIds.current.delete(\n storageFormatAdapter.getId(item.message),\n );\n }\n });\n\n persistInFlightRef.current = deletion.catch(() => {});\n await deletion;\n },\n [formatAdapter, runtimeRef, storageFormatAdapter],\n );\n\n return { isLoading, deleteMessage };\n};\n"],"mappings":";;;;;;AAsBA,MAAae,+BACXC,kBACAC,aAC8B;CAC9B,MAAME,+BAAe,IAAIC,IAAY;CACrC,MAAMC,YAAYJ,SAASA,SAASK,SAASC,MAAM;EACjD,MAAMC,UAAUR,iBAAiB,CAACO,EAAEC,OAAO,CAAC,CAAC,CAAC;EAC9C,IAAI,CAACA,SAAS;GACZC,QAAQC,KAAK,qDAAqD;GAClE,OAAO,CAAA;EACT;EACA,IAAIH,EAAEI,YAAY,CAACR,aAAaS,IAAIL,EAAEI,QAAQ,GAAG,OAAO,CAAA;EACxDR,aAAaU,IAAIL,QAAQM,EAAE;EAC3B,OAAO,CAAC;GAAE,GAAGP;GAAGC;EAAQ,CAAC;CAC3B,CAAC;CAED,OAAO;EACLO,QACEd,SAASc,UAAUZ,aAAaS,IAAIX,SAASc,MAAM,IAC/Cd,SAASc,SACT;EACNd,UAAUI;CACZ;AACF;AAEA,MAAMW,0BAA0BR,YAC9BA,QAAQS,QAAQC,SAAS,qBACzBV,QAAQS,OAAOE,WAAW;AAE5B,MAAMC,4BACJnB,aAEA,IAAIoB,IACFpB,SAASqB,KAAKd,YAAY,CACxBA,QAAQM,IACR,CAAC,GAAGxB,yBAAmCkB,OAAO,CAAC,CAAC,CACjD,CACH;AAEF,MAAae,sBACXC,YACAC,gBACAzB,kBACA0B,sBACAC,kBACG;CACH,MAAMC,YAAYnC,OAAO,KAAK;CAC9B,MAAM,CAACoC,WAAWC,gBAAgBnC,SAAS,CAAC;CAE5C,MAAMoC,MAAMvC,OAAO;CACnB,MAAMwC,yBAAyBnC,kBACtBkC,IAAIE,eAAeC,SAASH,IAAIE,iBAAiB,MACxD,CAACF,GAAG,CACN;CAEA,MAAM,CAACI,WAAWC,gBAAgBzC,SAAS,KAAK;CAEhD,MAAM0C,aAAa5C,uBAAO,IAAIW,IAAY,CAAC;CAC3C,MAAMkC,oBAAoB7C,uBAAO,IAAIW,IAAY,CAAC;CAClD,MAAMmC,uBAAuB9C,uBAAO,IAAIW,IAAY,CAAC;CACrD,MAAMoC,4BAA4B/C,uBAAO,IAAI4B,IAAwB,CAAC;CAEtE,MAAMoB,mBAAmBhD,OAAOkC,aAAa;CAC7CjC,gBAAgB;EACd+C,iBAAiBC,UAAUf;CAC7B,CAAC;CAED,MAAMgB,gBAAgB7C,cAAc;EAClC,IAAI,CAAC2B,gBAAgB,OAAOmB,KAAAA;EAC5B,IAAI,CAACnB,eAAeoB,YAClB,MAAM,IAAIC,MACR,oFACF;EAEF,OAAOrB,eAAeoB,WAA0BnB,oBAAoB;CACtE,GAAG,CAACD,gBAAgBC,oBAAoB,CAAC;CAEzC,MAAMqB,YAAYJ,iBAAiB,QAAQ,CAACR;CAE5CzC,gBAAgB;EACd,IAAI,CAACiD,iBAAiBf,UAAUc,SAAS,OAAOE,KAAAA;EAEhD,MAAMI,cAAc,YAAY;GAC9B,IAAI;IACF,MAAMC,OAAO,MAAMN,cAAcO,KAAK;IACtC,IAAID,QAAQA,KAAKhD,SAASkD,SAAS,GAAG;KACpC,KAAK,MAAM5C,KAAK0C,KAAKhD,UACnBqC,kBAAkBI,QAAQ7B,IACxBa,qBAAqB0B,MAAM7C,EAAEC,OAAO,CACtC;KAEF,MAAM6C,YAAYtD,4BAA4BC,kBAAkBiD,IAAI;KACpEzB,WAAWkB,QAAQY,OAAOC,OAAOF,SAAS;KAE1C,MAAMG,WAAW,IAAIjE,kBAAkB;KACvCiE,SAASD,OAAOF,SAAS;KACzB,MAAMpD,WAAWuD,SAASC,YAAY;KAEtChB,iBAAiBC,QACfzC,SAASK,QAAQhB,wBAAkC,CACrD;KAEA+C,WAAWK,0BAAU,IAAItC,IAAI;KAC7B,KAAK,MAAMG,OAAK8C,UAAUpD,UAAU;MAClCoC,WAAWK,QAAQ7B,IAAIN,IAAEC,QAAQM,EAAE;MACnC,IAAIE,uBAAuBT,IAAEC,OAAO,GAClC+B,qBAAqBG,QAAQ7B,IAAIN,IAAEC,QAAQM,EAAE;KAEjD;KACA0B,0BAA0BE,UACxBtB,yBACEiC,UAAUpD,SAASqB,KAAKf,QAAMA,IAAEC,OAAO,CACzC;IACJ;GACF,SAASkD,OAAO;IACdjD,QAAQiD,MAAM,mCAAmCA,KAAK;GACxD,UAAU;IACRtB,aAAa,IAAI;GACnB;EACF;EAEAO,cAAcgB,MAAM;EAGpB,IAAI,CADa3B,uBAAuB,CAAC,EAAE6B,SAAS,CAAC,CAACD,UACvC;GACbxB,aAAa,IAAI;GACjB,OAAOL,IAAI+B,gBAAgB;IACzB,IAAI9B,uBAAuB,CAAC,EAAE6B,SAAS,CAAC,CAACD,UACvC9B,cAAciC,MAAMA,IAAI,CAAC;GAE7B,CAAC;EACH;EAEA,MAAMC,cAAcxC,WAAWkB,QAAQY,OAAOO,SAAS;EACvD,IAAIG,YAAYC,aAAaD,YAAY/D,SAASkD,SAAS,GAAG;GAC5DvB,UAAUc,UAAU;GACpBN,aAAa,IAAI;GACjB;EACF;EAEAR,UAAUc,UAAU;EACpB,YAAiB;CAEnB,GAAG;EACDC;EACA3C;EACAwB;EACAQ;EACAD;EACAF;EACAH;CAAoB,CACrB;CAED,MAAMwC,cAAczE,OAAsB,IAAI;CAC9C,MAAM0E,kBAAkB1E,OAA6C,IAAI;CACzE,MAAM6E,qBAAqB7E,OAAsB8E,QAAQC,QAAQ,CAAC;CAClE,MAAMC,oBAAoBhF,OAAiB,CAAA,CAAE;CAC7C,MAAMiF,gBAAgBjF,OAAO,KAAK;CAClC,MAAMkF,mBAAmBlF,OAAO,CAAC;CAEjCC,gBAAgB;EACd,IAAI,CAACiD,eAAe;EACpB,MAAMiC,UAAUjC;EAEhB,MAAMkC,cAAcrD,WAAWkB,QAAQY,OAAOQ,gBAAgB;GAC5D,MAAME,gBAAcxC,WAAWkB,QAAQY,OAAOO,SAAS;GACvD,MAAM,EAAEI,cAAcD;GACtB,MAAMc,aAAaJ,cAAchC;GACjCgC,cAAchC,UAAUuB;GAGxB,IAAIC,YAAYxB,WAAW,MAAM;IAC/B,MAAMqC,UAAUf,cAAY/D,SAAS+E,GAAG,EAAE;IAC1C,IAAID,SAASE,SAAS,aAAa;KACjC,MAAMC,uBAAuBH,QAAQI,QAAQC,QAC1CC,MAAMA,EAAEnE,SAAS,WACpB,CAAC,CAACiC;KACF,OAAOwB,iBAAiBjC,UAAUwC,sBAAsB;MACtDT,kBAAkB/B,QAAQ4C,KAAKC,KAAKC,IAAI,IAAItB,YAAYxB,OAAO;MAC/DiC,iBAAiBjC;KACnB;IACF;GACF;GAEA,IAAIuB,WAAW;IACb,IAAIC,YAAYxB,WAAW,MAAM;KAC/BwB,YAAYxB,UAAU6C,KAAKC,IAAI;KAC/Bf,kBAAkB/B,UAAU,CAAA;KAC5BiC,iBAAiBjC,UAAU;KAC3BkC,QAAQjB,MAAM;IAChB;IAEA,IAAIQ,gBAAgBzB,SAAS;KAC3B+C,aAAatB,gBAAgBzB,OAAO;KACpCyB,gBAAgBzB,UAAU;IAC5B;IACA;GACF;GAGA,IAAI,CAACoC,YAAY;GAGjB,IAAIZ,YAAYxB,WAAW,MACzB+B,kBAAkB/B,QAAQ4C,KAAKC,KAAKC,IAAI,IAAItB,YAAYxB,OAAO;GAIjE,IAAIyB,gBAAgBzB,SAAS+C,aAAatB,gBAAgBzB,OAAO;GACjEyB,gBAAgBzB,UAAU2B,iBAAiB;IACzCqB,eAAe,KAAK;GACtB,GAAG,CAAC;EACN,CAAC;EAED,MAAMC,qBAAqBnE,WAAWkB,QAAQY,OAAOO,SAAS;EAC9Da,cAAchC,UAAUiD,mBAAmB1B;EAC3C,IAAI0B,mBAAmB1B,WACjBC;OAAAA,YAAYxB,WAAW,MAAM;IAC/BwB,YAAYxB,UAAU6C,KAAKC,IAAI;IAC/Bf,kBAAkB/B,UAAU,CAAA;IAC5BiC,iBAAiBjC,UAAU;IAC3BkC,QAAQjB,MAAM;GAChB;SACK,IAAIgC,mBAAmB1F,SAASkD,SAAS,GAC9CuC,eAAe,IAAI;EAGrB,SAASA,eAAeE,eAAwB;GAC9CzB,gBAAgBzB,UAAU;GAC1B,MAAMmD,SAASrE,WAAWkB,QAAQY,OAAOO,SAAS;GAClD,IAAI,CAAC+B,iBAAiBC,OAAO5B,WAAW;GAExC,MAAM6B,aAAarB,kBAAkB/B;GACrC,MAAMqD,aAAaD,WAAW3C,SAAS,IAAI2C,WAAWd,GAAG,EAAE,IAAIpC,KAAAA;GAG/D,IAAIkD,WAAW3C,WAAW,KAAK4C,cAAc,MAAM;IACjD,MAAMC,gBAAgBH,OAAO5F,SAASgG,UACnC1F,QAAMA,IAAE0E,SAAS,WACpB;IACA,IAAIe,eAAe;KACjB,MAAME,UAAUF,cAAcb,QAAQC,QACnCC,QAAMA,IAAEnE,SAAS,WACpB,CAAC,CAACiC;KACF,IAAI+C,UAAU,GAAG;MACf,MAAMC,aAAaD,UAAU;MAC7B,MAAME,UAAUL,aAAaI;MAC7BL,WAAW3C,SAAS;MACpB,KAAK,IAAIkD,IAAI,GAAGA,IAAIF,YAAYE,KAC9BP,WAAWR,KAAKgB,KAAKC,OAAOF,IAAI,KAAKD,OAAO,CAAC;KAEjD;IACF;GACF;GAGA,MAAMI,iBACJV,WAAW3C,SAAS,IAChB2C,WAAWxE,KAAKmF,OAAOJ,SAAO;IAC5BK,UAAUL,QAAM,IAAI,IAAIP,WAAWO,MAAI;IACvCM,QAAQF;GACV,EAAE,IACF7D,KAAAA;GAENsB,YAAYxB,UAAU;GACtB+B,kBAAkB/B,UAAU,CAAA;GAE5B,MAAMkE,mBAAmB;IACvB,GAAIb,cAAc,OAAO,EAAEA,WAAW,IAAInD,KAAAA;IAC1C,GAAI4D,kBAAkB,OAAO,EAAEA,eAAe,IAAI5D,KAAAA;GACpD;GAEA0B,mBAAmB5B,UAAU4B,mBAAmB5B,QAC7CmE,KAAK,YAAY;IAChB,MAAMC,uCAAuB,IAAI1G,IAAY;IAC7C,KAAK,MAAMI,WAAWqF,OAAO5F,UAAU;KACrC,MAAM8G,mBACJzH,yBAAmCkB,OAAO;KAC5C,MAAMwG,WAAWxE,0BAA0BE,QAAQuE,IAAIzG,QAAQM,EAAE;KACjE,IACEkG,aAAapE,KAAAA,KACboE,SAAS7D,WAAW4D,iBAAiB5D,UACrC4D,iBAAiBG,MAAMC,MAAMC,UAAUD,SAASH,SAASI,MAAM,GAE/DN,qBAAqBjG,IAAIL,QAAQM,EAAE;IAEvC;IAEA,MAAM,EAAEb,UAAAA,eAAa4F;IACrB,IAAIwB,qBAAoC;IACxC,MAAMC,kCAAkB,IAAIlH,IAAY;IAExC,MAAMmH,kBAAkBC,SACtBA,KAAKrE,SAAS,IAAIzB,qBAAqB0B,MAAMoE,KAAKxC,GAAG,EAAE,CAAE,IAAI;IAE/D,MAAMyC,gBAAgBD,WACpBA,OAAKlG,KAAKoG,KAAKC,SAAS;KACtBhH,UACEgH,QAAQ,IACJN,qBACA3F,qBAAqB0B,MAAMoE,OAAKG,MAAM,EAAG;KAC/CnH,SAASkH;IACX,EAAE;IAEJ,KAAK,MAAMlH,aAAWP,YAAU;KAC9B,MAAM2H,gBAAgBtI,yBAAmCkB,SAAO;KAEhE,MAAMqH,aACJrH,UAAQS,WAAW2B,KAAAA,KACnBpC,UAAQS,OAAOC,SAAS,cACxBV,UAAQS,OAAOC,SAAS;KAC1B,MAAM4G,sBAAsB9G,uBAAuBR,SAAO;KAM1D,IAAI,EAHFqH,cACCC,uBAAuBlD,QAAQoD,WAAWpF,KAAAA,IAE/B;MACZyE,qBACEE,eAAeK,aAAa,KAAKP;MACnC;KACF;KAEA,MAAMY,qBAAqB5F,WAAWK,QAAQ9B,IAAIJ,UAAQM,EAAE;KAC5D,IAAImH,sBAAsB,CAACnB,qBAAqBlG,IAAIJ,UAAQM,EAAE,GAAG;MAC/DuG,qBACEE,eAAeK,aAAa,KAAKP;MACnC;KACF;KACA,IAAI,CAACY,oBAAoB;MACvB5F,WAAWK,QAAQ7B,IAAIL,UAAQM,EAAE;MACjCyB,qBAAqBG,QAAQ7B,IAAIL,UAAQM,EAAE;KAC7C;KAEA,MAAMoH,aAAaT,aAAaG,aAAa;KAC7C,KAAK,MAAMT,UAAQe,YAAY;MAC7B,MAAMC,UAAUzG,qBAAqB0B,MAAM+D,OAAK3G,OAAO;MACvD,IAAI,CAAC8B,kBAAkBI,QAAQ9B,IAAIuH,OAAO,GAAG;OAC3C,MAAMvD,QAAQwD,OAAOjB,MAAI;OACzB7E,kBAAkBI,QAAQ7B,IAAIsH,OAAO;MACvC,OAAO,IAAIpC,eAAenD,KAAAA,GACxB,IAAI;OACF,MAAMgC,QAAQoD,SAASb,QAAMgB,OAAO;MACtC,QAAQ;OAENb,gBAAgBzG,IAAIL,UAAQM,EAAE;MAChC;KAEJ;KAEAuG,qBACEE,eAAeK,aAAa,KAAKP;KAEnC,IAAI9E,qBAAqBG,QAAQ9B,IAAIJ,UAAQM,EAAE,KAAK+G,YAAY;MAC9DtF,qBAAqBG,QAAQ2F,OAAO7H,UAAQM,EAAE;MAC9C8D,QAAQ0D,kBAAkBJ,YAAYtB,gBAAgB;KACxD;IACF;IAEA,MAAM2B,eAAenH,yBACnByE,OAAO5F,QACT;IACA,KAAK,MAAMa,MAAMwG,iBACfiB,aAAaF,OAAOvH,EAAE;IAExB0B,0BAA0BE,UAAU6F;GACtC,CAAC,CAAC,CACDC,OAAO9E,YAAU;IAChBjD,QAAQiD,MAAM,sCAAsCA,OAAK;GAC3D,CAAC;EACL;EAEA,aAAa;GACXmB,YAAY;GACZ,IAAIV,gBAAgBzB,SAAS;IAC3B+C,aAAatB,gBAAgBzB,OAAO;IACpCyB,gBAAgBzB,UAAU;IAC1BgD,eAAe,KAAK;GACtB;EACF;CACF,GAAG;EAAC/C;EAAejB;EAAsBF;CAAU,CAAC;CA4CpD,OAAO;EAAEuB;EAAW0F,eA1CE5I,YACpB,OAAO6I,cAAsB;GAC3B,MAAMC,iBAAiBhG,eAAe0F,QAAQO,KAAKjG,aAAa;GAChE,IAAI,CAACgG,gBAAgB;GAErB,MAAM1I,aAAWuB,WAAWkB,QAAQY,OAAOO,SAAS,CAAC,CAAC5D;GACtD,MAAM4I,eAAe5I,WAAS6I,WAAWvI,QAAMA,IAAEO,OAAO4H,SAAS;GACjE,IAAIG,iBAAiB,IAAI;GAEzB,MAAME,wBAAwB9I,WAC3B+I,MAAM,GAAGH,YAAY,CAAC,CACtBvI,QAAQhB,wBAAkC;GAC7C,IAAIqB,WAAWoI,sBAAsB/D,GAAG,EAAE,IACtCtD,qBAAqB0B,MAAM2F,sBAAsB/D,GAAG,EAAE,CAAE,IACxD;GACJ,MAAMiE,gBAAgB3J,yBACpBW,WAAS4I,aACX,CAAC,CAACvH,KAAKd,cAAY;IACjB,MAAM2G,SAAO;KAAExG;KAAUH,SAAAA;IAAQ;IACjCG,WAAWe,qBAAqB0B,MAAM5C,SAAO;IAC7C,OAAO2G;GACT,CAAC;GAED,MAAM+B,WAAW5E,mBAAmB5B,QAAQmE,KAAK,YAAY;IAC3D,MAAM8B,eAAeM,aAAa;IAElC5G,WAAWK,QAAQ2F,OAAOK,SAAS;IACnCnG,qBAAqBG,QAAQ2F,OAAOK,SAAS;IAC7ClG,0BAA0BE,QAAQ2F,OAAOK,SAAS;IAClD,KAAK,MAAMvB,UAAQ8B,eACjB3G,kBAAkBI,QAAQ2F,OACxB3G,qBAAqB0B,MAAM+D,OAAK3G,OAAO,CACzC;GAEJ,CAAC;GAED8D,mBAAmB5B,UAAUwG,SAASV,YAAY,CAAC,CAAC;GACpD,MAAMU;EACR,GACA;GAACvG;GAAenB;GAAYE;EAAoB,CAG9B+G;CAAc;AACpC"}
|
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.3",
|
|
4
4
|
"description": "Vercel AI SDK adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-sdk",
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
],
|
|
47
47
|
"sideEffects": false,
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@ai-sdk/mcp": "^2.0.
|
|
50
|
-
"@ai-sdk/react": "^4.0.
|
|
51
|
-
"@assistant-ui/core": "^0.3.
|
|
52
|
-
"@assistant-ui/store": "^0.3.
|
|
53
|
-
"@assistant-ui/tap": "^0.9.
|
|
54
|
-
"ai": "^7.0.
|
|
49
|
+
"@ai-sdk/mcp": "^2.0.36",
|
|
50
|
+
"@ai-sdk/react": "^4.0.80",
|
|
51
|
+
"@assistant-ui/core": "^0.3.16",
|
|
52
|
+
"@assistant-ui/store": "^0.3.11",
|
|
53
|
+
"@assistant-ui/tap": "^0.9.15",
|
|
54
|
+
"ai": "^7.0.77",
|
|
55
55
|
"assistant-cloud": "*",
|
|
56
|
-
"assistant-stream": "^0.3.
|
|
56
|
+
"assistant-stream": "^0.3.40"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@types/react": "*",
|
|
@@ -68,14 +68,14 @@
|
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
+
"@assistant-ui/x-buildutils": "0.0.24",
|
|
71
72
|
"@testing-library/react": "^16.3.2",
|
|
72
73
|
"@types/json-schema": "^7.0.15",
|
|
73
74
|
"@types/react": "^19.2.18",
|
|
74
|
-
"@types/react-dom": "^19.2.
|
|
75
|
+
"@types/react-dom": "^19.2.5",
|
|
75
76
|
"jsdom": "^30.0.1",
|
|
76
77
|
"react": "^19.2.8",
|
|
77
|
-
"vitest": "^4.1.
|
|
78
|
-
"@assistant-ui/x-buildutils": "0.0.23"
|
|
78
|
+
"vitest": "^4.1.11"
|
|
79
79
|
},
|
|
80
80
|
"publishConfig": {
|
|
81
81
|
"access": "public",
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { AppendMessage } from "@assistant-ui/core";
|
|
2
2
|
import {
|
|
3
|
-
httpUrlPattern,
|
|
4
|
-
parseDataUrl,
|
|
5
3
|
resolveFileMediaType,
|
|
4
|
+
resolveFilePartSource,
|
|
6
5
|
resolveImageMediaType,
|
|
7
6
|
toMediaWireUrl,
|
|
8
7
|
} from "@assistant-ui/core/internal";
|
|
@@ -72,11 +71,13 @@ export const toCreateMessage = <UI_MESSAGE extends UIMessage = UIMessage>(
|
|
|
72
71
|
// envelope is rebuilt from the typed format rather than forwarded.
|
|
73
72
|
const mediaType = `audio/${part.audio.format}`;
|
|
74
73
|
const data = part.audio.data;
|
|
74
|
+
const source = resolveFilePartSource({ data, mimeType: mediaType });
|
|
75
75
|
return {
|
|
76
76
|
type: "file",
|
|
77
|
-
url:
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
url:
|
|
78
|
+
source.kind === "url"
|
|
79
|
+
? source.url
|
|
80
|
+
: `data:${mediaType};base64,${source.data}`,
|
|
80
81
|
mediaType,
|
|
81
82
|
...(part.filename && { filename: part.filename }),
|
|
82
83
|
};
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
* @example
|
|
24
24
|
* ```ts
|
|
25
25
|
* import { convertToModelMessages, streamText } from "ai";
|
|
26
|
-
* import { unstable_injectInteractableContext } from "@assistant-ui/
|
|
26
|
+
* import { unstable_injectInteractableContext } from "@assistant-ui/ai-sdk";
|
|
27
27
|
*
|
|
28
28
|
* export async function POST(req: Request) {
|
|
29
29
|
* const { messages } = await req.json();
|
|
@@ -22,7 +22,7 @@ const getQuoteText = (metadata: unknown): string | undefined => {
|
|
|
22
22
|
* @example
|
|
23
23
|
* ```ts
|
|
24
24
|
* import { convertToModelMessages, streamText } from "ai";
|
|
25
|
-
* import { injectQuoteContext } from "@assistant-ui/
|
|
25
|
+
* import { injectQuoteContext } from "@assistant-ui/ai-sdk";
|
|
26
26
|
*
|
|
27
27
|
* export async function POST(req: Request) {
|
|
28
28
|
* const { messages } = await req.json();
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { renderHook } from "@testing-library/react";
|
|
4
|
+
import type { ExternalStoreAdapter } from "@assistant-ui/core";
|
|
5
|
+
import { describe, expect, it, vi } from "vitest";
|
|
6
|
+
|
|
7
|
+
const mocks = vi.hoisted(() => ({
|
|
8
|
+
adapter: undefined as ExternalStoreAdapter | undefined,
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
vi.mock("@assistant-ui/core/react", async (importOriginal) => {
|
|
12
|
+
const original =
|
|
13
|
+
await importOriginal<typeof import("@assistant-ui/core/react")>();
|
|
14
|
+
return {
|
|
15
|
+
...original,
|
|
16
|
+
useExternalStoreRuntime: vi.fn((adapter: ExternalStoreAdapter) => {
|
|
17
|
+
mocks.adapter = adapter;
|
|
18
|
+
return {};
|
|
19
|
+
}),
|
|
20
|
+
useRuntimeAdapters: vi.fn(() => ({})),
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
vi.mock("./useExternalHistory", async (importOriginal) => {
|
|
25
|
+
const original =
|
|
26
|
+
await importOriginal<typeof import("./useExternalHistory")>();
|
|
27
|
+
return {
|
|
28
|
+
...original,
|
|
29
|
+
useExternalHistory: vi.fn(() => ({
|
|
30
|
+
isLoading: false,
|
|
31
|
+
deleteMessage: vi.fn().mockResolvedValue(undefined),
|
|
32
|
+
})),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
import { useAISDKRuntime } from "./useAISDKRuntime";
|
|
37
|
+
|
|
38
|
+
describe("useAISDKRuntime pending tool completion", () => {
|
|
39
|
+
const setup = (toolPart: Record<string, unknown>) => {
|
|
40
|
+
const messages = [
|
|
41
|
+
{
|
|
42
|
+
id: "m1",
|
|
43
|
+
role: "assistant",
|
|
44
|
+
parts: [toolPart],
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
let updated: any[] | undefined;
|
|
48
|
+
const setMessages = vi.fn(
|
|
49
|
+
(updater: (m: typeof messages) => typeof messages) => {
|
|
50
|
+
updated = typeof updater === "function" ? updater(messages) : updater;
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
const chat = {
|
|
54
|
+
id: "chat-1",
|
|
55
|
+
status: "ready",
|
|
56
|
+
error: undefined,
|
|
57
|
+
messages,
|
|
58
|
+
setMessages,
|
|
59
|
+
sendMessage: vi.fn().mockResolvedValue(undefined),
|
|
60
|
+
regenerate: vi.fn(),
|
|
61
|
+
addToolOutput: vi.fn(),
|
|
62
|
+
addToolApprovalResponse: vi.fn(),
|
|
63
|
+
stop: vi.fn(),
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
renderHook(() => useAISDKRuntime(chat as never));
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
send: () =>
|
|
70
|
+
mocks.adapter!.onNew!({
|
|
71
|
+
role: "user",
|
|
72
|
+
parentId: "m1",
|
|
73
|
+
sourceId: null,
|
|
74
|
+
runConfig: {},
|
|
75
|
+
content: [{ type: "text", text: "next" }],
|
|
76
|
+
attachments: [],
|
|
77
|
+
createdAt: new Date(0),
|
|
78
|
+
metadata: { custom: {} },
|
|
79
|
+
} as never),
|
|
80
|
+
getUpdated: () => updated,
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
it("preserves a denied tool call and its approval record on the next send", async () => {
|
|
85
|
+
const { send, getUpdated } = setup({
|
|
86
|
+
type: "tool-search",
|
|
87
|
+
toolCallId: "call_1",
|
|
88
|
+
state: "output-denied",
|
|
89
|
+
input: { query: "x" },
|
|
90
|
+
approval: { id: "appr_1", approved: false, reason: "too dangerous" },
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await send();
|
|
94
|
+
|
|
95
|
+
const updated = getUpdated();
|
|
96
|
+
const part = updated?.at(-1)?.parts?.[0];
|
|
97
|
+
expect(part?.state).toBe("output-denied");
|
|
98
|
+
expect(part?.approval).toEqual({
|
|
99
|
+
id: "appr_1",
|
|
100
|
+
approved: false,
|
|
101
|
+
reason: "too dangerous",
|
|
102
|
+
});
|
|
103
|
+
expect(part?.errorText).toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("still cancels a genuinely pending tool call on the next send", async () => {
|
|
107
|
+
const { send, getUpdated } = setup({
|
|
108
|
+
type: "tool-search",
|
|
109
|
+
toolCallId: "call_1",
|
|
110
|
+
state: "input-available",
|
|
111
|
+
input: { query: "x" },
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
await send();
|
|
115
|
+
|
|
116
|
+
const part = getUpdated()?.at(-1)?.parts?.[0];
|
|
117
|
+
expect(part?.state).toBe("output-error");
|
|
118
|
+
expect(part?.errorText).toBe(
|
|
119
|
+
"User cancelled tool call by sending a new message.",
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -7,13 +7,17 @@ import { validateUIMessages } from "ai";
|
|
|
7
7
|
// Mock only the sibling module that requires AUI store context (not available
|
|
8
8
|
// in isolation). Every other dependency — useExternalStoreRuntime,
|
|
9
9
|
// useToolInvocations, the message converter — runs for real.
|
|
10
|
-
vi.mock("./useExternalHistory", () =>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
vi.mock("./useExternalHistory", async (importOriginal) => {
|
|
11
|
+
const original =
|
|
12
|
+
await importOriginal<typeof import("./useExternalHistory")>();
|
|
13
|
+
return {
|
|
14
|
+
...original,
|
|
15
|
+
useExternalHistory: vi.fn(() => ({
|
|
16
|
+
isLoading: false,
|
|
17
|
+
deleteMessage: vi.fn().mockResolvedValue(undefined),
|
|
18
|
+
})),
|
|
19
|
+
};
|
|
20
|
+
});
|
|
17
21
|
|
|
18
22
|
import { useExternalHistory } from "./useExternalHistory";
|
|
19
23
|
import { useAISDKRuntime } from "./useAISDKRuntime";
|
|
@@ -690,6 +694,186 @@ describe("useAISDKRuntime", () => {
|
|
|
690
694
|
expect(result.current.thread.getState().suggestions).toEqual(suggestions);
|
|
691
695
|
});
|
|
692
696
|
|
|
697
|
+
it("imports a message tree without replacing the chat feed", async () => {
|
|
698
|
+
const chat = createChatHelpers();
|
|
699
|
+
const onBranchChange = vi.fn();
|
|
700
|
+
const messageRepository = {
|
|
701
|
+
headId: "a2",
|
|
702
|
+
messages: [
|
|
703
|
+
{
|
|
704
|
+
parentId: null,
|
|
705
|
+
message: {
|
|
706
|
+
id: "u1",
|
|
707
|
+
role: "user" as const,
|
|
708
|
+
parts: [{ type: "text" as const, text: "question" }],
|
|
709
|
+
},
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
parentId: "u1",
|
|
713
|
+
message: {
|
|
714
|
+
id: "a1",
|
|
715
|
+
role: "assistant" as const,
|
|
716
|
+
parts: [{ type: "text" as const, text: "first" }],
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
parentId: "u1",
|
|
721
|
+
message: {
|
|
722
|
+
id: "a2",
|
|
723
|
+
role: "assistant" as const,
|
|
724
|
+
parts: [{ type: "text" as const, text: "second" }],
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
],
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
const { result, rerender } = renderHook(() =>
|
|
731
|
+
useAISDKRuntime(chat, {
|
|
732
|
+
messageRepository,
|
|
733
|
+
unstable_onBranchChange: onBranchChange,
|
|
734
|
+
}),
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
await waitFor(() => {
|
|
738
|
+
expect(
|
|
739
|
+
result.current.thread.getState().messages.map((message) => message.id),
|
|
740
|
+
).toEqual(["u1", "a2"]);
|
|
741
|
+
});
|
|
742
|
+
expect(result.current.thread.getMessageById("a2").getState()).toMatchObject(
|
|
743
|
+
{
|
|
744
|
+
branchNumber: 2,
|
|
745
|
+
branchCount: 2,
|
|
746
|
+
},
|
|
747
|
+
);
|
|
748
|
+
expect(chat.messages.map((message: { id: string }) => message.id)).toEqual([
|
|
749
|
+
"u1",
|
|
750
|
+
"a2",
|
|
751
|
+
]);
|
|
752
|
+
|
|
753
|
+
act(() => {
|
|
754
|
+
result.current.thread
|
|
755
|
+
.getMessageById("a2")
|
|
756
|
+
.switchToBranch({ branchId: "a1" });
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
expect(onBranchChange).toHaveBeenCalledWith({
|
|
760
|
+
headId: "a1",
|
|
761
|
+
visibleMessageIds: ["u1", "a1"],
|
|
762
|
+
});
|
|
763
|
+
expect(chat.messages.map((message: { id: string }) => message.id)).toEqual([
|
|
764
|
+
"u1",
|
|
765
|
+
"a1",
|
|
766
|
+
]);
|
|
767
|
+
expect(chat.messages).not.toEqual([]);
|
|
768
|
+
|
|
769
|
+
chat.messages = [
|
|
770
|
+
{ id: "u1", role: "user", parts: [{ type: "text", text: "question" }] },
|
|
771
|
+
{
|
|
772
|
+
id: "a1",
|
|
773
|
+
role: "assistant",
|
|
774
|
+
parts: [{ type: "text", text: "first token" }],
|
|
775
|
+
},
|
|
776
|
+
];
|
|
777
|
+
rerender();
|
|
778
|
+
|
|
779
|
+
await waitFor(() => {
|
|
780
|
+
expect(textOf(result.current.thread.getState().messages.at(-1))).toBe(
|
|
781
|
+
"first token",
|
|
782
|
+
);
|
|
783
|
+
});
|
|
784
|
+
expect(result.current.thread.getMessageById("a1").getState()).toMatchObject(
|
|
785
|
+
{
|
|
786
|
+
branchNumber: 1,
|
|
787
|
+
branchCount: 2,
|
|
788
|
+
},
|
|
789
|
+
);
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
it("does not overwrite a nonempty chat with the repository", async () => {
|
|
793
|
+
const chat = createChatHelpers([
|
|
794
|
+
{ id: "live", role: "user", parts: [{ type: "text", text: "keep me" }] },
|
|
795
|
+
]);
|
|
796
|
+
const messageRepository = {
|
|
797
|
+
headId: "a1",
|
|
798
|
+
messages: [
|
|
799
|
+
{
|
|
800
|
+
parentId: null,
|
|
801
|
+
message: {
|
|
802
|
+
id: "u1",
|
|
803
|
+
role: "user" as const,
|
|
804
|
+
parts: [{ type: "text" as const, text: "question" }],
|
|
805
|
+
},
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
parentId: "u1",
|
|
809
|
+
message: {
|
|
810
|
+
id: "a1",
|
|
811
|
+
role: "assistant" as const,
|
|
812
|
+
parts: [{ type: "text" as const, text: "first" }],
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
],
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
const { result } = renderHook(() =>
|
|
819
|
+
useAISDKRuntime(chat, { messageRepository }),
|
|
820
|
+
);
|
|
821
|
+
|
|
822
|
+
await waitFor(() => {
|
|
823
|
+
expect(
|
|
824
|
+
result.current.thread.getState().messages.map((message) => message.id),
|
|
825
|
+
).toEqual(["live"]);
|
|
826
|
+
});
|
|
827
|
+
expect(chat.messages.map((message: { id: string }) => message.id)).toEqual([
|
|
828
|
+
"live",
|
|
829
|
+
]);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
it("does not reseed when the repository object identity changes", async () => {
|
|
833
|
+
const chat = createChatHelpers();
|
|
834
|
+
const makeRepository = (text: string) => ({
|
|
835
|
+
headId: "a1",
|
|
836
|
+
messages: [
|
|
837
|
+
{
|
|
838
|
+
parentId: null,
|
|
839
|
+
message: {
|
|
840
|
+
id: "u1",
|
|
841
|
+
role: "user" as const,
|
|
842
|
+
parts: [{ type: "text" as const, text: "question" }],
|
|
843
|
+
},
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
parentId: "u1",
|
|
847
|
+
message: {
|
|
848
|
+
id: "a1",
|
|
849
|
+
role: "assistant" as const,
|
|
850
|
+
parts: [{ type: "text" as const, text }],
|
|
851
|
+
},
|
|
852
|
+
},
|
|
853
|
+
],
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
const { result, rerender } = renderHook(
|
|
857
|
+
({ repository }) =>
|
|
858
|
+
useAISDKRuntime(chat, { messageRepository: repository }),
|
|
859
|
+
{ initialProps: { repository: makeRepository("first") } },
|
|
860
|
+
);
|
|
861
|
+
|
|
862
|
+
await waitFor(() => {
|
|
863
|
+
expect(textOf(result.current.thread.getState().messages.at(-1))).toBe(
|
|
864
|
+
"first",
|
|
865
|
+
);
|
|
866
|
+
});
|
|
867
|
+
const setMessagesCalls = chat.setMessages.mock.calls.length;
|
|
868
|
+
|
|
869
|
+
rerender({ repository: makeRepository("second") });
|
|
870
|
+
|
|
871
|
+
expect(textOf(result.current.thread.getState().messages.at(-1))).toBe(
|
|
872
|
+
"first",
|
|
873
|
+
);
|
|
874
|
+
expect(chat.setMessages.mock.calls.length).toBe(setMessagesCalls);
|
|
875
|
+
});
|
|
876
|
+
|
|
693
877
|
it("calls adapters.suggestion after settle with messages and signal", async () => {
|
|
694
878
|
const generate = vi.fn().mockResolvedValue([{ prompt: "next" }]);
|
|
695
879
|
const chat = createChatHelpers([
|