@assistant-ui/core 0.3.19 → 0.3.21
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/adapters/attachment.d.ts +50 -43
- package/dist/adapters/attachment.d.ts.map +1 -1
- package/dist/adapters/attachment.js +52 -21
- package/dist/adapters/attachment.js.map +1 -1
- package/dist/adapters/directive-formatter.d.ts +2 -5
- package/dist/adapters/directive-formatter.d.ts.map +1 -1
- package/dist/adapters/feedback.d.ts +7 -8
- package/dist/adapters/feedback.d.ts.map +1 -1
- package/dist/adapters/index.d.ts +14 -9
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +1 -1
- package/dist/adapters/speech.d.ts +63 -65
- package/dist/adapters/speech.d.ts.map +1 -1
- package/dist/adapters/speech.js +16 -2
- package/dist/adapters/speech.js.map +1 -1
- package/dist/adapters/suggestion.d.ts +19 -22
- package/dist/adapters/suggestion.d.ts.map +1 -1
- package/dist/adapters/thread-history.d.ts +60 -58
- package/dist/adapters/thread-history.d.ts.map +1 -1
- package/dist/adapters/trigger.d.ts +8 -11
- package/dist/adapters/trigger.d.ts.map +1 -1
- package/dist/adapters/voice.d.ts +50 -45
- package/dist/adapters/voice.d.ts.map +1 -1
- package/dist/adapters/voice.js +17 -3
- package/dist/adapters/voice.js.map +1 -1
- package/dist/index.d.ts +70 -56
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/internal/duplicate-detection.d.ts +1 -4
- package/dist/internal/duplicate-detection.d.ts.map +1 -1
- package/dist/internal.d.ts +27 -58
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +8 -6
- package/dist/model-context/frame/host.d.ts +20 -23
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +3 -1
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/frame/provider.d.ts +28 -30
- package/dist/model-context/frame/provider.d.ts.map +1 -1
- package/dist/model-context/frame/provider.js +6 -0
- package/dist/model-context/frame/provider.js.map +1 -1
- package/dist/model-context/frame/types.d.ts +24 -27
- package/dist/model-context/frame/types.d.ts.map +1 -1
- package/dist/model-context/frame/validate.d.ts +3 -0
- package/dist/model-context/frame/validate.d.ts.map +1 -0
- package/dist/model-context/frame/validate.js +26 -0
- package/dist/model-context/frame/validate.js.map +1 -0
- package/dist/model-context/interactable-composer-metadata.d.ts +51 -39
- package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
- package/dist/model-context/interactable-composer-metadata.js +30 -6
- package/dist/model-context/interactable-composer-metadata.js.map +1 -1
- package/dist/model-context/registry-handles.d.ts +9 -12
- package/dist/model-context/registry-handles.d.ts.map +1 -1
- package/dist/model-context/registry.d.ts +15 -18
- package/dist/model-context/registry.d.ts.map +1 -1
- package/dist/model-context/tool-call-text.d.ts +15 -17
- package/dist/model-context/tool-call-text.d.ts.map +1 -1
- package/dist/model-context/tool.d.ts +12 -14
- package/dist/model-context/tool.d.ts.map +1 -1
- package/dist/model-context/types.d.ts +40 -43
- package/dist/model-context/types.d.ts.map +1 -1
- package/dist/react/AssistantProvider.d.ts +9 -12
- package/dist/react/AssistantProvider.d.ts.map +1 -1
- package/dist/react/AssistantRuntimeProvider.d.ts +23 -26
- package/dist/react/AssistantRuntimeProvider.d.ts.map +1 -1
- package/dist/react/RuntimeAdapter.d.ts +4 -8
- package/dist/react/RuntimeAdapter.d.ts.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts +28 -23
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.js +77 -58
- package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
- package/dist/react/adapters/TitleGenerationAdapter.d.ts +4 -7
- package/dist/react/adapters/TitleGenerationAdapter.d.ts.map +1 -1
- package/dist/react/client/AdaptedRemoteThread.d.ts +6 -9
- package/dist/react/client/AdaptedRemoteThread.d.ts.map +1 -1
- package/dist/react/client/DataRenderers.d.ts +2 -5
- package/dist/react/client/DataRenderers.d.ts.map +1 -1
- package/dist/react/client/InMemoryThreadList.d.ts +9 -12
- package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
- package/dist/react/client/Interactables.d.ts +3 -6
- package/dist/react/client/Interactables.d.ts.map +1 -1
- package/dist/react/client/Interactables.js +306 -101
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/RemoteThreadList.d.ts +34 -37
- package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
- package/dist/react/client/RemoteThreadList.js +2 -1
- package/dist/react/client/RemoteThreadList.js.map +1 -1
- package/dist/react/client/Tools.d.ts +10 -12
- package/dist/react/client/Tools.d.ts.map +1 -1
- package/dist/react/client/Tools.js +4 -8
- package/dist/react/client/Tools.js.map +1 -1
- package/dist/react/client/interactable-model-context.d.ts +9 -11
- package/dist/react/client/interactable-model-context.d.ts.map +1 -1
- package/dist/react/client/interactable-model-context.js +32 -22
- package/dist/react/client/interactable-model-context.js.map +1 -1
- package/dist/react/index.d.ts +160 -122
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +5 -5
- package/dist/react/interactables-legacy/Interactables.d.ts +2 -5
- package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
- package/dist/react/interactables-legacy/Interactables.js +15 -8
- package/dist/react/interactables-legacy/Interactables.js.map +1 -1
- package/dist/react/interactables-legacy/interactable-model-context.d.ts +6 -9
- package/dist/react/interactables-legacy/interactable-model-context.d.ts.map +1 -1
- package/dist/react/interactables-legacy/interactable-model-context.js +13 -14
- package/dist/react/interactables-legacy/interactable-model-context.js.map +1 -1
- package/dist/react/interactables-legacy/scopes.d.ts +42 -46
- package/dist/react/interactables-legacy/scopes.d.ts.map +1 -1
- package/dist/react/interactables-legacy/useAssistantInteractable.d.ts +8 -11
- package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -1
- package/dist/react/interactables-legacy/useInteractableState.d.ts +7 -9
- package/dist/react/interactables-legacy/useInteractableState.d.ts.map +1 -1
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +23 -14
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +54 -25
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
- package/dist/react/model-context/define-mcp-toolkit.d.ts +15 -18
- package/dist/react/model-context/define-mcp-toolkit.d.ts.map +1 -1
- package/dist/react/model-context/define-toolkit.d.ts +13 -6
- package/dist/react/model-context/define-toolkit.d.ts.map +1 -1
- package/dist/react/model-context/external-tool.d.ts +1 -4
- package/dist/react/model-context/external-tool.d.ts.map +1 -1
- package/dist/react/model-context/human-tool.d.ts +3 -6
- package/dist/react/model-context/human-tool.d.ts.map +1 -1
- package/dist/react/model-context/interactableTool.d.ts +26 -29
- package/dist/react/model-context/interactableTool.d.ts.map +1 -1
- package/dist/react/model-context/makeAssistantDataUI.d.ts +6 -9
- package/dist/react/model-context/makeAssistantDataUI.d.ts.map +1 -1
- package/dist/react/model-context/makeAssistantTool.d.ts +6 -9
- package/dist/react/model-context/makeAssistantTool.d.ts.map +1 -1
- package/dist/react/model-context/makeAssistantToolUI.d.ts +6 -9
- package/dist/react/model-context/makeAssistantToolUI.d.ts.map +1 -1
- package/dist/react/model-context/provider-tool.d.ts +5 -7
- package/dist/react/model-context/provider-tool.d.ts.map +1 -1
- package/dist/react/model-context/stub-tool.d.ts +1 -4
- package/dist/react/model-context/stub-tool.d.ts.map +1 -1
- package/dist/react/model-context/toolbox.d.ts +44 -32
- package/dist/react/model-context/toolbox.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantContext.d.ts +3 -6
- package/dist/react/model-context/useAssistantContext.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantDataUI.d.ts +7 -10
- package/dist/react/model-context/useAssistantDataUI.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantInstructions.d.ts +3 -6
- package/dist/react/model-context/useAssistantInstructions.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantTool.d.ts +9 -13
- package/dist/react/model-context/useAssistantTool.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantToolUI.d.ts +13 -16
- package/dist/react/model-context/useAssistantToolUI.d.ts.map +1 -1
- package/dist/react/model-context/useAuiToolOverrides.d.ts +3 -5
- package/dist/react/model-context/useAuiToolOverrides.d.ts.map +1 -1
- package/dist/react/model-context/useInlineRender.d.ts +3 -6
- package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
- package/dist/react/model-context/useInteractable.d.ts +40 -40
- package/dist/react/model-context/useInteractable.d.ts.map +1 -1
- package/dist/react/model-context/useInteractableState.d.ts +10 -9
- package/dist/react/model-context/useInteractableState.d.ts.map +1 -1
- package/dist/react/model-context/useInteractableVersions.d.ts +4 -7
- package/dist/react/model-context/useInteractableVersions.d.ts.map +1 -1
- package/dist/react/model-context/useToolArgsStatus.d.ts +7 -9
- package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useActionBarCopy.d.ts +7 -10
- package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useActionBarEdit.d.ts +3 -6
- package/dist/react/primitive-hooks/useActionBarEdit.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useActionBarFeedback.d.ts +6 -9
- package/dist/react/primitive-hooks/useActionBarFeedback.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useActionBarReload.d.ts +3 -6
- package/dist/react/primitive-hooks/useActionBarReload.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useActionBarSpeak.d.ts +3 -6
- package/dist/react/primitive-hooks/useActionBarSpeak.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useActionBarStopSpeaking.d.ts +3 -6
- package/dist/react/primitive-hooks/useActionBarStopSpeaking.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useBranchPickerNext.d.ts +3 -6
- package/dist/react/primitive-hooks/useBranchPickerNext.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts +3 -6
- package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts +4 -7
- package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useComposerCancel.d.ts +3 -6
- package/dist/react/primitive-hooks/useComposerCancel.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useComposerDictate.d.ts +3 -6
- package/dist/react/primitive-hooks/useComposerDictate.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useComposerSend.d.ts +4 -7
- package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useEditComposerCancel.d.ts +2 -5
- package/dist/react/primitive-hooks/useEditComposerCancel.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useEditComposerSend.d.ts +3 -6
- package/dist/react/primitive-hooks/useEditComposerSend.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useMessageBranching.d.ts +5 -8
- package/dist/react/primitive-hooks/useMessageBranching.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useMessageError.d.ts +1 -4
- package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useMessageReload.d.ts +3 -6
- package/dist/react/primitive-hooks/useMessageReload.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts +7 -10
- package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useSuggestionTrigger.js +4 -4
- package/dist/react/primitive-hooks/useSuggestionTrigger.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts +1 -4
- package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadIsRunning.d.ts +1 -4
- package/dist/react/primitive-hooks/useThreadIsRunning.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadListItemArchive.d.ts +2 -5
- package/dist/react/primitive-hooks/useThreadListItemArchive.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadListItemDelete.d.ts +2 -5
- package/dist/react/primitive-hooks/useThreadListItemDelete.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadListItemTrigger.d.ts +2 -5
- package/dist/react/primitive-hooks/useThreadListItemTrigger.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadListItemUnarchive.d.ts +2 -5
- package/dist/react/primitive-hooks/useThreadListItemUnarchive.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts +3 -6
- package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadListNew.d.ts +2 -5
- package/dist/react/primitive-hooks/useThreadListNew.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadMessageIds.d.ts +1 -4
- package/dist/react/primitive-hooks/useThreadMessageIds.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadMessages.d.ts +2 -5
- package/dist/react/primitive-hooks/useThreadMessages.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useVoice.d.ts +8 -11
- package/dist/react/primitive-hooks/useVoice.d.ts.map +1 -1
- package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.d.ts +27 -29
- package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.d.ts.map +1 -1
- package/dist/react/primitives/composer/ComposerAttachments.d.ts +26 -28
- package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
- package/dist/react/primitives/composer/ComposerIf.d.ts +13 -13
- package/dist/react/primitives/composer/ComposerIf.d.ts.map +1 -1
- package/dist/react/primitives/composer/ComposerQueue.d.ts +13 -17
- package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
- package/dist/react/primitives/generativeUI/GenerativeUI.d.ts +30 -33
- package/dist/react/primitives/generativeUI/GenerativeUI.d.ts.map +1 -1
- package/dist/react/primitives/generativeUI/GenerativeUI.js +42 -33
- package/dist/react/primitives/generativeUI/GenerativeUI.js.map +1 -1
- package/dist/react/primitives/message/MessageAttachments.d.ts +30 -28
- package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageAttachments.js +14 -15
- package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.d.ts +122 -120
- package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.js +3 -3
- package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
- package/dist/react/primitives/message/MessageParts.d.ts +208 -194
- package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageParts.js +124 -82
- package/dist/react/primitives/message/MessageParts.js.map +1 -1
- package/dist/react/primitives/message/MessageQuote.d.ts +8 -11
- package/dist/react/primitives/message/MessageQuote.d.ts.map +1 -1
- package/dist/react/primitives/messagePart/MessagePartInProgress.d.ts +4 -7
- package/dist/react/primitives/messagePart/MessagePartInProgress.d.ts.map +1 -1
- package/dist/react/primitives/part/PartMessages.d.ts +16 -19
- package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
- package/dist/react/primitives/thread/ThreadMessages.d.ts +61 -64
- package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
- package/dist/react/primitives/thread/ThreadSuggestions.d.ts +25 -27
- package/dist/react/primitives/thread/ThreadSuggestions.d.ts.map +1 -1
- package/dist/react/primitives/thread/ThreadSuggestions.js +24 -5
- package/dist/react/primitives/thread/ThreadSuggestions.js.map +1 -1
- package/dist/react/primitives/threadList/ThreadListItems.d.ts +26 -28
- package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
- package/dist/react/primitives/threadListItem/ThreadListItemTitle.d.ts +6 -9
- package/dist/react/primitives/threadListItem/ThreadListItemTitle.d.ts.map +1 -1
- package/dist/react/providers/AttachmentByIndexProvider.d.ts +5 -8
- package/dist/react/providers/AttachmentByIndexProvider.d.ts.map +1 -1
- package/dist/react/providers/ChainOfThoughtByIndicesProvider.d.ts +4 -7
- package/dist/react/providers/ChainOfThoughtByIndicesProvider.d.ts.map +1 -1
- package/dist/react/providers/ChainOfThoughtPartByIndexProvider.d.ts +3 -6
- package/dist/react/providers/ChainOfThoughtPartByIndexProvider.d.ts.map +1 -1
- package/dist/react/providers/MessageByIdProvider.d.ts +3 -6
- package/dist/react/providers/MessageByIdProvider.d.ts.map +1 -1
- package/dist/react/providers/MessageByIndexProvider.d.ts +3 -6
- package/dist/react/providers/MessageByIndexProvider.d.ts.map +1 -1
- package/dist/react/providers/PartByIndexProvider.d.ts +3 -6
- package/dist/react/providers/PartByIndexProvider.d.ts.map +1 -1
- package/dist/react/providers/QueueItemByIndexProvider.d.ts +4 -7
- package/dist/react/providers/QueueItemByIndexProvider.d.ts.map +1 -1
- package/dist/react/providers/ReadonlyThreadProvider.d.ts +7 -10
- package/dist/react/providers/ReadonlyThreadProvider.d.ts.map +1 -1
- package/dist/react/providers/ReadonlyThreadProvider.js.map +1 -1
- package/dist/react/providers/SuggestionByIndexProvider.d.ts +4 -7
- package/dist/react/providers/SuggestionByIndexProvider.d.ts.map +1 -1
- package/dist/react/providers/TextMessagePartProvider.d.ts +4 -7
- package/dist/react/providers/TextMessagePartProvider.d.ts.map +1 -1
- package/dist/react/providers/TextMessagePartProvider.js +5 -1
- package/dist/react/providers/TextMessagePartProvider.js.map +1 -1
- package/dist/react/providers/ThreadListItemByIndexProvider.d.ts +4 -7
- package/dist/react/providers/ThreadListItemByIndexProvider.d.ts.map +1 -1
- package/dist/react/providers/ThreadListItemRuntimeProvider.d.ts +4 -8
- package/dist/react/providers/ThreadListItemRuntimeProvider.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +210 -219
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +70 -46
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +186 -198
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +13 -4
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.d.ts +17 -23
- package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.js +3 -1
- package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
- package/dist/react/runtimes/RuntimeAdapterProvider.d.ts +10 -11
- package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/commandQueue.d.ts +11 -14
- package/dist/react/runtimes/assistant-transport/commandQueue.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts +6 -8
- package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js +9 -2
- package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js.map +1 -1
- package/dist/react/runtimes/assistant-transport/runManager.d.ts +9 -12
- package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/runManager.js +4 -1
- package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -1
- package/dist/react/runtimes/assistant-transport/types.d.ts +123 -118
- package/dist/react/runtimes/assistant-transport/types.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts +7 -10
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +17 -6
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts +3 -6
- package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts +2 -5
- package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +7 -10
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +426 -216
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +15 -18
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +8 -4
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts +146 -125
- package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.js +120 -8
- package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +13 -15
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +5 -7
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/sdkIdentity.d.ts +2 -5
- package/dist/react/runtimes/cloud/sdkIdentity.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/sdkIdentity.js +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +3 -6
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts +9 -11
- package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/createMessageConverter.d.ts +15 -19
- package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
- package/dist/react/runtimes/createMessageConverter.js.map +1 -1
- package/dist/react/runtimes/createRuntimeExtras.d.ts +14 -17
- package/dist/react/runtimes/createRuntimeExtras.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.d.ts +17 -21
- package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
- package/dist/react/runtimes/external-store-history-copy.d.ts +25 -0
- package/dist/react/runtimes/external-store-history-copy.d.ts.map +1 -0
- package/dist/react/runtimes/external-store-history-copy.js +148 -0
- package/dist/react/runtimes/external-store-history-copy.js.map +1 -0
- package/dist/react/runtimes/useExternalStoreRuntime.d.ts +3 -6
- package/dist/react/runtimes/useExternalStoreRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/useExternalStoreRuntime.js +72 -35
- package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
- package/dist/react/runtimes/useExternalStoreSharedOptions.d.ts +2 -5
- package/dist/react/runtimes/useExternalStoreSharedOptions.d.ts.map +1 -1
- package/dist/react/runtimes/useLocalRuntime.d.ts +29 -41
- package/dist/react/runtimes/useLocalRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/useLocalRuntime.js +142 -34
- package/dist/react/runtimes/useLocalRuntime.js.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts +3 -6
- package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.js +2 -2
- package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
- package/dist/react/runtimes/useRuntimeAdapters.d.ts +5 -7
- package/dist/react/runtimes/useRuntimeAdapters.d.ts.map +1 -1
- package/dist/react/runtimes/useStreamingTiming.d.ts +4 -6
- package/dist/react/runtimes/useStreamingTiming.d.ts.map +1 -1
- package/dist/react/types/MessagePartComponentTypes.d.ts +72 -70
- package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
- package/dist/react/types/scopes/dataRenderers.d.ts +11 -15
- package/dist/react/types/scopes/dataRenderers.d.ts.map +1 -1
- package/dist/react/types/scopes/interactables.d.ts +60 -60
- package/dist/react/types/scopes/interactables.d.ts.map +1 -1
- package/dist/react/types/scopes/tools.d.ts +21 -24
- package/dist/react/types/scopes/tools.d.ts.map +1 -1
- package/dist/react/types/store-augmentation.d.ts +10 -11
- package/dist/react/types/store-augmentation.d.ts.map +1 -1
- package/dist/react/utils/getMessageQuote.d.ts +6 -8
- package/dist/react/utils/getMessageQuote.d.ts.map +1 -1
- package/dist/react/utils/groupParts.d.ts +35 -37
- package/dist/react/utils/groupParts.d.ts.map +1 -1
- package/dist/react/utils/useJSONEqual.d.ts +3 -6
- package/dist/react/utils/useJSONEqual.d.ts.map +1 -1
- package/dist/react/utils/useJSONSchemaDependency.d.ts +3 -6
- package/dist/react/utils/useJSONSchemaDependency.d.ts.map +1 -1
- package/dist/runtime/api/assistant-runtime.d.ts +28 -31
- package/dist/runtime/api/assistant-runtime.d.ts.map +1 -1
- package/dist/runtime/api/attachment-runtime.d.ts +51 -49
- package/dist/runtime/api/attachment-runtime.d.ts.map +1 -1
- package/dist/runtime/api/attachment-runtime.js.map +1 -1
- package/dist/runtime/api/bindings.d.ts +35 -35
- package/dist/runtime/api/bindings.d.ts.map +1 -1
- package/dist/runtime/api/composer-runtime.d.ts +215 -201
- package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
- package/dist/runtime/api/composer-runtime.js +4 -0
- package/dist/runtime/api/composer-runtime.js.map +1 -1
- package/dist/runtime/api/message-part-runtime.d.ts +31 -30
- package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
- package/dist/runtime/api/message-part-runtime.js +25 -2
- package/dist/runtime/api/message-part-runtime.js.map +1 -1
- package/dist/runtime/api/message-runtime.d.ts +87 -82
- package/dist/runtime/api/message-runtime.d.ts.map +1 -1
- package/dist/runtime/api/message-runtime.js +4 -3
- package/dist/runtime/api/message-runtime.js.map +1 -1
- package/dist/runtime/api/paths.d.ts +54 -57
- package/dist/runtime/api/paths.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.d.ts +70 -72
- package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
- package/dist/runtime/api/thread-list-runtime.d.ts +77 -81
- package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +306 -308
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-runtime.js +2 -1
- package/dist/runtime/api/thread-runtime.js.map +1 -1
- package/dist/runtime/base/base-assistant-runtime-core.d.ts +9 -12
- package/dist/runtime/base/base-assistant-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts +150 -101
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +400 -122
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +121 -104
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +174 -62
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.d.ts +30 -31
- package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.js +21 -2
- package/dist/runtime/base/default-edit-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +28 -31
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.js +23 -2
- package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
- package/dist/runtime/branch/external-thread-branch-adapter.d.ts +15 -18
- package/dist/runtime/branch/external-thread-branch-adapter.d.ts.map +1 -1
- package/dist/runtime/interfaces/assistant-runtime-core.d.ts +8 -11
- package/dist/runtime/interfaces/assistant-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/composer-runtime-core.d.ts +98 -83
- package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +67 -70
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts +225 -211
- package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/internal.d.ts +25 -20
- package/dist/runtime/internal.d.ts.map +1 -0
- package/dist/runtime/queue/external-thread-queue-adapter.d.ts +37 -40
- package/dist/runtime/queue/external-thread-queue-adapter.d.ts.map +1 -1
- package/dist/runtime/queue/message-queue.d.ts +32 -33
- package/dist/runtime/queue/message-queue.d.ts.map +1 -1
- package/dist/runtime/queue/message-queue.js +10 -2
- package/dist/runtime/queue/message-queue.js.map +1 -1
- package/dist/runtime/queue/queue-item.d.ts +7 -10
- package/dist/runtime/queue/queue-item.d.ts.map +1 -1
- package/dist/runtime/utils/abortable-thread-load.d.ts +9 -11
- package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -1
- package/dist/runtime/utils/attachment-add-operations.d.ts +16 -15
- package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -1
- package/dist/runtime/utils/attachment-add-operations.js +24 -2
- package/dist/runtime/utils/attachment-add-operations.js.map +1 -1
- package/dist/runtime/utils/attachment-send-operations.d.ts +12 -0
- package/dist/runtime/utils/attachment-send-operations.d.ts.map +1 -0
- package/dist/runtime/utils/attachment-send-operations.js +34 -0
- package/dist/runtime/utils/attachment-send-operations.js.map +1 -0
- package/dist/runtime/utils/auto-status.d.ts +11 -13
- package/dist/runtime/utils/auto-status.d.ts.map +1 -1
- package/dist/runtime/utils/auto-status.js +3 -5
- package/dist/runtime/utils/auto-status.js.map +1 -1
- package/dist/runtime/utils/chat-model-adapter.d.ts +31 -33
- package/dist/runtime/utils/chat-model-adapter.d.ts.map +1 -1
- package/dist/runtime/utils/external-message-conversion.d.ts +47 -48
- package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
- package/dist/runtime/utils/external-message-conversion.js +6 -5
- package/dist/runtime/utils/external-message-conversion.js.map +1 -1
- package/dist/runtime/utils/external-store-message.d.ts +6 -9
- package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository-session.d.ts +24 -27
- package/dist/runtime/utils/message-repository-session.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.d.ts +52 -54
- package/dist/runtime/utils/message-repository.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.js +8 -7
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtime/utils/pending-tool-calls.d.ts +12 -14
- package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -1
- package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +7 -10
- package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
- package/dist/runtime/utils/runtime-extras-brand.d.ts +6 -9
- package/dist/runtime/utils/runtime-extras-brand.d.ts.map +1 -1
- package/dist/runtime/utils/stored-message-parts.d.ts +21 -0
- package/dist/runtime/utils/stored-message-parts.d.ts.map +1 -0
- package/dist/runtime/utils/stored-message-parts.js +103 -0
- package/dist/runtime/utils/stored-message-parts.js.map +1 -0
- package/dist/runtime/utils/streaming-timing.d.ts +23 -26
- package/dist/runtime/utils/streaming-timing.d.ts.map +1 -1
- package/dist/runtime/utils/thread-message-like.d.ts +38 -34
- package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
- package/dist/runtime/utils/thread-message-like.js +16 -6
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/runtime/utils/thread-runtime-lifecycle.d.ts +5 -7
- package/dist/runtime/utils/thread-runtime-lifecycle.d.ts.map +1 -1
- package/dist/runtime/utils/thread-runtime-lifecycle.js +31 -4
- package/dist/runtime/utils/thread-runtime-lifecycle.js.map +1 -1
- package/dist/runtime/utils/tool-call-tree.d.ts +10 -13
- package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -1
- package/dist/runtime/utils/tool-interactions.d.ts +10 -0
- package/dist/runtime/utils/tool-interactions.d.ts.map +1 -0
- package/dist/runtime/utils/tool-interactions.js +92 -0
- package/dist/runtime/utils/tool-interactions.js.map +1 -0
- package/dist/runtimes/assistant-transport/utils.d.ts +1 -4
- package/dist/runtimes/assistant-transport/utils.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-adapter.d.ts +220 -200
- package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-runtime-core.d.ts +6 -8
- package/dist/runtimes/external-store/external-store-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-runtime-core.js +4 -1
- package/dist/runtimes/external-store/external-store-runtime-core.js.map +1 -1
- package/dist/runtimes/external-store/external-store-shared-options.d.ts +3 -6
- package/dist/runtimes/external-store/external-store-shared-options.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +40 -43
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +2 -2
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +119 -125
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js +71 -18
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/external-store/thread-message-converter.d.ts +5 -8
- package/dist/runtimes/external-store/thread-message-converter.d.ts.map +1 -1
- package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +96 -98
- package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtimes/inert/InertThreadRuntimeCore.js +3 -0
- package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
- package/dist/runtimes/internal.d.ts +20 -16
- package/dist/runtimes/internal.d.ts.map +1 -0
- package/dist/runtimes/local/local-runtime-core.d.ts +7 -10
- package/dist/runtimes/local/local-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-runtime-options.d.ts +41 -44
- package/dist/runtimes/local/local-runtime-options.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-list-runtime-core.d.ts +42 -45
- package/dist/runtimes/local/local-thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts +94 -93
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +220 -29
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/local/should-continue.d.ts +2 -5
- package/dist/runtimes/local/should-continue.d.ts.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +77 -55
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +28 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
- package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts +12 -15
- package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/adapter-changed.d.ts +3 -6
- package/dist/runtimes/remote-thread-list/adapter-changed.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts +2 -5
- package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/optimistic-state.d.ts +34 -31
- package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/optimistic-state.js +8 -3
- package/dist/runtimes/remote-thread-list/optimistic-state.js.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +60 -63
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.js +1 -1
- package/dist/runtimes/remote-thread-list/title-generation.d.ts +31 -33
- package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/title.d.ts +5 -8
- package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +119 -121
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +147 -149
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +22 -7
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
- package/dist/store/clients/chain-of-thought-client.d.ts +8 -11
- package/dist/store/clients/chain-of-thought-client.d.ts.map +1 -1
- package/dist/store/clients/external-thread.d.ts +56 -58
- package/dist/store/clients/external-thread.d.ts.map +1 -1
- package/dist/store/clients/external-thread.js +1270 -741
- package/dist/store/clients/external-thread.js.map +1 -1
- package/dist/store/clients/model-context-client.d.ts +2 -5
- package/dist/store/clients/model-context-client.d.ts.map +1 -1
- package/dist/store/clients/no-op-composer-client.d.ts +3 -6
- package/dist/store/clients/no-op-composer-client.d.ts.map +1 -1
- package/dist/store/clients/no-op-composer-client.js +3 -1
- package/dist/store/clients/no-op-composer-client.js.map +1 -1
- package/dist/store/clients/runtime-adapter.d.ts +2 -5
- package/dist/store/clients/runtime-adapter.d.ts.map +1 -1
- package/dist/store/clients/single-thread-list.d.ts +4 -6
- package/dist/store/clients/single-thread-list.d.ts.map +1 -1
- package/dist/store/clients/submission-message.d.ts +8 -0
- package/dist/store/clients/submission-message.d.ts.map +1 -0
- package/dist/store/clients/submission-message.js +22 -0
- package/dist/store/clients/submission-message.js.map +1 -0
- package/dist/store/clients/suggestions.d.ts +8 -11
- package/dist/store/clients/suggestions.d.ts.map +1 -1
- package/dist/store/clients/thread-message-client.d.ts +12 -12
- package/dist/store/clients/thread-message-client.d.ts.map +1 -1
- package/dist/store/clients/thread-message-client.js +81 -82
- package/dist/store/clients/thread-message-client.js.map +1 -1
- package/dist/store/clients/thread-selection-events.d.ts +1 -4
- package/dist/store/clients/thread-selection-events.d.ts.map +1 -1
- package/dist/store/clients/thread-tasks.d.ts +7 -10
- package/dist/store/clients/thread-tasks.d.ts.map +1 -1
- package/dist/store/env.d.ts +1 -4
- package/dist/store/env.d.ts.map +1 -1
- package/dist/store/index.d.ts +33 -36
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +5 -5
- package/dist/store/internal.d.ts +12 -12
- package/dist/store/internal.d.ts.map +1 -0
- package/dist/store/internal.js +2 -2
- package/dist/store/primitive-predicates.d.ts +14 -16
- package/dist/store/primitive-predicates.d.ts.map +1 -1
- package/dist/store/primitive-predicates.js +10 -5
- package/dist/store/primitive-predicates.js.map +1 -1
- package/dist/store/runtime-clients/attachment-runtime-client.d.ts +4 -7
- package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.d.ts +11 -14
- package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.js +65 -62
- package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/handle-runtime-action.d.ts +1 -4
- package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -1
- package/dist/store/runtime-clients/handle-thread-list-action.d.ts +1 -4
- package/dist/store/runtime-clients/handle-thread-list-action.d.ts.map +1 -1
- package/dist/store/runtime-clients/liveRef.d.ts +2 -5
- package/dist/store/runtime-clients/liveRef.d.ts.map +1 -1
- package/dist/store/runtime-clients/message-part-runtime-client.d.ts +4 -7
- package/dist/store/runtime-clients/message-part-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/message-part-runtime-client.js +26 -16
- package/dist/store/runtime-clients/message-part-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/message-runtime-client.d.ts +10 -11
- package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/message-runtime-client.js +121 -113
- package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts +5 -8
- package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-list-runtime-client.d.ts +6 -9
- package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.d.ts +4 -7
- package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.js +221 -158
- package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/useSubscribable.d.ts +3 -6
- package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
- package/dist/store/scope-registration.d.ts +28 -29
- package/dist/store/scope-registration.d.ts.map +1 -1
- package/dist/store/scopes/attachment.d.ts +19 -22
- package/dist/store/scopes/attachment.d.ts.map +1 -1
- package/dist/store/scopes/chain-of-thought.d.ts +33 -35
- package/dist/store/scopes/chain-of-thought.d.ts.map +1 -1
- package/dist/store/scopes/composer.d.ts +147 -135
- package/dist/store/scopes/composer.d.ts.map +1 -1
- package/dist/store/scopes/message.d.ts +119 -112
- package/dist/store/scopes/message.d.ts.map +1 -1
- package/dist/store/scopes/model-context.d.ts +10 -13
- package/dist/store/scopes/model-context.d.ts.map +1 -1
- package/dist/store/scopes/part.d.ts +53 -50
- package/dist/store/scopes/part.d.ts.map +1 -1
- package/dist/store/scopes/queue-item.d.ts +21 -23
- package/dist/store/scopes/queue-item.d.ts.map +1 -1
- package/dist/store/scopes/suggestion.d.ts +14 -17
- package/dist/store/scopes/suggestion.d.ts.map +1 -1
- package/dist/store/scopes/suggestions.d.ts +18 -21
- package/dist/store/scopes/suggestions.d.ts.map +1 -1
- package/dist/store/scopes/task.d.ts +27 -30
- package/dist/store/scopes/task.d.ts.map +1 -1
- package/dist/store/scopes/thread-list-item.d.ts +68 -71
- package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
- package/dist/store/scopes/thread.d.ts +197 -200
- package/dist/store/scopes/thread.d.ts.map +1 -1
- package/dist/store/scopes/threads.d.ts +49 -52
- package/dist/store/scopes/threads.d.ts.map +1 -1
- package/dist/subscribable/subscribable.d.ts +73 -69
- package/dist/subscribable/subscribable.d.ts.map +1 -1
- package/dist/subscribable/subscribable.js +35 -7
- package/dist/subscribable/subscribable.js.map +1 -1
- package/dist/types/attachment.d.ts +35 -37
- package/dist/types/attachment.d.ts.map +1 -1
- package/dist/types/augmentations.d.ts +7 -8
- package/dist/types/augmentations.d.ts.map +1 -1
- package/dist/types/directive.d.ts +13 -16
- package/dist/types/directive.d.ts.map +1 -1
- package/dist/types/error.d.ts +15 -17
- package/dist/types/error.d.ts.map +1 -1
- package/dist/types/index.d.ts +7 -7
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/message.d.ts +358 -318
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js.map +1 -1
- package/dist/types/quote.d.ts +3 -6
- package/dist/types/quote.d.ts.map +1 -1
- package/dist/types/trigger.d.ts +10 -13
- package/dist/types/trigger.d.ts.map +1 -1
- package/dist/types/unsubscribe.d.ts +1 -4
- package/dist/types/unsubscribe.d.ts.map +1 -1
- package/dist/utils/abortable-iterable.d.ts +2 -5
- package/dist/utils/abortable-iterable.d.ts.map +1 -1
- package/dist/utils/composite-context-provider.d.ts +9 -13
- package/dist/utils/composite-context-provider.d.ts.map +1 -1
- package/dist/utils/data-url.d.ts +16 -19
- package/dist/utils/data-url.d.ts.map +1 -1
- package/dist/utils/getGroupStatus.d.ts +19 -21
- package/dist/utils/getGroupStatus.d.ts.map +1 -1
- package/dist/utils/id.d.ts +3 -6
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/image-media-type.d.ts +1 -4
- package/dist/utils/image-media-type.d.ts.map +1 -1
- package/dist/utils/invoke-user-callback.d.ts +1 -4
- package/dist/utils/invoke-user-callback.d.ts.map +1 -1
- package/dist/utils/json/is-json-equal.d.ts +1 -4
- package/dist/utils/json/is-json-equal.d.ts.map +1 -1
- package/dist/utils/json/is-json.d.ts +5 -8
- package/dist/utils/json/is-json.d.ts.map +1 -1
- package/dist/utils/json/stable-stringify-tool-args.d.ts +3 -6
- package/dist/utils/json/stable-stringify-tool-args.d.ts.map +1 -1
- package/dist/utils/normalizePartStatus.d.ts +6 -8
- package/dist/utils/normalizePartStatus.d.ts.map +1 -1
- package/dist/utils/normalizePartStatus.js +3 -2
- package/dist/utils/normalizePartStatus.js.map +1 -1
- package/dist/utils/notify-event-listeners.d.ts +2 -4
- package/dist/utils/notify-event-listeners.d.ts.map +1 -1
- package/dist/utils/record.d.ts +1 -4
- package/dist/utils/record.d.ts.map +1 -1
- package/dist/utils/silent-runtime-action.d.ts +2 -5
- package/dist/utils/silent-runtime-action.d.ts.map +1 -1
- package/dist/utils/text.d.ts +2 -5
- package/dist/utils/text.d.ts.map +1 -1
- package/dist/utils/wire-media.d.ts +3 -6
- package/dist/utils/wire-media.d.ts.map +1 -1
- package/package.json +15 -13
- package/src/adapters/attachment.test.ts +98 -1
- package/src/adapters/attachment.ts +77 -20
- package/src/adapters/feedback.ts +1 -0
- package/src/adapters/speech.test.ts +94 -0
- package/src/adapters/speech.ts +16 -2
- package/src/adapters/thread-history.ts +16 -5
- package/src/adapters/voice.test.ts +169 -0
- package/src/adapters/voice.ts +25 -4
- package/src/index.ts +14 -2
- package/src/internal.ts +7 -0
- package/src/model-context/frame/host.test.ts +51 -1
- package/src/model-context/frame/host.ts +3 -1
- package/src/model-context/frame/provider.test.ts +106 -20
- package/src/model-context/frame/provider.ts +8 -1
- package/src/model-context/frame/validate.ts +57 -0
- package/src/model-context/interactable-composer-metadata.test.ts +82 -0
- package/src/model-context/interactable-composer-metadata.ts +45 -2
- package/src/react/RuntimeAdapter.test.ts +4 -1
- package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +711 -0
- package/src/react/adapters/LocalStorageThreadListAdapter.tsx +125 -109
- package/src/react/client/Interactables.test.ts +865 -8
- package/src/react/client/Interactables.ts +325 -45
- package/src/react/client/RemoteThreadList.concurrent.test.tsx +9 -3
- package/src/react/client/RemoteThreadList.initialize-failure.test.ts +124 -0
- package/src/react/client/RemoteThreadList.test.ts +5 -6
- package/src/react/client/RemoteThreadList.ts +6 -0
- package/src/react/client/Tools.ts +2 -3
- package/src/react/client/interactable-model-context.test.ts +56 -13
- package/src/react/client/interactable-model-context.ts +48 -23
- package/src/react/index.ts +2 -2
- package/src/react/interactables-legacy/Interactables.test.ts +221 -4
- package/src/react/interactables-legacy/Interactables.ts +25 -14
- package/src/react/interactables-legacy/interactable-model-context.ts +26 -24
- package/src/react/interactables-legacy/useAssistantInteractable.test.tsx +40 -22
- package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +22 -2
- package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +63 -11
- package/src/react/model-context/useInteractable.test.tsx +56 -29
- package/src/react/primitive-hooks/useActionBarCopy.test.ts +5 -5
- package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +33 -0
- package/src/react/primitive-hooks/useSuggestionTrigger.ts +7 -4
- package/src/react/primitives/generativeUI/GenerativeUI.test.tsx +170 -0
- package/src/react/primitives/generativeUI/GenerativeUI.tsx +43 -28
- package/src/react/primitives/message/MessageAttachments.test.tsx +2 -6
- package/src/react/primitives/message/MessageAttachments.tsx +14 -8
- package/src/react/primitives/message/MessageGroupedParts.test.tsx +155 -8
- package/src/react/primitives/message/MessageGroupedParts.tsx +19 -6
- package/src/react/primitives/message/MessageParts.test.tsx +84 -0
- package/src/react/primitives/message/MessageParts.tool-interaction.test.tsx +69 -0
- package/src/react/primitives/message/MessageParts.tsx +29 -8
- package/src/react/primitives/thread/ThreadMessages.test.tsx +25 -7
- package/src/react/primitives/thread/ThreadSuggestions.test.tsx +97 -0
- package/src/react/primitives/thread/ThreadSuggestions.tsx +29 -6
- package/src/react/providers/ReadonlyThreadProvider.tsx +2 -2
- package/src/react/providers/TextMessagePartProvider.tsx +3 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.layout.test.tsx +62 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.retry.test.tsx +2 -2
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +93 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +181 -21
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.title.test.tsx +62 -17
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +112 -77
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +19 -3
- package/src/react/runtimes/RemoteThreadResource.ts +5 -2
- package/src/react/runtimes/assistant-transport/replayBoundaryStream.test.ts +3 -1
- package/src/react/runtimes/assistant-transport/replayBoundaryStream.ts +10 -2
- package/src/react/runtimes/assistant-transport/runManager.ts +3 -0
- package/src/react/runtimes/assistant-transport/transport-parentid.test.tsx +57 -0
- package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +73 -46
- package/src/react/runtimes/assistant-transport/transport-thread-id.test.tsx +571 -0
- package/src/react/runtimes/assistant-transport/types.ts +9 -1
- package/src/react/runtimes/assistant-transport/useAssistantTransport.spec.md +10 -0
- package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +108 -1
- package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +33 -9
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.copy.integration.test.tsx +289 -0
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.copy.test.tsx +471 -0
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.engagement.test.tsx +330 -0
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +78 -2
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +463 -2
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +595 -227
- package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +33 -4
- package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +7 -3
- package/src/react/runtimes/cloud/auiV0.ts +275 -6
- package/src/react/runtimes/cloud/auiV0DecodeSafely.test.ts +873 -0
- package/src/react/runtimes/createMessageConverter.ts +9 -3
- package/src/react/runtimes/external-message-converter.test.ts +7 -3
- package/src/react/runtimes/external-store-history-copy.ts +223 -0
- package/src/react/runtimes/remote-thread-hook-on-tap.test.ts +2 -1
- package/src/react/runtimes/useExternalStoreRuntime.history-copy.test.tsx +483 -0
- package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +84 -3
- package/src/react/runtimes/useExternalStoreRuntime.suggestions.test.tsx +6 -2
- package/src/react/runtimes/useExternalStoreRuntime.ts +25 -5
- package/src/react/runtimes/useLocalRuntime.test.tsx +145 -1
- package/src/react/runtimes/useLocalRuntime.ts +8 -5
- package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +84 -4
- package/src/react/runtimes/useRemoteThreadListRuntime.ts +5 -1
- package/src/react/types/MessagePartComponentTypes.ts +10 -0
- package/src/react/types/scopes/interactables.ts +3 -0
- package/src/runtime/api/attachment-runtime.ts +10 -5
- package/src/runtime/api/bindings.ts +6 -1
- package/src/runtime/api/composer-runtime.ts +29 -6
- package/src/runtime/api/message-part-runtime.test.ts +215 -0
- package/src/runtime/api/message-part-runtime.ts +44 -1
- package/src/runtime/api/message-runtime.test.ts +112 -2
- package/src/runtime/api/message-runtime.ts +25 -7
- package/src/runtime/api/thread-list-item-runtime.ts +5 -5
- package/src/runtime/api/thread-list-runtime.ts +3 -3
- package/src/runtime/api/thread-runtime.test.ts +59 -0
- package/src/runtime/api/thread-runtime.ts +21 -11
- package/src/runtime/base/base-composer-runtime-core.ts +548 -153
- package/src/runtime/base/base-thread-runtime-core.test.ts +1614 -136
- package/src/runtime/base/base-thread-runtime-core.ts +236 -46
- package/src/runtime/base/default-edit-composer-runtime-core.ts +24 -2
- package/src/runtime/base/default-thread-composer-runtime-core.ts +33 -3
- package/src/runtime/interfaces/composer-runtime-core.ts +19 -0
- package/src/runtime/interfaces/thread-runtime-core.ts +24 -1
- package/src/runtime/queue/message-queue.ts +11 -1
- package/src/runtime/utils/attachment-add-operations.ts +45 -2
- package/src/runtime/utils/attachment-send-operations.ts +54 -0
- package/src/runtime/utils/auto-status.test.ts +54 -0
- package/src/runtime/utils/auto-status.ts +3 -10
- package/src/runtime/utils/external-message-conversion.test.ts +91 -0
- package/src/runtime/utils/external-message-conversion.ts +9 -6
- package/src/runtime/utils/message-repository.test.ts +69 -1
- package/src/runtime/utils/message-repository.ts +14 -7
- package/src/runtime/utils/stored-message-parts.ts +216 -0
- package/src/runtime/utils/thread-message-like.test.ts +58 -0
- package/src/runtime/utils/thread-message-like.ts +79 -48
- package/src/runtime/utils/thread-runtime-lifecycle.test.ts +67 -0
- package/src/runtime/utils/thread-runtime-lifecycle.ts +42 -8
- package/src/runtime/utils/tool-interactions.test.ts +193 -0
- package/src/runtime/utils/tool-interactions.ts +147 -0
- package/src/runtimes/external-store/external-store-adapter.ts +25 -0
- package/src/runtimes/external-store/external-store-runtime-core.ts +10 -2
- package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +2 -2
- package/src/runtimes/external-store/external-store-thread-runtime-core.test.ts +49 -0
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +114 -32
- package/src/runtimes/inert/InertThreadRuntimeCore.test.ts +127 -56
- package/src/runtimes/inert/InertThreadRuntimeCore.ts +10 -1
- package/src/runtimes/local/local-thread-runtime-core.edit-cancellation.test.ts +70 -0
- package/src/runtimes/local/local-thread-runtime-core.test.ts +1866 -169
- package/src/runtimes/local/local-thread-runtime-core.ts +365 -40
- package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +53 -1
- package/src/runtimes/remote-thread-list/optimistic-state.ts +8 -3
- package/src/runtimes/tool-invocations/EDGE_CASES.md +19 -4
- package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +392 -39
- package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +31 -8
- package/src/store/clients/external-thread.test.ts +145 -0
- package/src/store/clients/external-thread.ts +669 -144
- package/src/store/clients/model-context-client.test.ts +8 -8
- package/src/store/clients/no-op-composer-client.ts +2 -0
- package/src/store/clients/submission-message.ts +27 -0
- package/src/store/clients/thread-message-client.test.ts +39 -0
- package/src/store/clients/thread-message-client.ts +13 -6
- package/src/store/index.ts +2 -2
- package/src/store/internal.ts +1 -0
- package/src/store/primitive-predicates.test.ts +119 -0
- package/src/store/primitive-predicates.ts +19 -4
- package/src/store/runtime-clients/composer-runtime-client.ts +4 -3
- package/src/store/runtime-clients/message-part-runtime-client.test.ts +42 -0
- package/src/store/runtime-clients/message-part-runtime-client.ts +4 -0
- package/src/store/runtime-clients/message-runtime-client.ts +5 -0
- package/src/store/runtime-clients/thread-runtime-client.test.tsx +243 -0
- package/src/store/runtime-clients/thread-runtime-client.ts +46 -9
- package/src/store/scopes/composer.ts +22 -4
- package/src/store/scopes/message.ts +12 -1
- package/src/store/scopes/part.ts +8 -0
- package/src/subscribable/subscribable.test.ts +205 -3
- package/src/subscribable/subscribable.ts +49 -7
- package/src/tests/RemoteThreadListThreadListRuntimeCore-errors.test.ts +95 -1
- package/src/tests/attachment-adapters.test.ts +16 -1
- package/src/tests/auiV0Encode.test.ts +412 -1
- package/src/tests/base-composer-runtime-core-addAttachment.test.ts +6 -6
- package/src/tests/base-composer-runtime-core-send.test.ts +722 -29
- package/src/tests/base-composer-runtime-core.test.ts +212 -5
- package/src/tests/composer-can-cancel.test.ts +35 -6
- package/src/tests/composer-can-send.test.ts +120 -1
- package/src/tests/composer-submission.test.tsx +796 -0
- package/src/tests/default-edit-composer-runtime-core.test.ts +149 -2
- package/src/tests/external-store-thread-list-runtime-core.test.ts +111 -0
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +810 -28
- package/src/tests/external-store-thread-runtime-core.test.ts +217 -0
- package/src/tests/external-thread-attachments.test.tsx +763 -26
- package/src/tests/external-thread-feedback.test.tsx +20 -7
- package/src/tests/external-thread-isLast.test.tsx +69 -0
- package/src/tests/external-thread-parity.test.tsx +1 -1
- package/src/tests/get-thread-state-isRunning.test.ts +2 -2
- package/src/tests/message-queue.test.ts +45 -10
- package/src/tests/remote-thread-list-adapter-switch.test.ts +45 -1
- package/src/tests/thread-switch-events.test.tsx +11 -7
- package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +2 -1
- package/src/types/message.test.ts +19 -0
- package/src/types/message.ts +65 -10
- package/src/utils/normalizePartStatus.ts +14 -1
- package/dist/tests/remote-thread-list-test-helpers.d.ts +0 -16
- package/dist/tests/remote-thread-list-test-helpers.d.ts.map +0 -1
- package/dist/tests/remote-thread-list-test-helpers.js +0 -59
- package/dist/tests/remote-thread-list-test-helpers.js.map +0 -1
|
@@ -5,7 +5,12 @@ import type {
|
|
|
5
5
|
ChatModelRunOptions,
|
|
6
6
|
ChatModelRunResult,
|
|
7
7
|
} from "../../runtime/utils/chat-model-adapter";
|
|
8
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
AppendMessage,
|
|
10
|
+
ThreadMessage,
|
|
11
|
+
ToolCallMessagePart,
|
|
12
|
+
} from "../../types/message";
|
|
13
|
+
import type { ThreadHistoryAdapter } from "../../adapters/thread-history";
|
|
9
14
|
import type { LocalRuntimeOptionsBase } from "./local-runtime-options";
|
|
10
15
|
import {
|
|
11
16
|
ExportedMessageRepository,
|
|
@@ -150,260 +155,1636 @@ describe("LocalThreadRuntimeCore events", () => {
|
|
|
150
155
|
});
|
|
151
156
|
|
|
152
157
|
describe("LocalThreadRuntimeCore history persistence", () => {
|
|
153
|
-
it("
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
}));
|
|
158
|
-
const append = vi.fn(async (item: ExportedMessageRepositoryItem) => {
|
|
159
|
-
if (item.message.role === "user") throw persistenceError;
|
|
158
|
+
it("keeps streaming after feedback arrives and persists the completed message", async () => {
|
|
159
|
+
let releaseStream!: () => void;
|
|
160
|
+
const gate = new Promise<void>((resolve) => {
|
|
161
|
+
releaseStream = resolve;
|
|
160
162
|
});
|
|
163
|
+
const appendHistory = vi.fn(
|
|
164
|
+
async (_item: ExportedMessageRepositoryItem) => {},
|
|
165
|
+
);
|
|
161
166
|
const thread = createThread(
|
|
162
|
-
{
|
|
167
|
+
{
|
|
168
|
+
async *run() {
|
|
169
|
+
yield { content: [{ type: "text", text: "a" }] };
|
|
170
|
+
await gate;
|
|
171
|
+
yield { content: [{ type: "text", text: "ab" }] };
|
|
172
|
+
},
|
|
173
|
+
},
|
|
163
174
|
{
|
|
164
175
|
history: {
|
|
165
176
|
async load() {
|
|
166
177
|
return { messages: [] };
|
|
167
178
|
},
|
|
168
|
-
append,
|
|
179
|
+
append: appendHistory,
|
|
180
|
+
async update() {},
|
|
169
181
|
},
|
|
170
182
|
},
|
|
171
183
|
);
|
|
172
184
|
|
|
173
|
-
|
|
174
|
-
|
|
185
|
+
const send = thread.append(userMessage("hello"));
|
|
186
|
+
await vi.waitFor(() =>
|
|
187
|
+
expect(thread.messages.at(-1)?.content).toHaveLength(1),
|
|
175
188
|
);
|
|
189
|
+
const messageId = thread.messages.at(-1)!.id;
|
|
190
|
+
thread.submitFeedback({ messageId, type: "positive" });
|
|
191
|
+
releaseStream();
|
|
192
|
+
await send;
|
|
176
193
|
|
|
177
|
-
|
|
178
|
-
expect(
|
|
194
|
+
const assistant = thread.messages.at(-1);
|
|
195
|
+
expect(assistant?.status?.type).toBe("complete");
|
|
196
|
+
expect(assistant?.content).toEqual([{ type: "text", text: "ab" }]);
|
|
197
|
+
expect(assistant?.metadata.submittedFeedback).toEqual({
|
|
198
|
+
type: "positive",
|
|
199
|
+
});
|
|
200
|
+
expect(appendHistory.mock.calls.at(-1)?.[0].message).toEqual(assistant);
|
|
179
201
|
});
|
|
180
202
|
|
|
181
|
-
it("
|
|
182
|
-
|
|
183
|
-
const
|
|
203
|
+
it("persists the completed message when feedback lands before the adapter returns", async () => {
|
|
204
|
+
let releaseTeardown!: () => void;
|
|
205
|
+
const teardown = new Promise<void>((resolve) => {
|
|
206
|
+
releaseTeardown = resolve;
|
|
207
|
+
});
|
|
208
|
+
const appendHistory = vi.fn(
|
|
209
|
+
async (_item: ExportedMessageRepositoryItem) => {},
|
|
210
|
+
);
|
|
184
211
|
const thread = createThread(
|
|
185
|
-
{
|
|
212
|
+
{
|
|
213
|
+
async *run() {
|
|
214
|
+
yield {
|
|
215
|
+
content: [{ type: "text", text: "done" }],
|
|
216
|
+
status: { type: "complete", reason: "stop" },
|
|
217
|
+
} satisfies ChatModelRunResult;
|
|
218
|
+
await teardown;
|
|
219
|
+
},
|
|
220
|
+
},
|
|
186
221
|
{
|
|
187
222
|
history: {
|
|
188
223
|
async load() {
|
|
189
224
|
return { messages: [] };
|
|
190
225
|
},
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
},
|
|
226
|
+
append: appendHistory,
|
|
227
|
+
async update() {},
|
|
194
228
|
},
|
|
195
229
|
},
|
|
196
230
|
);
|
|
197
231
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
232
|
+
const send = thread.append(userMessage("hello"));
|
|
233
|
+
await vi.waitFor(() =>
|
|
234
|
+
expect(thread.messages.at(-1)?.content).toEqual([
|
|
235
|
+
{ type: "text", text: "done" },
|
|
236
|
+
]),
|
|
237
|
+
);
|
|
238
|
+
thread.submitFeedback({
|
|
239
|
+
messageId: thread.messages.at(-1)!.id,
|
|
240
|
+
type: "positive",
|
|
241
|
+
});
|
|
242
|
+
releaseTeardown();
|
|
243
|
+
await send;
|
|
201
244
|
|
|
202
|
-
|
|
245
|
+
const assistant = thread.messages.at(-1);
|
|
246
|
+
expect(assistant?.metadata.submittedFeedback).toEqual({
|
|
247
|
+
type: "positive",
|
|
248
|
+
});
|
|
249
|
+
expect(appendHistory.mock.calls.at(-1)?.[0].message).toEqual(assistant);
|
|
203
250
|
});
|
|
204
251
|
|
|
205
|
-
it("
|
|
206
|
-
|
|
207
|
-
|
|
252
|
+
it.each(["feedback", "tool result"])(
|
|
253
|
+
"keeps streaming when %s arrives first and the other writer follows",
|
|
254
|
+
async (first) => {
|
|
255
|
+
let releaseStream!: () => void;
|
|
256
|
+
const gate = new Promise<void>((resolve) => {
|
|
257
|
+
releaseStream = resolve;
|
|
258
|
+
});
|
|
259
|
+
const thread = createThread({
|
|
260
|
+
async *run() {
|
|
261
|
+
yield { content: [toolCallPart("lookup_weather")] };
|
|
262
|
+
await gate;
|
|
263
|
+
yield {
|
|
264
|
+
content: [
|
|
265
|
+
toolCallPart("lookup_weather"),
|
|
266
|
+
{ type: "text", text: "It is sunny." },
|
|
267
|
+
],
|
|
268
|
+
} satisfies ChatModelRunResult;
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const send = thread.append(userMessage("weather"));
|
|
273
|
+
await vi.waitFor(() =>
|
|
274
|
+
expect(thread.messages.at(-1)?.content).toHaveLength(1),
|
|
275
|
+
);
|
|
276
|
+
const messageId = thread.messages.at(-1)!.id;
|
|
277
|
+
const submitFeedback = () =>
|
|
278
|
+
thread.submitFeedback({ messageId, type: "positive" });
|
|
279
|
+
const addToolResult = () =>
|
|
280
|
+
thread.addToolResult({
|
|
281
|
+
messageId,
|
|
282
|
+
toolCallId: "call-lookup_weather",
|
|
283
|
+
toolName: "lookup_weather",
|
|
284
|
+
result: { temperature: 21 },
|
|
285
|
+
isError: false,
|
|
286
|
+
});
|
|
287
|
+
if (first === "feedback") {
|
|
288
|
+
submitFeedback();
|
|
289
|
+
addToolResult();
|
|
290
|
+
} else {
|
|
291
|
+
addToolResult();
|
|
292
|
+
submitFeedback();
|
|
293
|
+
}
|
|
294
|
+
releaseStream();
|
|
295
|
+
await send;
|
|
296
|
+
|
|
297
|
+
const assistant = thread.messages.at(-1);
|
|
298
|
+
expect(assistant?.status?.type).toBe("complete");
|
|
299
|
+
expect(assistant?.content).toEqual([
|
|
300
|
+
expect.objectContaining({ result: { temperature: 21 } }),
|
|
301
|
+
{ type: "text", text: "It is sunny." },
|
|
302
|
+
]);
|
|
303
|
+
expect(assistant?.metadata.submittedFeedback).toEqual({
|
|
304
|
+
type: "positive",
|
|
305
|
+
});
|
|
306
|
+
},
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
it("keeps streaming after a tool result arrives and persists the completed message", async () => {
|
|
310
|
+
let releaseStream!: () => void;
|
|
311
|
+
const gate = new Promise<void>((resolve) => {
|
|
312
|
+
releaseStream = resolve;
|
|
313
|
+
});
|
|
314
|
+
const appendHistory = vi.fn(
|
|
315
|
+
async (_item: ExportedMessageRepositoryItem) => {},
|
|
316
|
+
);
|
|
208
317
|
const thread = createThread(
|
|
209
|
-
{
|
|
318
|
+
{
|
|
319
|
+
async *run() {
|
|
320
|
+
yield { content: [toolCallPart("lookup_weather")] };
|
|
321
|
+
await gate;
|
|
322
|
+
yield {
|
|
323
|
+
content: [
|
|
324
|
+
{
|
|
325
|
+
...toolCallPart("lookup_weather"),
|
|
326
|
+
result: { temperature: 20 },
|
|
327
|
+
isPreliminary: true,
|
|
328
|
+
artifact: { preview: true },
|
|
329
|
+
modelContent: [{ type: "text", text: "preview" }],
|
|
330
|
+
},
|
|
331
|
+
{ type: "text", text: "It is sunny." },
|
|
332
|
+
],
|
|
333
|
+
} satisfies ChatModelRunResult;
|
|
334
|
+
},
|
|
335
|
+
},
|
|
210
336
|
{
|
|
211
337
|
history: {
|
|
212
338
|
async load() {
|
|
213
339
|
return { messages: [] };
|
|
214
340
|
},
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
},
|
|
341
|
+
append: appendHistory,
|
|
342
|
+
async update() {},
|
|
218
343
|
},
|
|
219
344
|
},
|
|
220
345
|
);
|
|
221
|
-
|
|
222
|
-
|
|
346
|
+
|
|
347
|
+
const send = thread.append(userMessage("weather"));
|
|
348
|
+
await vi.waitFor(() =>
|
|
349
|
+
expect(thread.messages.at(-1)?.content).toHaveLength(1),
|
|
350
|
+
);
|
|
351
|
+
const messageId = thread.messages.at(-1)!.id;
|
|
352
|
+
thread.addToolResult({
|
|
353
|
+
messageId,
|
|
354
|
+
toolCallId: "call-lookup_weather",
|
|
355
|
+
toolName: "lookup_weather",
|
|
356
|
+
result: { temperature: 21 },
|
|
357
|
+
isError: false,
|
|
223
358
|
});
|
|
359
|
+
releaseStream();
|
|
360
|
+
await send;
|
|
224
361
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
).
|
|
228
|
-
|
|
362
|
+
const assistant = thread.messages.at(-1);
|
|
363
|
+
expect(assistant?.status?.type).toBe("complete");
|
|
364
|
+
expect(assistant?.content).toEqual([
|
|
365
|
+
expect.objectContaining({ result: { temperature: 21 } }),
|
|
366
|
+
{ type: "text", text: "It is sunny." },
|
|
367
|
+
]);
|
|
368
|
+
expect(assistant?.content[0]).not.toHaveProperty("artifact");
|
|
369
|
+
expect(assistant?.content[0]).not.toHaveProperty("modelContent");
|
|
370
|
+
expect(appendHistory.mock.calls.at(-1)?.[0].message).toEqual(assistant);
|
|
229
371
|
});
|
|
230
|
-
});
|
|
231
372
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
373
|
+
it("does not settle a preliminary tool result from a later stream chunk", async () => {
|
|
374
|
+
const thread = createThread(
|
|
375
|
+
{
|
|
376
|
+
async *run() {
|
|
377
|
+
yield {
|
|
378
|
+
content: [
|
|
379
|
+
{
|
|
380
|
+
...toolCallPart("lookup_weather"),
|
|
381
|
+
result: { temperature: 20 },
|
|
382
|
+
isPreliminary: true,
|
|
383
|
+
},
|
|
384
|
+
],
|
|
385
|
+
};
|
|
386
|
+
yield { content: [toolCallPart("lookup_weather")] };
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
{ maxSteps: 1 },
|
|
390
|
+
);
|
|
243
391
|
|
|
244
|
-
|
|
245
|
-
await Promise.resolve();
|
|
246
|
-
thread.detach();
|
|
247
|
-
resolveInitialization();
|
|
392
|
+
await thread.append(userMessage("weather"));
|
|
248
393
|
|
|
249
|
-
|
|
250
|
-
expect(
|
|
251
|
-
|
|
394
|
+
expect(thread.messages.at(-1)?.content[0]).not.toHaveProperty("result");
|
|
395
|
+
expect(thread.messages.at(-1)?.content[0]).not.toHaveProperty(
|
|
396
|
+
"isPreliminary",
|
|
397
|
+
);
|
|
252
398
|
});
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
describe("LocalThreadRuntimeCore optimistic append", () => {
|
|
256
|
-
it("paints the appended message before initialization resolves", async () => {
|
|
257
|
-
let resolveInitialization!: () => void;
|
|
258
|
-
const initialization = new Promise<void>((resolve) => {
|
|
259
|
-
resolveInitialization = resolve;
|
|
260
|
-
});
|
|
261
|
-
const run = vi.fn(async () => ({
|
|
262
|
-
content: [{ type: "text" as const, text: "done" }],
|
|
263
|
-
}));
|
|
264
|
-
const thread = createThread({ run });
|
|
265
|
-
thread.__internal_setGetInitializePromise(() => initialization);
|
|
266
|
-
const onUpdate = vi.fn();
|
|
267
|
-
thread.subscribe(onUpdate);
|
|
268
399
|
|
|
269
|
-
|
|
270
|
-
|
|
400
|
+
it.each([false, true])(
|
|
401
|
+
"does not carry adapter-only results into a later snapshot (preliminary: %s)",
|
|
402
|
+
async (preliminary) => {
|
|
403
|
+
const nextPart = {
|
|
404
|
+
...toolCallPart("lookup_weather"),
|
|
405
|
+
...(preliminary && {
|
|
406
|
+
result: "preview",
|
|
407
|
+
isPreliminary: true,
|
|
408
|
+
artifact: { preview: true },
|
|
409
|
+
modelContent: [{ type: "text" as const, text: "preview" }],
|
|
410
|
+
}),
|
|
411
|
+
};
|
|
412
|
+
const thread = createThread({
|
|
413
|
+
async *run() {
|
|
414
|
+
yield {
|
|
415
|
+
content: [{ ...toolCallPart("lookup_weather"), result: "first" }],
|
|
416
|
+
};
|
|
417
|
+
yield { content: [nextPart] };
|
|
418
|
+
},
|
|
419
|
+
});
|
|
271
420
|
|
|
272
|
-
|
|
273
|
-
expect(thread.messages[0]?.role).toBe("user");
|
|
274
|
-
expect(onUpdate).toHaveBeenCalled();
|
|
275
|
-
expect(run).not.toHaveBeenCalled();
|
|
421
|
+
await thread.append(userMessage("weather"));
|
|
276
422
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
});
|
|
423
|
+
expect(thread.messages.at(-1)?.content).toEqual([nextPart]);
|
|
424
|
+
},
|
|
425
|
+
);
|
|
281
426
|
|
|
282
|
-
it("
|
|
283
|
-
let
|
|
284
|
-
const
|
|
285
|
-
|
|
427
|
+
it("keeps external results matched to their own tool-call occurrence", async () => {
|
|
428
|
+
let releaseStream!: () => void;
|
|
429
|
+
const gate = new Promise<void>((resolve) => {
|
|
430
|
+
releaseStream = resolve;
|
|
286
431
|
});
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
432
|
+
const thread = createThread(
|
|
433
|
+
{
|
|
434
|
+
async *run() {
|
|
435
|
+
yield {
|
|
436
|
+
content: [
|
|
437
|
+
toolCallPart("lookup_weather"),
|
|
438
|
+
{ ...toolCallPart("lookup_time"), result: "noon" },
|
|
439
|
+
],
|
|
440
|
+
};
|
|
441
|
+
await gate;
|
|
442
|
+
yield {
|
|
443
|
+
content: [
|
|
444
|
+
toolCallPart("lookup_weather"),
|
|
445
|
+
toolCallPart("lookup_weather"),
|
|
446
|
+
toolCallPart("lookup_time"),
|
|
447
|
+
],
|
|
448
|
+
};
|
|
449
|
+
yield {
|
|
450
|
+
content: [
|
|
451
|
+
toolCallPart("lookup_weather"),
|
|
452
|
+
toolCallPart("lookup_weather"),
|
|
453
|
+
toolCallPart("lookup_time"),
|
|
454
|
+
{ type: "text", text: "done" },
|
|
455
|
+
],
|
|
456
|
+
};
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
{ maxSteps: 1 },
|
|
460
|
+
);
|
|
292
461
|
|
|
293
|
-
const
|
|
294
|
-
await
|
|
295
|
-
|
|
296
|
-
|
|
462
|
+
const send = thread.append(userMessage("weather"));
|
|
463
|
+
await vi.waitFor(() =>
|
|
464
|
+
expect(thread.messages.at(-1)?.content).toHaveLength(2),
|
|
465
|
+
);
|
|
466
|
+
thread.addToolResult({
|
|
467
|
+
messageId: thread.messages.at(-1)!.id,
|
|
468
|
+
toolCallId: "call-lookup_weather",
|
|
469
|
+
toolName: "lookup_weather",
|
|
470
|
+
result: "first",
|
|
471
|
+
isError: false,
|
|
472
|
+
});
|
|
473
|
+
releaseStream();
|
|
474
|
+
await send;
|
|
297
475
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
476
|
+
expect(thread.messages.at(-1)?.content[0]).toMatchObject({
|
|
477
|
+
result: "first",
|
|
478
|
+
});
|
|
479
|
+
expect(thread.messages.at(-1)?.content[1]).not.toHaveProperty("result");
|
|
480
|
+
expect(thread.messages.at(-1)?.content[2]).not.toHaveProperty("result");
|
|
301
481
|
});
|
|
302
482
|
|
|
303
|
-
it("
|
|
304
|
-
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
})
|
|
308
|
-
|
|
309
|
-
thread
|
|
310
|
-
|
|
483
|
+
it("keeps an external result on a tool call id reused by a later roundtrip", async () => {
|
|
484
|
+
let releaseStream!: () => void;
|
|
485
|
+
const gate = new Promise<void>((resolve) => {
|
|
486
|
+
releaseStream = resolve;
|
|
487
|
+
});
|
|
488
|
+
let calls = 0;
|
|
489
|
+
const thread = createThread({
|
|
490
|
+
async *run() {
|
|
491
|
+
calls++;
|
|
492
|
+
if (calls === 1) {
|
|
493
|
+
yield {
|
|
494
|
+
content: [{ ...toolCallPart("lookup_weather"), result: "first" }],
|
|
495
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
496
|
+
} satisfies ChatModelRunResult;
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
yield { content: [toolCallPart("lookup_weather")] };
|
|
500
|
+
await gate;
|
|
501
|
+
yield { content: [toolCallPart("lookup_weather")] };
|
|
502
|
+
yield {
|
|
503
|
+
content: [
|
|
504
|
+
toolCallPart("lookup_weather"),
|
|
505
|
+
{ type: "text", text: "done" },
|
|
506
|
+
],
|
|
507
|
+
};
|
|
508
|
+
},
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
const send = thread.append(userMessage("weather"));
|
|
512
|
+
await vi.waitFor(() =>
|
|
513
|
+
expect(thread.messages.at(-1)?.content).toHaveLength(2),
|
|
311
514
|
);
|
|
515
|
+
thread.addToolResult({
|
|
516
|
+
messageId: thread.messages.at(-1)!.id,
|
|
517
|
+
toolCallId: "call-lookup_weather",
|
|
518
|
+
toolName: "lookup_weather",
|
|
519
|
+
result: "second",
|
|
520
|
+
isError: false,
|
|
521
|
+
});
|
|
522
|
+
releaseStream();
|
|
523
|
+
await send;
|
|
312
524
|
|
|
313
|
-
|
|
314
|
-
(
|
|
315
|
-
|
|
525
|
+
expect(thread.messages.at(-1)?.content).toEqual([
|
|
526
|
+
expect.objectContaining({ result: "first" }),
|
|
527
|
+
expect.objectContaining({ result: "second" }),
|
|
528
|
+
{ type: "text", text: "done" },
|
|
529
|
+
]);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it("records an interaction on a settled tool call and persists it", async () => {
|
|
533
|
+
const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
|
|
534
|
+
const interaction = {
|
|
535
|
+
type: "action" as const,
|
|
536
|
+
occurredAt: 1,
|
|
537
|
+
payload: { $input: "confirm" },
|
|
538
|
+
};
|
|
539
|
+
const thread = createThread(
|
|
540
|
+
{
|
|
541
|
+
async run() {
|
|
542
|
+
return { content: [toolCallPart("lookup_weather")] };
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
history: {
|
|
547
|
+
async load() {
|
|
548
|
+
return { messages: [] };
|
|
549
|
+
},
|
|
550
|
+
async append() {},
|
|
551
|
+
update,
|
|
552
|
+
},
|
|
316
553
|
},
|
|
317
|
-
(e: unknown) => e,
|
|
318
554
|
);
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
555
|
+
|
|
556
|
+
await thread.append(userMessage("what is the weather"));
|
|
557
|
+
const assistantMessage = thread.messages.at(-1)!;
|
|
558
|
+
|
|
559
|
+
await thread.unstable_recordToolInteraction({
|
|
560
|
+
messageId: assistantMessage.id,
|
|
561
|
+
toolCallId: "call-lookup_weather",
|
|
562
|
+
interaction,
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
expect(thread.messages.at(-1)?.content[0]).toMatchObject({
|
|
566
|
+
unstable_interactions: { entries: [interaction] },
|
|
567
|
+
});
|
|
568
|
+
await vi.waitFor(() => expect(update).toHaveBeenCalledOnce());
|
|
569
|
+
expect(update.mock.calls[0]?.[0].message.content[0]).toMatchObject({
|
|
570
|
+
unstable_interactions: { entries: [interaction] },
|
|
571
|
+
});
|
|
323
572
|
});
|
|
324
|
-
});
|
|
325
573
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const
|
|
574
|
+
it("keeps a recorded interaction through the next streamed update", async () => {
|
|
575
|
+
let releaseStream!: () => void;
|
|
576
|
+
const gate = new Promise<void>((resolve) => {
|
|
577
|
+
releaseStream = resolve;
|
|
578
|
+
});
|
|
579
|
+
const append = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
|
|
580
|
+
const interaction = {
|
|
581
|
+
type: "human-response" as const,
|
|
582
|
+
occurredAt: 1,
|
|
583
|
+
payload: "confirm",
|
|
584
|
+
};
|
|
585
|
+
const thread = createThread(
|
|
586
|
+
{
|
|
587
|
+
async *run() {
|
|
588
|
+
yield { content: [toolCallPart("lookup_weather")] };
|
|
589
|
+
await gate;
|
|
590
|
+
yield {
|
|
591
|
+
content: [
|
|
592
|
+
toolCallPart("lookup_weather"),
|
|
593
|
+
{ type: "text", text: "It is sunny." },
|
|
594
|
+
],
|
|
595
|
+
};
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
history: {
|
|
600
|
+
async load() {
|
|
601
|
+
return { messages: [] };
|
|
602
|
+
},
|
|
603
|
+
append,
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
const send = thread.append(userMessage("what is the weather"));
|
|
609
|
+
await vi.waitFor(() =>
|
|
610
|
+
expect(thread.messages.at(-1)?.content).toHaveLength(1),
|
|
611
|
+
);
|
|
612
|
+
const messageId = thread.messages.at(-1)!.id;
|
|
613
|
+
|
|
614
|
+
await thread.unstable_recordToolInteraction({
|
|
615
|
+
messageId,
|
|
616
|
+
toolCallId: "call-lookup_weather",
|
|
617
|
+
interaction,
|
|
618
|
+
});
|
|
619
|
+
expect(thread.messages.at(-1)?.content[0]).toMatchObject({
|
|
620
|
+
unstable_interactions: { entries: [interaction] },
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
releaseStream();
|
|
624
|
+
await send;
|
|
625
|
+
|
|
626
|
+
expect(thread.messages.at(-1)?.content[0]).toMatchObject({
|
|
627
|
+
unstable_interactions: { entries: [interaction] },
|
|
628
|
+
});
|
|
629
|
+
expect(append.mock.calls.at(-1)?.[0].message.content[0]).toMatchObject({
|
|
630
|
+
unstable_interactions: { entries: [interaction] },
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it("rejects interactions for unknown messages or tool calls", async () => {
|
|
635
|
+
const thread = createThread({
|
|
636
|
+
async run() {
|
|
637
|
+
return { content: [toolCallPart("lookup_weather")] };
|
|
638
|
+
},
|
|
639
|
+
});
|
|
640
|
+
const interaction = {
|
|
641
|
+
type: "action" as const,
|
|
642
|
+
occurredAt: 1,
|
|
643
|
+
payload: { $input: "confirm" },
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
await thread.append(userMessage("what is the weather"));
|
|
647
|
+
const assistantMessage = thread.messages.at(-1)!;
|
|
648
|
+
|
|
649
|
+
await expect(
|
|
650
|
+
thread.unstable_recordToolInteraction({
|
|
651
|
+
messageId: "missing",
|
|
652
|
+
toolCallId: "call-lookup_weather",
|
|
653
|
+
interaction,
|
|
654
|
+
}),
|
|
655
|
+
).rejects.toThrow("non-existing message");
|
|
656
|
+
await expect(
|
|
657
|
+
thread.unstable_recordToolInteraction({
|
|
658
|
+
messageId: assistantMessage.id,
|
|
659
|
+
toolCallId: "missing",
|
|
660
|
+
interaction,
|
|
661
|
+
}),
|
|
662
|
+
).rejects.toThrow("non-existing tool call");
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it("excludes recorded interactions from model input", async () => {
|
|
666
|
+
const runs: ChatModelRunOptions[] = [];
|
|
667
|
+
const interaction = {
|
|
668
|
+
type: "action" as const,
|
|
669
|
+
occurredAt: 1,
|
|
670
|
+
payload: { $input: "confirm" },
|
|
671
|
+
};
|
|
672
|
+
const nestedMessage: ThreadMessage = {
|
|
673
|
+
id: "nested-message",
|
|
674
|
+
role: "assistant",
|
|
675
|
+
createdAt: new Date(0),
|
|
676
|
+
content: [
|
|
677
|
+
{
|
|
678
|
+
...toolCallPart("nested_weather"),
|
|
679
|
+
unstable_interactions: { entries: [interaction] },
|
|
680
|
+
},
|
|
681
|
+
],
|
|
682
|
+
status: { type: "complete", reason: "stop" },
|
|
683
|
+
metadata: {
|
|
684
|
+
unstable_state: null,
|
|
685
|
+
unstable_annotations: [],
|
|
686
|
+
unstable_data: [],
|
|
687
|
+
steps: [],
|
|
688
|
+
custom: {},
|
|
689
|
+
},
|
|
690
|
+
};
|
|
691
|
+
const thread = createThread({
|
|
692
|
+
async run(options) {
|
|
693
|
+
runs.push(options);
|
|
694
|
+
return runs.length === 1
|
|
695
|
+
? {
|
|
696
|
+
content: [
|
|
697
|
+
{
|
|
698
|
+
...toolCallPart("lookup_weather"),
|
|
699
|
+
messages: [nestedMessage],
|
|
700
|
+
},
|
|
701
|
+
],
|
|
702
|
+
}
|
|
703
|
+
: { content: [{ type: "text", text: "done" }] };
|
|
704
|
+
},
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
await thread.append(userMessage("what is the weather"));
|
|
708
|
+
const assistantMessage = thread.messages.at(-1)!;
|
|
709
|
+
await thread.unstable_recordToolInteraction({
|
|
710
|
+
messageId: assistantMessage.id,
|
|
711
|
+
toolCallId: "call-lookup_weather",
|
|
712
|
+
interaction,
|
|
713
|
+
});
|
|
714
|
+
await thread.append({
|
|
715
|
+
...userMessage("and tomorrow"),
|
|
716
|
+
parentId: assistantMessage.id,
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
const modelToolCall = runs[1]?.messages
|
|
720
|
+
.find((message) => message.role === "assistant")
|
|
721
|
+
?.content.find((part) => part.type === "tool-call");
|
|
722
|
+
expect(modelToolCall).not.toHaveProperty("unstable_interactions");
|
|
723
|
+
expect(modelToolCall?.messages?.[0]?.content[0]).not.toHaveProperty(
|
|
724
|
+
"unstable_interactions",
|
|
725
|
+
);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it("exposes a tool result added mid-run to the adapter before its next chunk", async () => {
|
|
729
|
+
let releaseStream!: () => void;
|
|
730
|
+
const gate = new Promise<void>((resolve) => {
|
|
731
|
+
releaseStream = resolve;
|
|
732
|
+
});
|
|
733
|
+
let seen: unknown;
|
|
734
|
+
const thread = createThread({
|
|
735
|
+
async *run({ unstable_getMessage }) {
|
|
736
|
+
yield { content: [toolCallPart("lookup_weather")] };
|
|
737
|
+
await gate;
|
|
738
|
+
seen = unstable_getMessage().content;
|
|
739
|
+
yield {
|
|
740
|
+
content: [
|
|
741
|
+
toolCallPart("lookup_weather"),
|
|
742
|
+
{ type: "text", text: "done" },
|
|
743
|
+
],
|
|
744
|
+
};
|
|
745
|
+
},
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
const send = thread.append(userMessage("weather"));
|
|
749
|
+
await vi.waitFor(() =>
|
|
750
|
+
expect(thread.messages.at(-1)?.content).toHaveLength(1),
|
|
751
|
+
);
|
|
752
|
+
thread.addToolResult({
|
|
753
|
+
messageId: thread.messages.at(-1)!.id,
|
|
754
|
+
toolCallId: "call-lookup_weather",
|
|
755
|
+
toolName: "lookup_weather",
|
|
756
|
+
result: { temperature: 21 },
|
|
757
|
+
isError: false,
|
|
758
|
+
});
|
|
759
|
+
releaseStream();
|
|
760
|
+
await send;
|
|
761
|
+
|
|
762
|
+
expect(seen).toEqual([
|
|
763
|
+
expect.objectContaining({ result: { temperature: 21 } }),
|
|
764
|
+
]);
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
it("carries multiple external results through a replacement chain", async () => {
|
|
768
|
+
let releaseStream!: () => void;
|
|
769
|
+
const gate = new Promise<void>((resolve) => {
|
|
770
|
+
releaseStream = resolve;
|
|
771
|
+
});
|
|
772
|
+
const thread = createThread({
|
|
773
|
+
async *run() {
|
|
774
|
+
const content = [
|
|
775
|
+
toolCallPart("lookup_weather"),
|
|
776
|
+
toolCallPart("lookup_time"),
|
|
777
|
+
];
|
|
778
|
+
yield { content };
|
|
779
|
+
await gate;
|
|
780
|
+
yield { content: [...content, { type: "text", text: "done" }] };
|
|
781
|
+
},
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
const send = thread.append(userMessage("weather and time"));
|
|
785
|
+
await vi.waitFor(() =>
|
|
786
|
+
expect(thread.messages.at(-1)?.content).toHaveLength(2),
|
|
787
|
+
);
|
|
788
|
+
const messageId = thread.messages.at(-1)!.id;
|
|
789
|
+
for (const toolName of ["lookup_weather", "lookup_time"]) {
|
|
790
|
+
thread.addToolResult({
|
|
791
|
+
messageId,
|
|
792
|
+
toolCallId: `call-${toolName}`,
|
|
793
|
+
toolName,
|
|
794
|
+
result: `${toolName} result`,
|
|
795
|
+
isError: false,
|
|
796
|
+
artifact: { toolName },
|
|
797
|
+
modelContent: [{ type: "text", text: toolName }],
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
releaseStream();
|
|
801
|
+
await send;
|
|
802
|
+
|
|
803
|
+
expect(thread.messages.at(-1)?.content).toEqual([
|
|
804
|
+
...["lookup_weather", "lookup_time"].map((toolName) => ({
|
|
805
|
+
...toolCallPart(toolName),
|
|
806
|
+
result: `${toolName} result`,
|
|
807
|
+
isError: false,
|
|
808
|
+
artifact: { toolName },
|
|
809
|
+
modelContent: [{ type: "text", text: toolName }],
|
|
810
|
+
})),
|
|
811
|
+
{ type: "text", text: "done" },
|
|
812
|
+
]);
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
it("persists a tool result added after a completed message", async () => {
|
|
816
|
+
const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
|
|
817
|
+
const thread = createThread(
|
|
818
|
+
{
|
|
819
|
+
async run() {
|
|
820
|
+
return { content: [toolCallPart("lookup_weather")] };
|
|
821
|
+
},
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
history: {
|
|
825
|
+
async load() {
|
|
826
|
+
return { messages: [] };
|
|
827
|
+
},
|
|
828
|
+
async append() {},
|
|
829
|
+
update,
|
|
830
|
+
},
|
|
831
|
+
},
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
await thread.append(userMessage("what is the weather"));
|
|
835
|
+
const assistantMessage = thread.messages.at(-1)!;
|
|
836
|
+
expect(assistantMessage.status?.type).toBe("complete");
|
|
837
|
+
|
|
838
|
+
thread.addToolResult({
|
|
839
|
+
messageId: assistantMessage.id,
|
|
840
|
+
toolCallId: "call-lookup_weather",
|
|
841
|
+
toolName: "lookup_weather",
|
|
842
|
+
result: { temperature: 21 },
|
|
843
|
+
isError: false,
|
|
844
|
+
});
|
|
845
|
+
await flush();
|
|
846
|
+
|
|
847
|
+
expect(update).toHaveBeenCalledOnce();
|
|
848
|
+
expect(update.mock.calls[0]?.[0].message.content).toEqual([
|
|
849
|
+
expect.objectContaining({
|
|
850
|
+
result: { temperature: 21 },
|
|
851
|
+
isError: false,
|
|
852
|
+
}),
|
|
853
|
+
]);
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
it("persists a tool result added after an incomplete message", async () => {
|
|
857
|
+
const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
|
|
858
|
+
const thread = createThread(
|
|
859
|
+
{
|
|
860
|
+
async run() {
|
|
861
|
+
return {
|
|
862
|
+
content: [toolCallPart("lookup_weather")],
|
|
863
|
+
status: { type: "incomplete", reason: "cancelled" },
|
|
864
|
+
};
|
|
865
|
+
},
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
history: {
|
|
869
|
+
async load() {
|
|
870
|
+
return { messages: [] };
|
|
871
|
+
},
|
|
872
|
+
async append() {},
|
|
873
|
+
update,
|
|
874
|
+
},
|
|
875
|
+
},
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
await thread.append(userMessage("what is the weather"));
|
|
879
|
+
const assistantMessage = thread.messages.at(-1)!;
|
|
880
|
+
expect(assistantMessage.status?.type).toBe("incomplete");
|
|
881
|
+
|
|
882
|
+
thread.addToolResult({
|
|
883
|
+
messageId: assistantMessage.id,
|
|
884
|
+
toolCallId: "call-lookup_weather",
|
|
885
|
+
toolName: "lookup_weather",
|
|
886
|
+
result: { temperature: 21 },
|
|
887
|
+
isError: false,
|
|
888
|
+
});
|
|
889
|
+
await flush();
|
|
890
|
+
|
|
891
|
+
expect(update).toHaveBeenCalledOnce();
|
|
892
|
+
expect(update.mock.calls[0]?.[0].message.content).toEqual([
|
|
893
|
+
expect.objectContaining({ result: { temperature: 21 } }),
|
|
894
|
+
]);
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
it("persists a tool result added after later turns follow its message", async () => {
|
|
898
|
+
const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
|
|
899
|
+
const thread = createThread(
|
|
900
|
+
{
|
|
901
|
+
async run() {
|
|
902
|
+
return { content: [toolCallPart("lookup_weather")] };
|
|
903
|
+
},
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
history: {
|
|
907
|
+
async load() {
|
|
908
|
+
return { messages: [] };
|
|
909
|
+
},
|
|
910
|
+
async append() {},
|
|
911
|
+
update,
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
);
|
|
915
|
+
|
|
916
|
+
await thread.append(userMessage("what is the weather"));
|
|
917
|
+
const [question, answer] = thread.messages;
|
|
918
|
+
await thread.append({
|
|
919
|
+
...userMessage("and tomorrow?"),
|
|
920
|
+
parentId: answer!.id,
|
|
921
|
+
});
|
|
922
|
+
expect(thread.messages).toHaveLength(4);
|
|
923
|
+
|
|
924
|
+
thread.addToolResult({
|
|
925
|
+
messageId: answer!.id,
|
|
926
|
+
toolCallId: "call-lookup_weather",
|
|
927
|
+
toolName: "lookup_weather",
|
|
928
|
+
result: { temperature: 21 },
|
|
929
|
+
isError: false,
|
|
930
|
+
});
|
|
931
|
+
await flush();
|
|
932
|
+
|
|
933
|
+
expect(update).toHaveBeenCalledOnce();
|
|
934
|
+
expect(update.mock.calls[0]?.[0]).toMatchObject({
|
|
935
|
+
parentId: question!.id,
|
|
936
|
+
message: {
|
|
937
|
+
id: answer!.id,
|
|
938
|
+
content: [expect.objectContaining({ result: { temperature: 21 } })],
|
|
939
|
+
},
|
|
940
|
+
});
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
it("writes a result a subscriber adds in response after the late result", async () => {
|
|
944
|
+
const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
|
|
945
|
+
const thread = createThread(
|
|
946
|
+
{
|
|
947
|
+
async run() {
|
|
948
|
+
return {
|
|
949
|
+
content: [
|
|
950
|
+
toolCallPart("lookup_weather"),
|
|
951
|
+
toolCallPart("lookup_time"),
|
|
952
|
+
],
|
|
953
|
+
};
|
|
954
|
+
},
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
history: {
|
|
958
|
+
async load() {
|
|
959
|
+
return { messages: [] };
|
|
960
|
+
},
|
|
961
|
+
async append() {},
|
|
962
|
+
update,
|
|
963
|
+
},
|
|
964
|
+
},
|
|
965
|
+
);
|
|
966
|
+
|
|
967
|
+
await thread.append(userMessage("weather and time"));
|
|
968
|
+
const assistantMessage = thread.messages.at(-1)!;
|
|
969
|
+
expect(assistantMessage.status?.type).toBe("complete");
|
|
970
|
+
|
|
971
|
+
const unsubscribe = thread.subscribe(() => {
|
|
972
|
+
const message = thread.messages.find((m) => m.id === assistantMessage.id);
|
|
973
|
+
const [weather, time] = message?.content ?? [];
|
|
974
|
+
if (
|
|
975
|
+
weather?.type === "tool-call" &&
|
|
976
|
+
weather.result !== undefined &&
|
|
977
|
+
time?.type === "tool-call" &&
|
|
978
|
+
time.result === undefined
|
|
979
|
+
) {
|
|
980
|
+
thread.addToolResult({
|
|
981
|
+
messageId: assistantMessage.id,
|
|
982
|
+
toolCallId: "call-lookup_time",
|
|
983
|
+
toolName: "lookup_time",
|
|
984
|
+
result: { hour: 9 },
|
|
985
|
+
isError: false,
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
thread.addToolResult({
|
|
990
|
+
messageId: assistantMessage.id,
|
|
991
|
+
toolCallId: "call-lookup_weather",
|
|
992
|
+
toolName: "lookup_weather",
|
|
993
|
+
result: { temperature: 21 },
|
|
994
|
+
isError: false,
|
|
995
|
+
});
|
|
996
|
+
unsubscribe();
|
|
997
|
+
await flush();
|
|
998
|
+
|
|
999
|
+
expect(update).toHaveBeenCalledTimes(2);
|
|
1000
|
+
expect(update.mock.calls.at(-1)?.[0].message.content).toEqual([
|
|
1001
|
+
expect.objectContaining({ result: { temperature: 21 } }),
|
|
1002
|
+
expect.objectContaining({ result: { hour: 9 } }),
|
|
1003
|
+
]);
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
it("writes a decision a subscriber adds in response after the one it answers", async () => {
|
|
1007
|
+
const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
|
|
1008
|
+
let runs = 0;
|
|
1009
|
+
const thread = createThread(
|
|
1010
|
+
{
|
|
1011
|
+
async run() {
|
|
1012
|
+
runs++;
|
|
1013
|
+
if (runs > 1) return { content: [{ type: "text", text: "ok" }] };
|
|
1014
|
+
return {
|
|
1015
|
+
content: [
|
|
1016
|
+
{
|
|
1017
|
+
...toolCallPart("send_email", { id: "a1" }),
|
|
1018
|
+
toolCallId: "call-1",
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
...toolCallPart("send_email", { id: "a2" }),
|
|
1022
|
+
toolCallId: "call-2",
|
|
1023
|
+
},
|
|
1024
|
+
],
|
|
1025
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
1026
|
+
};
|
|
1027
|
+
},
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
history: {
|
|
1031
|
+
async load() {
|
|
1032
|
+
return { messages: [] };
|
|
1033
|
+
},
|
|
1034
|
+
async append() {},
|
|
1035
|
+
update,
|
|
1036
|
+
},
|
|
1037
|
+
},
|
|
1038
|
+
);
|
|
1039
|
+
|
|
1040
|
+
await thread.append(userMessage("send two emails"));
|
|
1041
|
+
const paused = thread.messages.at(-1)!;
|
|
1042
|
+
await thread.append({ ...userMessage("and then"), parentId: paused.id });
|
|
1043
|
+
await flush();
|
|
1044
|
+
|
|
1045
|
+
const unsubscribe = thread.subscribe(() => {
|
|
1046
|
+
const message = thread.messages.find((m) => m.id === paused.id);
|
|
1047
|
+
const [first, second] = message?.content ?? [];
|
|
1048
|
+
if (
|
|
1049
|
+
first?.type === "tool-call" &&
|
|
1050
|
+
first.approval?.approved !== undefined &&
|
|
1051
|
+
second?.type === "tool-call" &&
|
|
1052
|
+
second.approval?.approved === undefined
|
|
1053
|
+
) {
|
|
1054
|
+
void thread.respondToToolApproval({ approvalId: "a2", approved: true });
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
void thread.respondToToolApproval({ approvalId: "a1", approved: true });
|
|
1058
|
+
unsubscribe();
|
|
1059
|
+
await flush();
|
|
1060
|
+
|
|
1061
|
+
expect(update).toHaveBeenCalledTimes(2);
|
|
1062
|
+
expect(update.mock.calls.at(-1)?.[0].message.content).toEqual([
|
|
1063
|
+
expect.objectContaining({
|
|
1064
|
+
approval: expect.objectContaining({ approved: true }),
|
|
1065
|
+
}),
|
|
1066
|
+
expect.objectContaining({
|
|
1067
|
+
approval: expect.objectContaining({ approved: true }),
|
|
1068
|
+
}),
|
|
1069
|
+
]);
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
it("does not rewrite a message whose run is still streaming", async () => {
|
|
1073
|
+
const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
|
|
1074
|
+
let release!: () => void;
|
|
1075
|
+
const released = new Promise<void>((resolve) => {
|
|
1076
|
+
release = resolve;
|
|
1077
|
+
});
|
|
1078
|
+
const thread = createThread(
|
|
1079
|
+
{
|
|
1080
|
+
async *run() {
|
|
1081
|
+
yield { content: [toolCallPart("lookup_weather")] };
|
|
1082
|
+
await released;
|
|
1083
|
+
},
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
history: {
|
|
1087
|
+
async load() {
|
|
1088
|
+
return { messages: [] };
|
|
1089
|
+
},
|
|
1090
|
+
async append() {},
|
|
1091
|
+
update,
|
|
1092
|
+
},
|
|
1093
|
+
},
|
|
1094
|
+
);
|
|
1095
|
+
|
|
1096
|
+
const appended = thread.append(userMessage("what is the weather"));
|
|
1097
|
+
await flush();
|
|
1098
|
+
const assistantMessage = thread.messages.at(-1)!;
|
|
1099
|
+
expect(assistantMessage.status?.type).toBe("running");
|
|
1100
|
+
|
|
1101
|
+
thread.addToolResult({
|
|
1102
|
+
messageId: assistantMessage.id,
|
|
1103
|
+
toolCallId: "call-lookup_weather",
|
|
1104
|
+
toolName: "lookup_weather",
|
|
1105
|
+
result: { temperature: 21 },
|
|
1106
|
+
isError: false,
|
|
1107
|
+
});
|
|
1108
|
+
await flush();
|
|
1109
|
+
release();
|
|
1110
|
+
await appended;
|
|
1111
|
+
|
|
1112
|
+
expect(update).not.toHaveBeenCalled();
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
it("persists a final result that replaces a preliminary result", async () => {
|
|
1116
|
+
const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
|
|
1117
|
+
const thread = createThread(
|
|
1118
|
+
{
|
|
1119
|
+
async run() {
|
|
1120
|
+
return {
|
|
1121
|
+
content: [
|
|
1122
|
+
{
|
|
1123
|
+
...toolCallPart("lookup_weather"),
|
|
1124
|
+
result: { temperature: 20 },
|
|
1125
|
+
isPreliminary: true,
|
|
1126
|
+
},
|
|
1127
|
+
],
|
|
1128
|
+
};
|
|
1129
|
+
},
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
history: {
|
|
1133
|
+
async load() {
|
|
1134
|
+
return { messages: [] };
|
|
1135
|
+
},
|
|
1136
|
+
async append() {},
|
|
1137
|
+
update,
|
|
1138
|
+
},
|
|
1139
|
+
},
|
|
1140
|
+
);
|
|
1141
|
+
|
|
1142
|
+
await thread.append(userMessage("what is the weather"));
|
|
1143
|
+
const assistantMessage = thread.messages.at(-1)!;
|
|
1144
|
+
expect(assistantMessage.status?.type).toBe("complete");
|
|
1145
|
+
|
|
1146
|
+
thread.addToolResult({
|
|
1147
|
+
messageId: assistantMessage.id,
|
|
1148
|
+
toolCallId: "call-lookup_weather",
|
|
1149
|
+
toolName: "lookup_weather",
|
|
1150
|
+
result: { temperature: 21 },
|
|
1151
|
+
isError: false,
|
|
1152
|
+
});
|
|
1153
|
+
await flush();
|
|
1154
|
+
|
|
1155
|
+
expect(update).toHaveBeenCalledOnce();
|
|
1156
|
+
const updatedToolCall = update.mock.calls[0]?.[0].message.content[0];
|
|
1157
|
+
expect(updatedToolCall).toMatchObject({ result: { temperature: 21 } });
|
|
1158
|
+
expect(updatedToolCall).not.toHaveProperty("isPreliminary");
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
it("surfaces failed user persistence without abandoning the run", async () => {
|
|
1162
|
+
const persistenceError = new Error("history unavailable");
|
|
1163
|
+
const run = vi.fn(async () => ({
|
|
1164
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
1165
|
+
}));
|
|
1166
|
+
const append = vi.fn(async (item: ExportedMessageRepositoryItem) => {
|
|
1167
|
+
if (item.message.role === "user") throw persistenceError;
|
|
1168
|
+
});
|
|
1169
|
+
const thread = createThread(
|
|
1170
|
+
{ run },
|
|
1171
|
+
{
|
|
1172
|
+
history: {
|
|
1173
|
+
async load() {
|
|
1174
|
+
return { messages: [] };
|
|
1175
|
+
},
|
|
1176
|
+
append,
|
|
1177
|
+
},
|
|
1178
|
+
},
|
|
1179
|
+
);
|
|
1180
|
+
|
|
1181
|
+
await expect(thread.append(userMessage("hello"))).rejects.toBe(
|
|
1182
|
+
persistenceError,
|
|
1183
|
+
);
|
|
1184
|
+
|
|
1185
|
+
expect(append).toHaveBeenCalledTimes(2);
|
|
1186
|
+
expect(run).toHaveBeenCalledOnce();
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
it("surfaces failed persistence for messages that do not start a run", async () => {
|
|
1190
|
+
const persistenceError = new Error("history unavailable");
|
|
1191
|
+
const run = vi.fn();
|
|
1192
|
+
const thread = createThread(
|
|
1193
|
+
{ run },
|
|
1194
|
+
{
|
|
1195
|
+
history: {
|
|
1196
|
+
async load() {
|
|
1197
|
+
return { messages: [] };
|
|
1198
|
+
},
|
|
1199
|
+
async append() {
|
|
1200
|
+
throw persistenceError;
|
|
1201
|
+
},
|
|
1202
|
+
},
|
|
1203
|
+
},
|
|
1204
|
+
);
|
|
1205
|
+
|
|
1206
|
+
await expect(
|
|
1207
|
+
thread.append({ ...userMessage("hello"), startRun: false }),
|
|
1208
|
+
).rejects.toBe(persistenceError);
|
|
1209
|
+
|
|
1210
|
+
expect(run).not.toHaveBeenCalled();
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
it("handles failed persistence before notifying no-run subscribers", async () => {
|
|
1214
|
+
const persistenceError = new Error("history unavailable");
|
|
1215
|
+
const listenerError = new Error("listener unavailable");
|
|
1216
|
+
const thread = createThread(
|
|
1217
|
+
{ run: vi.fn() },
|
|
1218
|
+
{
|
|
1219
|
+
history: {
|
|
1220
|
+
async load() {
|
|
1221
|
+
return { messages: [] };
|
|
1222
|
+
},
|
|
1223
|
+
async append() {
|
|
1224
|
+
throw persistenceError;
|
|
1225
|
+
},
|
|
1226
|
+
},
|
|
1227
|
+
},
|
|
1228
|
+
);
|
|
1229
|
+
thread.subscribe(() => {
|
|
1230
|
+
throw listenerError;
|
|
1231
|
+
});
|
|
1232
|
+
|
|
1233
|
+
await expect(
|
|
1234
|
+
thread.append({ ...userMessage("hello"), startRun: false }),
|
|
1235
|
+
).rejects.toBe(listenerError);
|
|
1236
|
+
await flush();
|
|
1237
|
+
});
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
describe("LocalThreadRuntimeCore - detach", () => {
|
|
1241
|
+
it("drops a pending append when detached", async () => {
|
|
1242
|
+
let resolveInitialization!: () => void;
|
|
1243
|
+
const initialization = new Promise<void>((resolve) => {
|
|
1244
|
+
resolveInitialization = resolve;
|
|
1245
|
+
});
|
|
1246
|
+
const run = vi.fn(async () => ({
|
|
1247
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
1248
|
+
}));
|
|
1249
|
+
const thread = createThread({ run });
|
|
1250
|
+
thread.__internal_setGetInitializePromise(() => initialization);
|
|
1251
|
+
|
|
1252
|
+
const appendPromise = thread.append(userMessage("hello"));
|
|
1253
|
+
await Promise.resolve();
|
|
1254
|
+
thread.detach();
|
|
1255
|
+
resolveInitialization();
|
|
1256
|
+
|
|
1257
|
+
await appendPromise;
|
|
1258
|
+
expect(run).not.toHaveBeenCalled();
|
|
1259
|
+
expect(thread.messages).toEqual([]);
|
|
1260
|
+
});
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
describe("LocalThreadRuntimeCore optimistic append", () => {
|
|
1264
|
+
it("paints the appended message before initialization resolves", async () => {
|
|
1265
|
+
let resolveInitialization!: () => void;
|
|
1266
|
+
const initialization = new Promise<void>((resolve) => {
|
|
1267
|
+
resolveInitialization = resolve;
|
|
1268
|
+
});
|
|
1269
|
+
const run = vi.fn(async () => ({
|
|
1270
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
1271
|
+
}));
|
|
1272
|
+
const thread = createThread({ run });
|
|
1273
|
+
thread.__internal_setGetInitializePromise(() => initialization);
|
|
1274
|
+
const onUpdate = vi.fn();
|
|
1275
|
+
thread.subscribe(onUpdate);
|
|
1276
|
+
|
|
1277
|
+
const appendPromise = thread.append(userMessage("hello"));
|
|
1278
|
+
await Promise.resolve();
|
|
1279
|
+
|
|
1280
|
+
expect(thread.messages).toHaveLength(1);
|
|
1281
|
+
expect(thread.messages[0]?.role).toBe("user");
|
|
1282
|
+
expect(onUpdate).toHaveBeenCalled();
|
|
1283
|
+
expect(run).not.toHaveBeenCalled();
|
|
1284
|
+
|
|
1285
|
+
resolveInitialization();
|
|
1286
|
+
await appendPromise;
|
|
1287
|
+
expect(run).toHaveBeenCalledTimes(1);
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
it("silently drops a detached append even when initialization rejects", async () => {
|
|
1291
|
+
let rejectInitialization!: (error: unknown) => void;
|
|
1292
|
+
const initialization = new Promise<void>((_, reject) => {
|
|
1293
|
+
rejectInitialization = reject;
|
|
1294
|
+
});
|
|
1295
|
+
const run = vi.fn(async () => ({
|
|
1296
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
1297
|
+
}));
|
|
1298
|
+
const thread = createThread({ run });
|
|
1299
|
+
thread.__internal_setGetInitializePromise(() => initialization);
|
|
1300
|
+
|
|
1301
|
+
const appendPromise = thread.append(userMessage("hello"));
|
|
1302
|
+
await Promise.resolve();
|
|
1303
|
+
thread.detach();
|
|
1304
|
+
rejectInitialization(new Error("initialization failed"));
|
|
1305
|
+
|
|
1306
|
+
await expect(appendPromise).resolves.toBeUndefined();
|
|
1307
|
+
expect(thread.messages).toEqual([]);
|
|
1308
|
+
expect(run).not.toHaveBeenCalled();
|
|
1309
|
+
});
|
|
1310
|
+
|
|
1311
|
+
it("rolls the optimistic message back when initialization rejects", async () => {
|
|
1312
|
+
const initializationError = new Error("initialization failed");
|
|
1313
|
+
const run = vi.fn(async () => ({
|
|
1314
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
1315
|
+
}));
|
|
1316
|
+
const thread = createThread({ run });
|
|
1317
|
+
thread.__internal_setGetInitializePromise(() =>
|
|
1318
|
+
Promise.reject(initializationError),
|
|
1319
|
+
);
|
|
1320
|
+
|
|
1321
|
+
const error = await thread.append(userMessage("hello")).then(
|
|
1322
|
+
() => {
|
|
1323
|
+
throw new Error("expected the append to reject");
|
|
1324
|
+
},
|
|
1325
|
+
(e: unknown) => e,
|
|
1326
|
+
);
|
|
1327
|
+
expect(isMessageNotSentError(error)).toBe(true);
|
|
1328
|
+
expect((error as Error).cause).toBe(initializationError);
|
|
1329
|
+
expect(thread.messages).toEqual([]);
|
|
1330
|
+
expect(run).not.toHaveBeenCalled();
|
|
1331
|
+
});
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
describe("LocalThreadRuntimeCore append during a history load", () => {
|
|
1335
|
+
const persistedMessage = {
|
|
1336
|
+
id: "persisted",
|
|
1337
|
+
role: "user" as const,
|
|
1338
|
+
content: [{ type: "text" as const, text: "persisted" }],
|
|
1339
|
+
attachments: [],
|
|
1340
|
+
createdAt: new Date(0),
|
|
1341
|
+
metadata: { custom: {} },
|
|
1342
|
+
};
|
|
1343
|
+
|
|
1344
|
+
const createLoadingThread = (
|
|
1345
|
+
load: ThreadHistoryAdapter["load"],
|
|
1346
|
+
run: ChatModelAdapter["run"] = async () => ({
|
|
1347
|
+
content: [{ type: "text", text: "done" }],
|
|
1348
|
+
}),
|
|
1349
|
+
) => {
|
|
1350
|
+
const appended: ExportedMessageRepositoryItem[] = [];
|
|
1351
|
+
const thread = createThread(
|
|
1352
|
+
{ run },
|
|
1353
|
+
{
|
|
1354
|
+
history: {
|
|
1355
|
+
load,
|
|
1356
|
+
async append(item) {
|
|
1357
|
+
appended.push(item);
|
|
1358
|
+
},
|
|
1359
|
+
},
|
|
1360
|
+
},
|
|
1361
|
+
);
|
|
1362
|
+
return { thread, appended };
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
it("lands a message typed during a load after the imported history", async () => {
|
|
1366
|
+
let releaseLoad!: () => void;
|
|
1367
|
+
const loadBarrier = new Promise<void>((resolve) => {
|
|
1368
|
+
releaseLoad = resolve;
|
|
1369
|
+
});
|
|
1370
|
+
const { thread, appended } = createLoadingThread(() =>
|
|
1371
|
+
loadBarrier.then(() => ({
|
|
1372
|
+
headId: "persisted",
|
|
1373
|
+
messages: [{ parentId: null, message: persistedMessage }],
|
|
1374
|
+
})),
|
|
1375
|
+
);
|
|
1376
|
+
|
|
1377
|
+
thread.__internal_load();
|
|
1378
|
+
expect(thread.isLoading).toBe(true);
|
|
1379
|
+
|
|
1380
|
+
const appendPromise = thread.append(userMessage("typed during load"));
|
|
1381
|
+
releaseLoad();
|
|
1382
|
+
await appendPromise;
|
|
1383
|
+
await flush();
|
|
1384
|
+
|
|
1385
|
+
expect(thread.messages.map((message) => message.content)).toEqual([
|
|
1386
|
+
[{ type: "text", text: "persisted" }],
|
|
1387
|
+
[{ type: "text", text: "typed during load" }],
|
|
1388
|
+
[{ type: "text", text: "done" }],
|
|
1389
|
+
]);
|
|
1390
|
+
expect(appended.map((item) => [item.parentId, item.message.role])).toEqual([
|
|
1391
|
+
["persisted", "user"],
|
|
1392
|
+
[thread.messages[1]!.id, "assistant"],
|
|
1393
|
+
]);
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
// The tail re-point must not move an append that named its parent, or an
|
|
1397
|
+
// edit issued during a load would silently reattach to the imported tail.
|
|
1398
|
+
it("keeps an explicit parent for a message appended during a load", async () => {
|
|
1399
|
+
const olderMessage = {
|
|
1400
|
+
...persistedMessage,
|
|
1401
|
+
id: "older",
|
|
1402
|
+
content: [{ type: "text" as const, text: "older" }],
|
|
1403
|
+
};
|
|
1404
|
+
let releaseLoad!: () => void;
|
|
1405
|
+
const loadBarrier = new Promise<void>((resolve) => {
|
|
1406
|
+
releaseLoad = resolve;
|
|
1407
|
+
});
|
|
1408
|
+
const { thread, appended } = createLoadingThread(() =>
|
|
1409
|
+
loadBarrier.then(() => ({
|
|
1410
|
+
headId: "persisted",
|
|
1411
|
+
messages: [
|
|
1412
|
+
{ parentId: null, message: olderMessage },
|
|
1413
|
+
{ parentId: "older", message: persistedMessage },
|
|
1414
|
+
],
|
|
1415
|
+
})),
|
|
1416
|
+
);
|
|
1417
|
+
|
|
1418
|
+
thread.__internal_load();
|
|
1419
|
+
expect(thread.isLoading).toBe(true);
|
|
1420
|
+
|
|
1421
|
+
const appendPromise = thread.append({
|
|
1422
|
+
...userMessage("branched during load"),
|
|
1423
|
+
parentId: "older",
|
|
1424
|
+
});
|
|
1425
|
+
releaseLoad();
|
|
1426
|
+
await appendPromise;
|
|
1427
|
+
await flush();
|
|
1428
|
+
|
|
1429
|
+
expect(appended.map((item) => [item.parentId, item.message.role])).toEqual([
|
|
1430
|
+
["older", "user"],
|
|
1431
|
+
[thread.messages.at(-2)!.id, "assistant"],
|
|
1432
|
+
]);
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
it("keeps a message typed during a rejected load", async () => {
|
|
1436
|
+
let rejectLoad!: (error: unknown) => void;
|
|
1437
|
+
const loadBarrier = new Promise<never>((_, reject) => {
|
|
1438
|
+
rejectLoad = reject;
|
|
1439
|
+
});
|
|
1440
|
+
const { thread, appended } = createLoadingThread(() => loadBarrier);
|
|
1441
|
+
|
|
1442
|
+
thread.__internal_load().catch(() => {});
|
|
1443
|
+
const appendPromise = thread.append(userMessage("typed during load"));
|
|
1444
|
+
rejectLoad(new Error("history unavailable"));
|
|
1445
|
+
await appendPromise;
|
|
1446
|
+
await flush();
|
|
1447
|
+
|
|
1448
|
+
expect(thread.messages.map((message) => message.content)).toEqual([
|
|
1449
|
+
[{ type: "text", text: "typed during load" }],
|
|
1450
|
+
[{ type: "text", text: "done" }],
|
|
1451
|
+
]);
|
|
1452
|
+
expect(appended[0]?.parentId).toBe(null);
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
it("drops a message typed during a load on a detached thread", async () => {
|
|
1456
|
+
let releaseLoad!: () => void;
|
|
1457
|
+
const loadBarrier = new Promise<void>((resolve) => {
|
|
1458
|
+
releaseLoad = resolve;
|
|
1459
|
+
});
|
|
1460
|
+
const run = vi.fn(async () => ({
|
|
1461
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
1462
|
+
}));
|
|
1463
|
+
const { thread, appended } = createLoadingThread(
|
|
1464
|
+
() =>
|
|
1465
|
+
loadBarrier.then(() => ({
|
|
1466
|
+
headId: "persisted",
|
|
1467
|
+
messages: [{ parentId: null, message: persistedMessage }],
|
|
1468
|
+
})),
|
|
1469
|
+
run,
|
|
1470
|
+
);
|
|
1471
|
+
|
|
1472
|
+
thread.__internal_load();
|
|
1473
|
+
const appendPromise = thread.append(userMessage("typed during load"));
|
|
1474
|
+
thread.detach();
|
|
1475
|
+
releaseLoad();
|
|
1476
|
+
await appendPromise;
|
|
1477
|
+
await flush();
|
|
1478
|
+
|
|
1479
|
+
expect(run).not.toHaveBeenCalled();
|
|
1480
|
+
expect(appended).toEqual([]);
|
|
1481
|
+
expect(thread.messages.map((message) => message.id)).toEqual(["persisted"]);
|
|
1482
|
+
});
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
describe("LocalThreadRuntimeCore human-in-the-loop tools", () => {
|
|
1486
|
+
it("pauses on requires-action while a listed tool call has no result", async () => {
|
|
1487
|
+
const { thread, runs } = createApprovalThread(toolCallResult("send_email"));
|
|
1488
|
+
|
|
1489
|
+
await thread.append(userMessage("send an email"));
|
|
1490
|
+
await flush();
|
|
1491
|
+
|
|
1492
|
+
expect(runs).toHaveLength(1);
|
|
1493
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("requires-action");
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
it("does not hold the run for unlisted tool calls", async () => {
|
|
1497
|
+
const { thread, runs } = createApprovalThread(
|
|
1498
|
+
toolCallResult("lookup_weather"),
|
|
1499
|
+
);
|
|
1500
|
+
|
|
1501
|
+
await thread.append(userMessage("what is the weather"));
|
|
1502
|
+
await flush();
|
|
1503
|
+
|
|
1504
|
+
expect(runs).toHaveLength(2);
|
|
1505
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
it("resumes via addToolResult and exposes the result to the adapter", async () => {
|
|
1509
|
+
const { thread, runs } = createApprovalThread(toolCallResult("send_email"));
|
|
1510
|
+
|
|
1511
|
+
await thread.append(userMessage("send an email"));
|
|
1512
|
+
await flush();
|
|
1513
|
+
|
|
1514
|
+
const assistantMessage = thread.messages.at(-1)!;
|
|
1515
|
+
thread.addToolResult({
|
|
1516
|
+
messageId: assistantMessage.id,
|
|
1517
|
+
toolCallId: "call-send_email",
|
|
1518
|
+
toolName: "send_email",
|
|
1519
|
+
result: { approved: true },
|
|
1520
|
+
isError: false,
|
|
1521
|
+
});
|
|
1522
|
+
await flush();
|
|
1523
|
+
|
|
1524
|
+
expect(runs).toHaveLength(2);
|
|
1525
|
+
const resumed = runs[1]!;
|
|
1526
|
+
expect(resumed.messages.at(-1)?.role).toBe("user");
|
|
1527
|
+
const toolCall = resumed
|
|
1528
|
+
.unstable_getMessage()
|
|
1529
|
+
.content.find((part) => part.type === "tool-call");
|
|
1530
|
+
expect(toolCall?.result).toEqual({ approved: true });
|
|
1531
|
+
|
|
1532
|
+
const finalMessage = thread.messages.at(-1)!;
|
|
1533
|
+
expect(finalMessage.status?.type).toBe("complete");
|
|
1534
|
+
expect(finalMessage.content.map((part) => part.type)).toEqual([
|
|
1535
|
+
"tool-call",
|
|
1536
|
+
"text",
|
|
1537
|
+
]);
|
|
1538
|
+
});
|
|
1539
|
+
|
|
1540
|
+
it.each([
|
|
1541
|
+
["false", false],
|
|
1542
|
+
["zero", 0],
|
|
1543
|
+
["an empty string", ""],
|
|
1544
|
+
["null", null],
|
|
1545
|
+
])("resumes when the tool result is %s", async (_label, result) => {
|
|
1546
|
+
const { thread, runs } = createApprovalThread(toolCallResult("send_email"));
|
|
1547
|
+
|
|
1548
|
+
await thread.append(userMessage("send an email"));
|
|
1549
|
+
await flush();
|
|
1550
|
+
|
|
1551
|
+
thread.addToolResult({
|
|
1552
|
+
messageId: thread.messages.at(-1)!.id,
|
|
1553
|
+
toolCallId: "call-send_email",
|
|
1554
|
+
toolName: "send_email",
|
|
1555
|
+
result,
|
|
1556
|
+
isError: false,
|
|
1557
|
+
});
|
|
1558
|
+
await flush();
|
|
1559
|
+
|
|
1560
|
+
expect(runs).toHaveLength(2);
|
|
1561
|
+
const toolCall = runs[1]!
|
|
1562
|
+
.unstable_getMessage()
|
|
1563
|
+
.content.find((part) => part.type === "tool-call");
|
|
1564
|
+
expect(toolCall?.result).toEqual(result);
|
|
1565
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
1566
|
+
});
|
|
1567
|
+
|
|
1568
|
+
it.each(["tool result", "approval"])(
|
|
1569
|
+
"resumes with feedback a subscriber adds while the %s is notified",
|
|
1570
|
+
async (answer) => {
|
|
1571
|
+
const runs: ChatModelRunOptions[] = [];
|
|
1572
|
+
const thread = createThread({
|
|
1573
|
+
async run(options) {
|
|
1574
|
+
runs.push(options);
|
|
1575
|
+
if (runs.length === 1)
|
|
1576
|
+
return answer === "approval"
|
|
1577
|
+
? toolCallResult("deploy", { id: "approval-1" })
|
|
1578
|
+
: toolCallResult("send_email");
|
|
1579
|
+
if (runs.length > 2) throw new Error("resumed more than once");
|
|
1580
|
+
return { content: [{ type: "text", text: "done" }] };
|
|
1581
|
+
},
|
|
1582
|
+
});
|
|
1583
|
+
|
|
1584
|
+
await thread.append(userMessage("send"));
|
|
1585
|
+
const messageId = thread.messages.at(-1)!.id;
|
|
1586
|
+
const unsubscribe = thread.subscribe(() => {
|
|
1587
|
+
const message = thread.messages.find((m) => m.id === messageId);
|
|
1588
|
+
if (
|
|
1589
|
+
message?.status?.type === "requires-action" &&
|
|
1590
|
+
message.metadata.submittedFeedback === undefined
|
|
1591
|
+
) {
|
|
1592
|
+
thread.submitFeedback({ messageId, type: "positive" });
|
|
1593
|
+
}
|
|
1594
|
+
});
|
|
1595
|
+
if (answer === "approval") {
|
|
1596
|
+
void thread.respondToToolApproval({
|
|
1597
|
+
approvalId: "approval-1",
|
|
1598
|
+
approved: true,
|
|
1599
|
+
});
|
|
1600
|
+
} else {
|
|
1601
|
+
thread.addToolResult({
|
|
1602
|
+
messageId,
|
|
1603
|
+
toolCallId: "call-send_email",
|
|
1604
|
+
toolName: "send_email",
|
|
1605
|
+
result: { sent: true },
|
|
1606
|
+
isError: false,
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
unsubscribe();
|
|
1610
|
+
await flush();
|
|
1611
|
+
|
|
1612
|
+
expect(runs).toHaveLength(2);
|
|
1613
|
+
const message = thread.messages.at(-1);
|
|
1614
|
+
expect(message?.status?.type).toBe("complete");
|
|
1615
|
+
expect(message?.metadata.submittedFeedback).toEqual({
|
|
1616
|
+
type: "positive",
|
|
1617
|
+
});
|
|
1618
|
+
},
|
|
1619
|
+
);
|
|
1620
|
+
|
|
1621
|
+
it("continues from feedback that lands before a paused roundtrip returns", async () => {
|
|
1622
|
+
let releaseTeardown!: () => void;
|
|
1623
|
+
const teardown = new Promise<void>((resolve) => {
|
|
1624
|
+
releaseTeardown = resolve;
|
|
1625
|
+
});
|
|
1626
|
+
const runs: ChatModelRunOptions[] = [];
|
|
1627
|
+
const thread = createThread({
|
|
1628
|
+
run(options) {
|
|
1629
|
+
runs.push(options);
|
|
1630
|
+
return (async function* () {
|
|
1631
|
+
if (runs.length > 2) throw new Error("continued more than once");
|
|
1632
|
+
if (runs.length === 2) {
|
|
1633
|
+
yield { content: [{ type: "text", text: "done" }] };
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
yield toolCallResult("lookup_weather");
|
|
1637
|
+
await teardown;
|
|
1638
|
+
})();
|
|
1639
|
+
},
|
|
1640
|
+
});
|
|
1641
|
+
|
|
1642
|
+
const send = thread.append(userMessage("weather"));
|
|
1643
|
+
await flush();
|
|
1644
|
+
const messageId = thread.messages.at(-1)!.id;
|
|
1645
|
+
thread.submitFeedback({ messageId, type: "positive" });
|
|
1646
|
+
releaseTeardown();
|
|
1647
|
+
await send;
|
|
1648
|
+
|
|
1649
|
+
expect(runs).toHaveLength(2);
|
|
1650
|
+
const message = thread.messages.at(-1);
|
|
1651
|
+
expect(message?.status?.type).toBe("complete");
|
|
1652
|
+
expect(message?.metadata.submittedFeedback).toEqual({ type: "positive" });
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
it("continues from feedback that lands while a paused roundtrip returns", async () => {
|
|
1656
|
+
const runs: ChatModelRunOptions[] = [];
|
|
1657
|
+
const thread = createThread({
|
|
1658
|
+
run(options) {
|
|
1659
|
+
runs.push(options);
|
|
1660
|
+
return (async function* () {
|
|
1661
|
+
if (runs.length > 2) throw new Error("continued more than once");
|
|
1662
|
+
if (runs.length === 2) {
|
|
1663
|
+
yield { content: [{ type: "text", text: "done" }] };
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
yield toolCallResult("lookup_weather");
|
|
1667
|
+
const messageId = options.unstable_assistantMessageId!;
|
|
1668
|
+
let depth = 0;
|
|
1669
|
+
const submitFeedback = () => {
|
|
1670
|
+
thread.submitFeedback({ messageId, type: "positive" });
|
|
1671
|
+
if (++depth < 10) queueMicrotask(submitFeedback);
|
|
1672
|
+
};
|
|
1673
|
+
queueMicrotask(submitFeedback);
|
|
1674
|
+
})();
|
|
1675
|
+
},
|
|
1676
|
+
});
|
|
329
1677
|
|
|
330
|
-
await thread.append(userMessage("
|
|
331
|
-
await flush();
|
|
1678
|
+
await thread.append(userMessage("weather"));
|
|
332
1679
|
|
|
333
|
-
expect(runs).toHaveLength(
|
|
334
|
-
|
|
1680
|
+
expect(runs).toHaveLength(2);
|
|
1681
|
+
const message = thread.messages.at(-1);
|
|
1682
|
+
expect(message?.status?.type).toBe("complete");
|
|
1683
|
+
expect(message?.metadata.submittedFeedback).toEqual({ type: "positive" });
|
|
335
1684
|
});
|
|
336
1685
|
|
|
337
|
-
it("
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
1686
|
+
it("ends the run when anything but feedback replaces a paused message", async () => {
|
|
1687
|
+
let releaseTeardown!: () => void;
|
|
1688
|
+
const teardown = new Promise<void>((resolve) => {
|
|
1689
|
+
releaseTeardown = resolve;
|
|
1690
|
+
});
|
|
1691
|
+
const runs: ChatModelRunOptions[] = [];
|
|
1692
|
+
const thread = createThread({
|
|
1693
|
+
run(options) {
|
|
1694
|
+
runs.push(options);
|
|
1695
|
+
return (async function* () {
|
|
1696
|
+
if (runs.length > 1)
|
|
1697
|
+
throw new Error("continued after its message was replaced");
|
|
1698
|
+
yield toolCallResult("lookup_weather");
|
|
1699
|
+
await teardown;
|
|
1700
|
+
})();
|
|
1701
|
+
},
|
|
1702
|
+
});
|
|
341
1703
|
|
|
342
|
-
|
|
1704
|
+
const send = thread.append(userMessage("weather"));
|
|
343
1705
|
await flush();
|
|
1706
|
+
const exported = thread.export();
|
|
1707
|
+
thread.import({
|
|
1708
|
+
...exported,
|
|
1709
|
+
messages: exported.messages.map((item) => ({
|
|
1710
|
+
...item,
|
|
1711
|
+
message: { ...item.message },
|
|
1712
|
+
})),
|
|
1713
|
+
});
|
|
1714
|
+
releaseTeardown();
|
|
1715
|
+
await send;
|
|
344
1716
|
|
|
345
|
-
expect(runs).toHaveLength(
|
|
346
|
-
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
1717
|
+
expect(runs).toHaveLength(1);
|
|
347
1718
|
});
|
|
1719
|
+
});
|
|
348
1720
|
|
|
349
|
-
|
|
1721
|
+
describe("LocalThreadRuntimeCore addToolResult content", () => {
|
|
1722
|
+
it("stores modelContent and forwards it to the resumed adapter", async () => {
|
|
350
1723
|
const { thread, runs } = createApprovalThread(toolCallResult("send_email"));
|
|
351
1724
|
|
|
352
1725
|
await thread.append(userMessage("send an email"));
|
|
353
1726
|
await flush();
|
|
354
1727
|
|
|
355
|
-
const assistantMessage = thread.messages.at(-1)!;
|
|
356
1728
|
thread.addToolResult({
|
|
357
|
-
messageId:
|
|
1729
|
+
messageId: thread.messages.at(-1)!.id,
|
|
358
1730
|
toolCallId: "call-send_email",
|
|
359
1731
|
toolName: "send_email",
|
|
360
1732
|
result: { approved: true },
|
|
361
1733
|
isError: false,
|
|
1734
|
+
modelContent: [{ type: "text", text: "Email sent." }],
|
|
362
1735
|
});
|
|
363
1736
|
await flush();
|
|
364
1737
|
|
|
365
|
-
|
|
366
|
-
const resumed = runs[1]!;
|
|
367
|
-
expect(resumed.messages.at(-1)?.role).toBe("user");
|
|
368
|
-
const toolCall = resumed
|
|
1738
|
+
const resumedToolCall = runs[1]!
|
|
369
1739
|
.unstable_getMessage()
|
|
370
1740
|
.content.find((part) => part.type === "tool-call");
|
|
371
|
-
expect(
|
|
1741
|
+
expect(resumedToolCall?.modelContent).toEqual([
|
|
1742
|
+
{ type: "text", text: "Email sent." },
|
|
1743
|
+
]);
|
|
372
1744
|
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
"text",
|
|
1745
|
+
const storedToolCall = thread.messages
|
|
1746
|
+
.at(-1)!
|
|
1747
|
+
.content.find((part) => part.type === "tool-call");
|
|
1748
|
+
expect(storedToolCall?.modelContent).toEqual([
|
|
1749
|
+
{ type: "text", text: "Email sent." },
|
|
378
1750
|
]);
|
|
379
1751
|
});
|
|
380
1752
|
|
|
381
|
-
it
|
|
382
|
-
|
|
383
|
-
["zero", 0],
|
|
384
|
-
["an empty string", ""],
|
|
385
|
-
["null", null],
|
|
386
|
-
])("resumes when the tool result is %s", async (_label, result) => {
|
|
387
|
-
const { thread, runs } = createApprovalThread(toolCallResult("send_email"));
|
|
1753
|
+
it("keeps a stored artifact when a later result omits it", async () => {
|
|
1754
|
+
const { thread } = createApprovalThread(toolCallResult("send_email"));
|
|
388
1755
|
|
|
389
1756
|
await thread.append(userMessage("send an email"));
|
|
390
1757
|
await flush();
|
|
391
1758
|
|
|
1759
|
+
const messageId = thread.messages.at(-1)!.id;
|
|
392
1760
|
thread.addToolResult({
|
|
393
|
-
messageId
|
|
1761
|
+
messageId,
|
|
394
1762
|
toolCallId: "call-send_email",
|
|
395
1763
|
toolName: "send_email",
|
|
396
|
-
result,
|
|
1764
|
+
result: { approved: true },
|
|
397
1765
|
isError: false,
|
|
1766
|
+
artifact: { draftId: "d-1" },
|
|
398
1767
|
});
|
|
399
1768
|
await flush();
|
|
400
1769
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
1770
|
+
thread.addToolResult({
|
|
1771
|
+
messageId,
|
|
1772
|
+
toolCallId: "call-send_email",
|
|
1773
|
+
toolName: "send_email",
|
|
1774
|
+
result: { approved: true, sent: true },
|
|
1775
|
+
isError: false,
|
|
1776
|
+
});
|
|
1777
|
+
await flush();
|
|
1778
|
+
|
|
1779
|
+
const storedToolCall = thread.messages
|
|
1780
|
+
.map((message) =>
|
|
1781
|
+
message.content.find(
|
|
1782
|
+
(part): part is ToolCallMessagePart =>
|
|
1783
|
+
part.type === "tool-call" && part.toolCallId === "call-send_email",
|
|
1784
|
+
),
|
|
1785
|
+
)
|
|
1786
|
+
.find((part): part is ToolCallMessagePart => part !== undefined);
|
|
1787
|
+
expect(storedToolCall?.artifact).toEqual({ draftId: "d-1" });
|
|
407
1788
|
});
|
|
408
1789
|
});
|
|
409
1790
|
|
|
@@ -856,7 +2237,7 @@ describe("LocalThreadRuntimeCore cancellation", () => {
|
|
|
856
2237
|
const toolCall = thread.messages
|
|
857
2238
|
.find((item) => item.id === messageId)
|
|
858
2239
|
?.content.find(
|
|
859
|
-
(part) =>
|
|
2240
|
+
(part): part is ToolCallMessagePart =>
|
|
860
2241
|
part.type === "tool-call" && part.toolCallId === "call-send_email",
|
|
861
2242
|
);
|
|
862
2243
|
expect(toolCall?.result).toEqual({ approved: true });
|
|
@@ -864,9 +2245,10 @@ describe("LocalThreadRuntimeCore cancellation", () => {
|
|
|
864
2245
|
|
|
865
2246
|
resolveSecond({ content: [{ type: "text", text: "replacement" }] });
|
|
866
2247
|
await vi.waitFor(() => {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
2248
|
+
const message = thread.messages.find((item) => item.id === messageId);
|
|
2249
|
+
if (message?.role !== "assistant")
|
|
2250
|
+
throw new Error("expected assistant message");
|
|
2251
|
+
expect(message.status.type).toBe("complete");
|
|
870
2252
|
});
|
|
871
2253
|
});
|
|
872
2254
|
|
|
@@ -915,16 +2297,88 @@ describe("LocalThreadRuntimeCore cancellation", () => {
|
|
|
915
2297
|
await appendPromise;
|
|
916
2298
|
|
|
917
2299
|
const message = thread.messages.find((item) => item.id === messageId);
|
|
918
|
-
|
|
919
|
-
(
|
|
2300
|
+
if (message?.role !== "assistant")
|
|
2301
|
+
throw new Error("expected assistant message");
|
|
2302
|
+
const sendEmail = message.content.find(
|
|
2303
|
+
(part): part is ToolCallMessagePart =>
|
|
920
2304
|
part.type === "tool-call" && part.toolCallId === "call-send_email",
|
|
921
2305
|
);
|
|
922
|
-
const deploy = message
|
|
923
|
-
(part)
|
|
2306
|
+
const deploy = message.content.find(
|
|
2307
|
+
(part): part is ToolCallMessagePart =>
|
|
2308
|
+
part.type === "tool-call" && part.toolCallId === "call-deploy",
|
|
924
2309
|
);
|
|
925
2310
|
expect(sendEmail?.result).toEqual({ approved: true });
|
|
926
2311
|
expect(deploy?.approval).toEqual({ id: "approval-1" });
|
|
927
|
-
expect(message
|
|
2312
|
+
expect(message.status.type).toBe("requires-action");
|
|
2313
|
+
});
|
|
2314
|
+
|
|
2315
|
+
it("keeps a paused run's later chunks after feedback", async () => {
|
|
2316
|
+
let releaseTeardown!: () => void;
|
|
2317
|
+
const teardown = new Promise<void>((resolve) => {
|
|
2318
|
+
releaseTeardown = resolve;
|
|
2319
|
+
});
|
|
2320
|
+
const thread = createThread({
|
|
2321
|
+
async *run() {
|
|
2322
|
+
yield toolCallResult("send_email");
|
|
2323
|
+
await teardown;
|
|
2324
|
+
yield {
|
|
2325
|
+
content: [
|
|
2326
|
+
toolCallPart("send_email"),
|
|
2327
|
+
{ type: "text", text: "Waiting for approval." },
|
|
2328
|
+
],
|
|
2329
|
+
} satisfies ChatModelRunResult;
|
|
2330
|
+
},
|
|
2331
|
+
});
|
|
2332
|
+
|
|
2333
|
+
const appendPromise = thread.append(userMessage("send"));
|
|
2334
|
+
await flush();
|
|
2335
|
+
thread.submitFeedback({
|
|
2336
|
+
messageId: thread.messages.at(-1)!.id,
|
|
2337
|
+
type: "positive",
|
|
2338
|
+
});
|
|
2339
|
+
releaseTeardown();
|
|
2340
|
+
await appendPromise;
|
|
2341
|
+
|
|
2342
|
+
const message = thread.messages.at(-1);
|
|
2343
|
+
expect(message?.status?.type).toBe("requires-action");
|
|
2344
|
+
expect(message?.content).toEqual([
|
|
2345
|
+
toolCallPart("send_email"),
|
|
2346
|
+
{ type: "text", text: "Waiting for approval." },
|
|
2347
|
+
]);
|
|
2348
|
+
expect(message?.metadata.submittedFeedback).toEqual({ type: "positive" });
|
|
2349
|
+
});
|
|
2350
|
+
|
|
2351
|
+
it("cancels a paused run that received feedback", async () => {
|
|
2352
|
+
const runs: ChatModelRunOptions[] = [];
|
|
2353
|
+
const thread = createThread({
|
|
2354
|
+
run(options) {
|
|
2355
|
+
runs.push(options);
|
|
2356
|
+
return (async function* () {
|
|
2357
|
+
if (runs.length > 1) throw new Error("continued after cancel");
|
|
2358
|
+
yield toolCallResult("lookup_weather");
|
|
2359
|
+
await new Promise((resolve) =>
|
|
2360
|
+
options.abortSignal.addEventListener("abort", resolve),
|
|
2361
|
+
);
|
|
2362
|
+
})();
|
|
2363
|
+
},
|
|
2364
|
+
});
|
|
2365
|
+
|
|
2366
|
+
const appendPromise = thread.append(userMessage("weather"));
|
|
2367
|
+
await flush();
|
|
2368
|
+
thread.submitFeedback({
|
|
2369
|
+
messageId: thread.messages.at(-1)!.id,
|
|
2370
|
+
type: "positive",
|
|
2371
|
+
});
|
|
2372
|
+
thread.cancelRun();
|
|
2373
|
+
await appendPromise;
|
|
2374
|
+
|
|
2375
|
+
expect(runs).toHaveLength(1);
|
|
2376
|
+
const message = thread.messages.at(-1);
|
|
2377
|
+
expect(message?.status).toEqual({
|
|
2378
|
+
type: "incomplete",
|
|
2379
|
+
reason: "cancelled",
|
|
2380
|
+
});
|
|
2381
|
+
expect(message?.metadata.submittedFeedback).toEqual({ type: "positive" });
|
|
928
2382
|
});
|
|
929
2383
|
|
|
930
2384
|
it("ignores a superseded result after its message is removed", async () => {
|
|
@@ -1318,6 +2772,38 @@ describe("LocalThreadRuntimeCore tool approval persistence", () => {
|
|
|
1318
2772
|
);
|
|
1319
2773
|
};
|
|
1320
2774
|
|
|
2775
|
+
it("persists feedback submitted after the assistant run settles", async () => {
|
|
2776
|
+
const { history, appended, updated } = createHistory();
|
|
2777
|
+
const thread = createThread(
|
|
2778
|
+
{
|
|
2779
|
+
async run() {
|
|
2780
|
+
return { content: [{ type: "text", text: "hello" }] };
|
|
2781
|
+
},
|
|
2782
|
+
},
|
|
2783
|
+
{ history },
|
|
2784
|
+
);
|
|
2785
|
+
|
|
2786
|
+
await thread.append(userMessage("hi"));
|
|
2787
|
+
await flush();
|
|
2788
|
+
|
|
2789
|
+
const assistant = appended.find(
|
|
2790
|
+
(item) => item.message.role === "assistant",
|
|
2791
|
+
);
|
|
2792
|
+
if (!assistant) throw new Error("expected persisted assistant message");
|
|
2793
|
+
|
|
2794
|
+
thread.submitFeedback({
|
|
2795
|
+
messageId: assistant.message.id,
|
|
2796
|
+
type: "positive",
|
|
2797
|
+
});
|
|
2798
|
+
await flush();
|
|
2799
|
+
|
|
2800
|
+
expect(updated).toHaveLength(1);
|
|
2801
|
+
expect(updated[0]?.message.id).toBe(assistant.message.id);
|
|
2802
|
+
expect(updated[0]?.message.metadata.submittedFeedback).toEqual({
|
|
2803
|
+
type: "positive",
|
|
2804
|
+
});
|
|
2805
|
+
});
|
|
2806
|
+
|
|
1321
2807
|
it("persists a run paused for approval and rewrites it once the run finishes", async () => {
|
|
1322
2808
|
const { history, appended, updated } = createHistory();
|
|
1323
2809
|
const thread = createApprovalThreadWithHistory(history);
|
|
@@ -1339,6 +2825,39 @@ describe("LocalThreadRuntimeCore tool approval persistence", () => {
|
|
|
1339
2825
|
expect(updated.at(-1)?.message.status?.type).toBe("complete");
|
|
1340
2826
|
});
|
|
1341
2827
|
|
|
2828
|
+
it("persists an approval answer and feedback given after later turns follow the message", async () => {
|
|
2829
|
+
const { history, updated } = createHistory();
|
|
2830
|
+
const thread = createApprovalThreadWithHistory(history);
|
|
2831
|
+
|
|
2832
|
+
await thread.append(userMessage("send an email"));
|
|
2833
|
+
await flush();
|
|
2834
|
+
const [question, paused] = thread.messages;
|
|
2835
|
+
expect(paused?.status?.type).toBe("requires-action");
|
|
2836
|
+
await thread.append({
|
|
2837
|
+
...userMessage("and cc my manager"),
|
|
2838
|
+
parentId: paused!.id,
|
|
2839
|
+
});
|
|
2840
|
+
await flush();
|
|
2841
|
+
expect(thread.messages).toHaveLength(4);
|
|
2842
|
+
|
|
2843
|
+
thread.respondToToolApproval({ approvalId: "a1", approved: true });
|
|
2844
|
+
thread.submitFeedback({ messageId: paused!.id, type: "positive" });
|
|
2845
|
+
await flush();
|
|
2846
|
+
|
|
2847
|
+
expect(updated.map((i) => i.message.id)).toEqual([paused!.id, paused!.id]);
|
|
2848
|
+
expect(updated.at(-1)).toMatchObject({
|
|
2849
|
+
parentId: question!.id,
|
|
2850
|
+
message: {
|
|
2851
|
+
content: [
|
|
2852
|
+
expect.objectContaining({
|
|
2853
|
+
approval: expect.objectContaining({ id: "a1", approved: true }),
|
|
2854
|
+
}),
|
|
2855
|
+
],
|
|
2856
|
+
metadata: { submittedFeedback: { type: "positive" } },
|
|
2857
|
+
},
|
|
2858
|
+
});
|
|
2859
|
+
});
|
|
2860
|
+
|
|
1342
2861
|
it("keeps the append-only behavior for adapters without update", async () => {
|
|
1343
2862
|
const { history, appended } = createHistory({ update: false });
|
|
1344
2863
|
const thread = createApprovalThreadWithHistory(history);
|
|
@@ -1851,7 +3370,7 @@ describe("LocalThreadRuntimeCore runs", () => {
|
|
|
1851
3370
|
],
|
|
1852
3371
|
status: { type: "requires-action", reason: "tool-calls" },
|
|
1853
3372
|
metadata: {
|
|
1854
|
-
steps: [{ usage: {
|
|
3373
|
+
steps: [{ usage: { inputTokens: 10, outputTokens: 5 } }],
|
|
1855
3374
|
},
|
|
1856
3375
|
}));
|
|
1857
3376
|
const thread = createPlainThread({ run }, { maxSteps: 1 });
|
|
@@ -1913,7 +3432,7 @@ describe("LocalThreadRuntimeCore runs", () => {
|
|
|
1913
3432
|
run: async () => ({ content: [] }),
|
|
1914
3433
|
};
|
|
1915
3434
|
const thread = createPlainThread(adapter);
|
|
1916
|
-
const load = vi.fn(async () => ({
|
|
3435
|
+
const load = vi.fn<ThreadHistoryAdapter["load"]>(async () => ({
|
|
1917
3436
|
headId: "restored",
|
|
1918
3437
|
messages: [
|
|
1919
3438
|
{
|
|
@@ -1922,6 +3441,7 @@ describe("LocalThreadRuntimeCore runs", () => {
|
|
|
1922
3441
|
id: "restored",
|
|
1923
3442
|
role: "user" as const,
|
|
1924
3443
|
content: [{ type: "text" as const, text: "hello" }],
|
|
3444
|
+
attachments: [],
|
|
1925
3445
|
createdAt: new Date(0),
|
|
1926
3446
|
metadata: { custom: {} },
|
|
1927
3447
|
},
|
|
@@ -1950,7 +3470,7 @@ describe("LocalThreadRuntimeCore runs", () => {
|
|
|
1950
3470
|
run: async () => ({ content: [] }),
|
|
1951
3471
|
};
|
|
1952
3472
|
const thread = createPlainThread(adapter);
|
|
1953
|
-
const load = vi.fn(async () => ({
|
|
3473
|
+
const load = vi.fn<ThreadHistoryAdapter["load"]>(async () => ({
|
|
1954
3474
|
headId: "restored",
|
|
1955
3475
|
messages: [
|
|
1956
3476
|
{
|
|
@@ -1959,6 +3479,7 @@ describe("LocalThreadRuntimeCore runs", () => {
|
|
|
1959
3479
|
id: "restored",
|
|
1960
3480
|
role: "user" as const,
|
|
1961
3481
|
content: [{ type: "text" as const, text: "hello" }],
|
|
3482
|
+
attachments: [],
|
|
1962
3483
|
createdAt: new Date(0),
|
|
1963
3484
|
metadata: { custom: {} },
|
|
1964
3485
|
},
|
|
@@ -2219,4 +3740,180 @@ describe("LocalThreadRuntimeCore imported approvals", () => {
|
|
|
2219
3740
|
expect(runs).toHaveLength(1);
|
|
2220
3741
|
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
2221
3742
|
});
|
|
3743
|
+
|
|
3744
|
+
it("resumes an imported preliminary tool call and clears its marker", async () => {
|
|
3745
|
+
const { thread, runs } = createImportedThread([
|
|
3746
|
+
{ role: "user", content: [{ type: "text", text: "send an email" }] },
|
|
3747
|
+
{
|
|
3748
|
+
role: "assistant",
|
|
3749
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
3750
|
+
content: [
|
|
3751
|
+
{
|
|
3752
|
+
type: "tool-call",
|
|
3753
|
+
toolCallId: "call-send_email",
|
|
3754
|
+
toolName: "send_email",
|
|
3755
|
+
args: {},
|
|
3756
|
+
argsText: "{}",
|
|
3757
|
+
result: { preview: true },
|
|
3758
|
+
isPreliminary: true,
|
|
3759
|
+
},
|
|
3760
|
+
],
|
|
3761
|
+
},
|
|
3762
|
+
]);
|
|
3763
|
+
|
|
3764
|
+
thread.addToolResult({
|
|
3765
|
+
messageId: thread.messages.at(-1)!.id,
|
|
3766
|
+
toolCallId: "call-send_email",
|
|
3767
|
+
toolName: "send_email",
|
|
3768
|
+
result: { sent: true },
|
|
3769
|
+
isError: false,
|
|
3770
|
+
});
|
|
3771
|
+
await flush();
|
|
3772
|
+
|
|
3773
|
+
expect(runs).toHaveLength(1);
|
|
3774
|
+
const toolCall = runs[0]!
|
|
3775
|
+
.unstable_getMessage()
|
|
3776
|
+
.content.find((part) => part.type === "tool-call");
|
|
3777
|
+
expect(toolCall).toMatchObject({ result: { sent: true } });
|
|
3778
|
+
expect(toolCall?.type === "tool-call" && toolCall.isPreliminary).toBe(
|
|
3779
|
+
undefined,
|
|
3780
|
+
);
|
|
3781
|
+
});
|
|
3782
|
+
|
|
3783
|
+
it("persists a final result for a preliminary tool call that remains paused", async () => {
|
|
3784
|
+
const updated: ExportedMessageRepositoryItem[] = [];
|
|
3785
|
+
const { thread } = createImportedThread(
|
|
3786
|
+
[
|
|
3787
|
+
{ role: "user", content: [{ type: "text", text: "send an email" }] },
|
|
3788
|
+
{
|
|
3789
|
+
role: "assistant",
|
|
3790
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
3791
|
+
content: [
|
|
3792
|
+
{
|
|
3793
|
+
type: "tool-call",
|
|
3794
|
+
toolCallId: "call-send_email",
|
|
3795
|
+
toolName: "send_email",
|
|
3796
|
+
args: {},
|
|
3797
|
+
argsText: "{}",
|
|
3798
|
+
result: { preview: true },
|
|
3799
|
+
isPreliminary: true,
|
|
3800
|
+
},
|
|
3801
|
+
{
|
|
3802
|
+
type: "tool-call",
|
|
3803
|
+
toolCallId: "call-other",
|
|
3804
|
+
toolName: "send_email",
|
|
3805
|
+
args: {},
|
|
3806
|
+
argsText: "{}",
|
|
3807
|
+
},
|
|
3808
|
+
],
|
|
3809
|
+
},
|
|
3810
|
+
],
|
|
3811
|
+
{
|
|
3812
|
+
async load() {
|
|
3813
|
+
return { messages: [] };
|
|
3814
|
+
},
|
|
3815
|
+
async append() {},
|
|
3816
|
+
async update(item) {
|
|
3817
|
+
updated.push(item);
|
|
3818
|
+
},
|
|
3819
|
+
},
|
|
3820
|
+
);
|
|
3821
|
+
|
|
3822
|
+
thread.addToolResult({
|
|
3823
|
+
messageId: thread.messages.at(-1)!.id,
|
|
3824
|
+
toolCallId: "call-send_email",
|
|
3825
|
+
toolName: "send_email",
|
|
3826
|
+
result: { sent: true },
|
|
3827
|
+
isError: false,
|
|
3828
|
+
});
|
|
3829
|
+
await flush();
|
|
3830
|
+
|
|
3831
|
+
expect(updated).toHaveLength(1);
|
|
3832
|
+
const toolCall = updated[0]!.message.content.find(
|
|
3833
|
+
(part) => part.type === "tool-call",
|
|
3834
|
+
);
|
|
3835
|
+
expect(toolCall).toMatchObject({ result: { sent: true } });
|
|
3836
|
+
expect(toolCall?.type === "tool-call" && toolCall.isPreliminary).toBe(
|
|
3837
|
+
undefined,
|
|
3838
|
+
);
|
|
3839
|
+
});
|
|
3840
|
+
});
|
|
3841
|
+
|
|
3842
|
+
describe("LocalThreadRuntimeCore message queue", () => {
|
|
3843
|
+
const createQueuedThread = () => {
|
|
3844
|
+
const dispatched: string[] = [];
|
|
3845
|
+
let release!: () => void;
|
|
3846
|
+
let gate = new Promise<void>((resolve) => (release = resolve));
|
|
3847
|
+
|
|
3848
|
+
const core = new LocalRuntimeCore(
|
|
3849
|
+
{
|
|
3850
|
+
adapters: {
|
|
3851
|
+
chatModel: {
|
|
3852
|
+
async run(options) {
|
|
3853
|
+
const last = options.messages.at(-1);
|
|
3854
|
+
const text = last?.content
|
|
3855
|
+
.filter((part) => part.type === "text")
|
|
3856
|
+
.map((part) => (part as { text: string }).text)
|
|
3857
|
+
.join("");
|
|
3858
|
+
dispatched.push(text ?? "");
|
|
3859
|
+
await gate;
|
|
3860
|
+
return { content: [{ type: "text", text: "ok" }] };
|
|
3861
|
+
},
|
|
3862
|
+
},
|
|
3863
|
+
},
|
|
3864
|
+
unstable_enableMessageQueue: true,
|
|
3865
|
+
},
|
|
3866
|
+
undefined,
|
|
3867
|
+
);
|
|
3868
|
+
|
|
3869
|
+
// the in-flight run already awaits the current gate, so the next gate has
|
|
3870
|
+
// to be installed before releasing it or the run dispatched next resolves
|
|
3871
|
+
// against an already-settled promise
|
|
3872
|
+
const releaseRun = async () => {
|
|
3873
|
+
const releaseCurrent = release;
|
|
3874
|
+
gate = new Promise<void>((resolve) => (release = resolve));
|
|
3875
|
+
releaseCurrent();
|
|
3876
|
+
await flush();
|
|
3877
|
+
};
|
|
3878
|
+
|
|
3879
|
+
return {
|
|
3880
|
+
thread: core.threads.getMainThreadRuntimeCore(),
|
|
3881
|
+
dispatched,
|
|
3882
|
+
releaseRun,
|
|
3883
|
+
};
|
|
3884
|
+
};
|
|
3885
|
+
|
|
3886
|
+
it("keeps the steer lane for implicit sends during back-to-back queued runs", async () => {
|
|
3887
|
+
const { thread, dispatched, releaseRun } = createQueuedThread();
|
|
3888
|
+
// the queue lane is only taken for a message appended onto the tail
|
|
3889
|
+
const appendToTail = (text: string, steer?: boolean) =>
|
|
3890
|
+
void thread.append({
|
|
3891
|
+
...userMessage(text),
|
|
3892
|
+
parentId: thread.messages.at(-1)?.id ?? null,
|
|
3893
|
+
...(steer !== undefined && { steer }),
|
|
3894
|
+
});
|
|
3895
|
+
|
|
3896
|
+
appendToTail("first");
|
|
3897
|
+
await flush();
|
|
3898
|
+
expect(dispatched).toEqual(["first"]);
|
|
3899
|
+
|
|
3900
|
+
// buffers behind the running dispatch, so releasing "first" dispatches it
|
|
3901
|
+
appendToTail("second", false);
|
|
3902
|
+
await flush();
|
|
3903
|
+
await releaseRun();
|
|
3904
|
+
expect(dispatched).toEqual(["first", "second"]);
|
|
3905
|
+
|
|
3906
|
+
// "second" is a queue-dispatched run in flight, so an implicit send steers
|
|
3907
|
+
// ahead of a bulk item even though it is appended after it
|
|
3908
|
+
appendToTail("bulk", false);
|
|
3909
|
+
appendToTail("implicit");
|
|
3910
|
+
await flush();
|
|
3911
|
+
await releaseRun();
|
|
3912
|
+
|
|
3913
|
+
expect(dispatched).toEqual(["first", "second", "implicit"]);
|
|
3914
|
+
|
|
3915
|
+
await releaseRun();
|
|
3916
|
+
expect(dispatched).toEqual(["first", "second", "implicit", "bulk"]);
|
|
3917
|
+
await releaseRun();
|
|
3918
|
+
});
|
|
2222
3919
|
});
|