@assistant-ui/core 0.2.21 → 0.2.23
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.map +1 -1
- package/dist/adapters/speech.d.ts.map +1 -1
- package/dist/adapters/speech.js +9 -2
- package/dist/adapters/speech.js.map +1 -1
- package/dist/adapters/thread-history.d.ts +8 -0
- package/dist/adapters/thread-history.d.ts.map +1 -1
- package/dist/adapters/voice.d.ts.map +1 -1
- package/dist/adapters/voice.js +13 -5
- package/dist/adapters/voice.js.map +1 -1
- package/dist/internal.d.ts +5 -2
- package/dist/internal.js +5 -2
- package/dist/model-context/frame/host.d.ts +1 -0
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +41 -21
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/frame/provider.d.ts.map +1 -1
- package/dist/model-context/frame/provider.js +1 -0
- package/dist/model-context/frame/provider.js.map +1 -1
- package/dist/model-context/interactable-composer-metadata.js.map +1 -1
- package/dist/model-context/types.d.ts.map +1 -1
- package/dist/model-context/types.js +13 -4
- package/dist/model-context/types.js.map +1 -1
- package/dist/react/AssistantProvider.js.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.js +78 -50
- package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/Tools.d.ts.map +1 -1
- package/dist/react/client/interactable-model-context.d.ts.map +1 -1
- package/dist/react/client/interactable-model-context.js +1 -0
- package/dist/react/client/interactable-model-context.js.map +1 -1
- package/dist/react/interactables-legacy/useInteractableState.js +1 -0
- package/dist/react/interactables-legacy/useInteractableState.js.map +1 -1
- package/dist/react/model-context/useInteractable.js.map +1 -1
- package/dist/react/model-context/useInteractableState.js +1 -0
- package/dist/react/model-context/useInteractableState.js.map +1 -1
- package/dist/react/model-context/useInteractableVersions.js +1 -0
- package/dist/react/model-context/useInteractableVersions.js.map +1 -1
- package/dist/react/model-context/useToolArgsStatus.js +1 -0
- package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarCopy.js +14 -13
- package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarEdit.js +1 -0
- package/dist/react/primitive-hooks/useActionBarEdit.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarFeedback.js +1 -0
- package/dist/react/primitive-hooks/useActionBarFeedback.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarReload.js +1 -0
- package/dist/react/primitive-hooks/useActionBarReload.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarSpeak.js +1 -0
- package/dist/react/primitive-hooks/useActionBarSpeak.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarStopSpeaking.js +1 -0
- package/dist/react/primitive-hooks/useActionBarStopSpeaking.js.map +1 -1
- package/dist/react/primitive-hooks/useBranchPickerNext.js +1 -0
- package/dist/react/primitive-hooks/useBranchPickerNext.js.map +1 -1
- package/dist/react/primitive-hooks/useBranchPickerPrevious.js +1 -0
- package/dist/react/primitive-hooks/useBranchPickerPrevious.js.map +1 -1
- package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useComposerAddAttachment.js +1 -0
- package/dist/react/primitive-hooks/useComposerAddAttachment.js.map +1 -1
- package/dist/react/primitive-hooks/useComposerCancel.js +1 -0
- package/dist/react/primitive-hooks/useComposerCancel.js.map +1 -1
- package/dist/react/primitive-hooks/useComposerDictate.js +1 -0
- package/dist/react/primitive-hooks/useComposerDictate.js.map +1 -1
- package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useComposerSend.js +1 -0
- package/dist/react/primitive-hooks/useComposerSend.js.map +1 -1
- package/dist/react/primitive-hooks/useEditComposerCancel.js +1 -0
- package/dist/react/primitive-hooks/useEditComposerCancel.js.map +1 -1
- package/dist/react/primitive-hooks/useEditComposerSend.js +1 -0
- package/dist/react/primitive-hooks/useEditComposerSend.js.map +1 -1
- package/dist/react/primitive-hooks/useMessageBranching.js +1 -0
- package/dist/react/primitive-hooks/useMessageBranching.js.map +1 -1
- package/dist/react/primitive-hooks/useMessageReload.js +1 -0
- package/dist/react/primitive-hooks/useMessageReload.js.map +1 -1
- package/dist/react/primitive-hooks/useSuggestionTrigger.js +1 -0
- package/dist/react/primitive-hooks/useSuggestionTrigger.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadListItemArchive.js +1 -0
- package/dist/react/primitive-hooks/useThreadListItemArchive.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadListItemDelete.js +1 -0
- package/dist/react/primitive-hooks/useThreadListItemDelete.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadListItemTrigger.js +1 -0
- package/dist/react/primitive-hooks/useThreadListItemTrigger.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadListItemUnarchive.js +1 -0
- package/dist/react/primitive-hooks/useThreadListItemUnarchive.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadListLoadMore.js +1 -0
- package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadListNew.js +1 -0
- package/dist/react/primitive-hooks/useThreadListNew.js.map +1 -1
- package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.d.ts.map +1 -1
- package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.js +1 -1
- package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.js.map +1 -1
- package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
- package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
- package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
- package/dist/react/primitives/generativeUI/GenerativeUI.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
- package/dist/react/primitives/message/MessageParts.d.ts +2 -1
- package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageParts.js +46 -36
- package/dist/react/primitives/message/MessageParts.js.map +1 -1
- package/dist/react/primitives/part/PartMessages.js.map +1 -1
- package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
- package/dist/react/primitives/thread/ThreadSuggestions.js.map +1 -1
- package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
- package/dist/react/providers/ChainOfThoughtPartByIndexProvider.d.ts.map +1 -1
- package/dist/react/providers/ChainOfThoughtPartByIndexProvider.js +18 -30
- package/dist/react/providers/ChainOfThoughtPartByIndexProvider.js.map +1 -1
- package/dist/react/providers/PartByIndexProvider.d.ts.map +1 -1
- package/dist/react/providers/PartByIndexProvider.js +18 -30
- package/dist/react/providers/PartByIndexProvider.js.map +1 -1
- package/dist/react/providers/TextMessagePartProvider.js +1 -0
- package/dist/react/providers/TextMessagePartProvider.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +1 -0
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +9 -2
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +55 -12
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +31 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +12 -2
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +3 -2
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts +12 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.js +2 -1
- package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +75 -73
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
- package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.js +2 -2
- package/dist/react/runtimes/external-message-converter.js.map +1 -1
- package/dist/react/runtimes/useExternalStoreSharedOptions.js +1 -0
- package/dist/react/runtimes/useExternalStoreSharedOptions.js.map +1 -1
- package/dist/react/runtimes/useLocalRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/useLocalRuntime.js +9 -1
- package/dist/react/runtimes/useLocalRuntime.js.map +1 -1
- package/dist/runtime/api/assistant-runtime.d.ts +1 -1
- package/dist/runtime/api/assistant-runtime.d.ts.map +1 -1
- package/dist/runtime/api/assistant-runtime.js +1 -1
- package/dist/runtime/api/assistant-runtime.js.map +1 -1
- package/dist/runtime/api/attachment-runtime.d.ts +1 -1
- package/dist/runtime/api/attachment-runtime.d.ts.map +1 -1
- package/dist/runtime/api/attachment-runtime.js +1 -1
- package/dist/runtime/api/attachment-runtime.js.map +1 -1
- package/dist/runtime/api/composer-runtime.d.ts +2 -2
- package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
- package/dist/runtime/api/composer-runtime.js +2 -2
- package/dist/runtime/api/composer-runtime.js.map +1 -1
- package/dist/runtime/api/message-part-runtime.d.ts +4 -4
- package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
- package/dist/runtime/api/message-part-runtime.js +3 -3
- package/dist/runtime/api/message-part-runtime.js.map +1 -1
- package/dist/runtime/api/message-runtime.d.ts +1 -1
- package/dist/runtime/api/message-runtime.d.ts.map +1 -1
- package/dist/runtime/api/message-runtime.js +6 -6
- package/dist/runtime/api/message-runtime.js.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.d.ts +1 -1
- package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.js +4 -3
- package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
- package/dist/runtime/api/thread-list-runtime.d.ts +1 -1
- package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-runtime.js +1 -1
- package/dist/runtime/api/thread-list-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts +4 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +41 -14
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +3 -2
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.d.ts +2 -2
- package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.js +6 -4
- package/dist/runtime/base/default-edit-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.js +3 -3
- package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
- package/dist/runtime/utils/message-repository.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.js +4 -2
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtime/utils/thread-message-like.js +4 -1
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/runtimes/external-store/external-store-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +1 -1
- 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 +1 -1
- 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.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js +12 -0
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/internal.d.ts +2 -1
- package/dist/runtimes/internal.js +2 -1
- package/dist/runtimes/local/local-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -0
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +49 -15
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/optimistic-state.d.ts +4 -4
- package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/optimistic-state.js +22 -9
- package/dist/runtimes/remote-thread-list/optimistic-state.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +32 -27
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +3 -2
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
- package/dist/store/clients/chain-of-thought-client.d.ts.map +1 -1
- package/dist/store/clients/no-op-composer-client.d.ts.map +1 -1
- package/dist/store/clients/no-op-composer-client.js +1 -0
- package/dist/store/clients/no-op-composer-client.js.map +1 -1
- package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/message-part-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
- 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.map +1 -1
- package/dist/store/runtime-clients/thread-list-runtime-client.js +1 -0
- package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.js +17 -15
- package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
- package/dist/store/scopes/thread.d.ts +1 -0
- package/dist/store/scopes/thread.d.ts.map +1 -1
- package/dist/subscribable/subscribable.d.ts +4 -4
- package/dist/subscribable/subscribable.d.ts.map +1 -1
- package/dist/subscribable/subscribable.js +14 -6
- package/dist/subscribable/subscribable.js.map +1 -1
- package/dist/tests/remote-thread-list-test-helpers.d.ts +2 -1
- package/dist/tests/remote-thread-list-test-helpers.d.ts.map +1 -1
- package/dist/tests/remote-thread-list-test-helpers.js +5 -2
- package/dist/tests/remote-thread-list-test-helpers.js.map +1 -1
- package/dist/types/attachment.d.ts +2 -1
- package/dist/types/attachment.d.ts.map +1 -1
- package/dist/types/attachment.js +6 -0
- package/dist/types/attachment.js.map +1 -0
- package/dist/types/message.d.ts +1 -0
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js.map +1 -1
- package/dist/utils/data-url.d.ts +9 -0
- package/dist/utils/data-url.d.ts.map +1 -0
- package/dist/utils/data-url.js +14 -0
- package/dist/utils/data-url.js.map +1 -0
- package/dist/utils/notify-event-listeners.d.ts +6 -0
- package/dist/utils/notify-event-listeners.d.ts.map +1 -0
- package/dist/utils/notify-event-listeners.js +16 -0
- package/dist/utils/notify-event-listeners.js.map +1 -0
- package/package.json +10 -9
- package/src/adapters/speech.test.ts +88 -0
- package/src/adapters/speech.ts +17 -2
- package/src/adapters/thread-history.ts +8 -0
- package/src/adapters/voice.test.ts +130 -0
- package/src/adapters/voice.ts +26 -6
- package/src/internal.ts +6 -1
- package/src/model-context/frame/host.test.ts +147 -0
- package/src/model-context/frame/host.ts +56 -19
- package/src/model-context/frame/provider.test.ts +83 -0
- package/src/model-context/frame/provider.ts +1 -0
- package/src/model-context/types.ts +16 -6
- package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +82 -0
- package/src/react/adapters/LocalStorageThreadListAdapter.tsx +108 -58
- package/src/react/primitive-hooks/useActionBarCopy.test.ts +79 -0
- package/src/react/primitive-hooks/useActionBarCopy.ts +4 -3
- package/src/react/primitives/chainOfThought/ChainOfThoughtParts.tsx +1 -3
- package/src/react/primitives/message/MessageParts.tsx +48 -35
- package/src/react/providers/ChainOfThoughtPartByIndexProvider.tsx +19 -2
- package/src/react/providers/PartByIndexProvider.tsx +16 -2
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +18 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +1 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +68 -13
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +34 -0
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +69 -0
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +27 -6
- package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +16 -0
- package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +7 -2
- package/src/react/runtimes/cloud/auiV0.ts +14 -0
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +90 -88
- package/src/react/runtimes/external-message-converter.test.ts +346 -0
- package/src/react/runtimes/external-message-converter.test.tsx +21 -0
- package/src/react/runtimes/external-message-converter.ts +2 -2
- package/src/react/runtimes/useLocalRuntime.test.tsx +99 -0
- package/src/react/runtimes/useLocalRuntime.ts +13 -1
- package/src/runtime/api/assistant-runtime.ts +4 -1
- package/src/runtime/api/attachment-runtime.ts +8 -2
- package/src/runtime/api/composer-runtime.ts +9 -5
- package/src/runtime/api/message-part-runtime.ts +10 -3
- package/src/runtime/api/message-runtime.test.ts +81 -0
- package/src/runtime/api/message-runtime.ts +11 -8
- package/src/runtime/api/thread-list-item-runtime.ts +13 -3
- package/src/runtime/api/thread-list-runtime.ts +10 -2
- package/src/runtime/base/base-composer-runtime-core.ts +77 -31
- package/src/runtime/base/base-thread-runtime-core.ts +7 -2
- package/src/runtime/base/default-edit-composer-runtime-core.ts +18 -4
- package/src/runtime/base/default-thread-composer-runtime-core.ts +13 -3
- package/src/runtime/utils/message-repository.ts +19 -13
- package/src/runtime/utils/thread-message-like.ts +1 -0
- package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +4 -1
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +15 -0
- package/src/runtimes/internal.ts +3 -0
- package/src/runtimes/local/local-thread-runtime-core.test.ts +746 -1
- package/src/runtimes/local/local-thread-runtime-core.ts +89 -23
- package/src/runtimes/local/should-continue.test.ts +122 -0
- package/src/runtimes/remote-thread-list/optimistic-state.test.ts +63 -0
- package/src/runtimes/remote-thread-list/optimistic-state.ts +49 -17
- package/src/runtimes/remote-thread-list/types.ts +1 -1
- package/src/runtimes/tool-invocations/EDGE_CASES.md +13 -19
- package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +128 -8
- package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +42 -34
- package/src/store/clients/model-context-client.test.ts +21 -1
- package/src/store/runtime-clients/thread-runtime-client.ts +2 -1
- package/src/store/scopes/thread.ts +1 -0
- package/src/subscribable/subscribable.test.ts +58 -0
- package/src/subscribable/subscribable.ts +29 -11
- package/src/tests/MessageRepository.test.ts +12 -0
- package/src/tests/OptimisticState-list-race.test.ts +33 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-switchToThread-order.test.ts +223 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-switchToThread-position.test.ts +121 -0
- package/src/tests/auiV0Encode.test.ts +74 -0
- package/src/tests/base-composer-runtime-core-addAttachment.test.ts +44 -0
- package/src/tests/base-composer-runtime-core-send.test.ts +433 -5
- package/src/tests/base-composer-runtime-core.test.ts +422 -0
- package/src/tests/default-edit-composer-runtime-core.test.ts +43 -5
- package/src/tests/event-subscription-listener-errors.test.ts +205 -0
- package/src/tests/external-store-thread-list-runtime-core.test.ts +6 -6
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +575 -0
- package/src/tests/external-store-thread-runtime-core.test.ts +55 -1
- package/src/tests/remote-thread-list-test-helpers.ts +14 -7
- package/src/tests/thread-message-like.test.ts +57 -0
- package/src/types/attachment.ts +5 -0
- package/src/types/message.ts +1 -0
- package/src/utils/data-url.test.ts +50 -0
- package/src/utils/data-url.ts +9 -0
- package/src/utils/notify-event-listeners.ts +30 -0
- package/src/tests/OptimisticState-delete-crash.test.ts +0 -208
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { DefaultThreadComposerRuntimeCore } from "../runtime/base/default-thread-composer-runtime-core";
|
|
3
3
|
import type { AttachmentAdapter } from "../adapters/attachment";
|
|
4
4
|
import type { ThreadRuntimeCore } from "../runtime/interfaces/thread-runtime-core";
|
|
@@ -21,8 +21,7 @@ const makeAdapter = (
|
|
|
21
21
|
...overrides,
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
const makeComposer = (adapter?: AttachmentAdapter) => {
|
|
25
|
-
const append = vi.fn();
|
|
24
|
+
const makeComposer = (adapter?: AttachmentAdapter, append = vi.fn()) => {
|
|
26
25
|
const runtime = {
|
|
27
26
|
append,
|
|
28
27
|
cancelRun: vi.fn(),
|
|
@@ -83,7 +82,7 @@ describe("BaseComposerRuntimeCore.send restore-on-failure", () => {
|
|
|
83
82
|
await expect(sendPromise).rejects.toThrow("upload failed");
|
|
84
83
|
|
|
85
84
|
expect(composer.text).toBe("new draft");
|
|
86
|
-
expect(composer.attachments).toHaveLength(
|
|
85
|
+
expect(composer.attachments).toHaveLength(1);
|
|
87
86
|
expect(append).not.toHaveBeenCalled();
|
|
88
87
|
});
|
|
89
88
|
|
|
@@ -108,7 +107,7 @@ describe("BaseComposerRuntimeCore.send restore-on-failure", () => {
|
|
|
108
107
|
|
|
109
108
|
expect(composer.quote).toEqual({ text: "new quote", messageId: "m-2" });
|
|
110
109
|
expect(composer.text).toBe("");
|
|
111
|
-
expect(composer.attachments).toHaveLength(
|
|
110
|
+
expect(composer.attachments).toHaveLength(1);
|
|
112
111
|
expect(append).not.toHaveBeenCalled();
|
|
113
112
|
});
|
|
114
113
|
|
|
@@ -129,6 +128,298 @@ describe("BaseComposerRuntimeCore.send restore-on-failure", () => {
|
|
|
129
128
|
expect(message.attachments[0].status).toEqual({ type: "complete" });
|
|
130
129
|
});
|
|
131
130
|
|
|
131
|
+
it("keeps the attachments visible until the upload resolves", async () => {
|
|
132
|
+
let resolveSend!: () => void;
|
|
133
|
+
const adapter = makeAdapter({
|
|
134
|
+
send: (a) =>
|
|
135
|
+
new Promise((resolve) => {
|
|
136
|
+
resolveSend = () =>
|
|
137
|
+
resolve({ ...a, status: { type: "complete" }, content: [] });
|
|
138
|
+
}),
|
|
139
|
+
});
|
|
140
|
+
const { composer, append } = makeComposer(adapter);
|
|
141
|
+
|
|
142
|
+
composer.setText("hello");
|
|
143
|
+
await composer.addAttachment(textFile());
|
|
144
|
+
|
|
145
|
+
const sendPromise = composer.send();
|
|
146
|
+
await Promise.resolve();
|
|
147
|
+
|
|
148
|
+
expect(composer.text).toBe("");
|
|
149
|
+
expect(composer.attachments).toHaveLength(1);
|
|
150
|
+
expect(append).not.toHaveBeenCalled();
|
|
151
|
+
|
|
152
|
+
resolveSend();
|
|
153
|
+
await sendPromise;
|
|
154
|
+
|
|
155
|
+
expect(composer.attachments).toHaveLength(0);
|
|
156
|
+
expect(append).toHaveBeenCalledTimes(1);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("keeps an attachment added while the upload was in flight", async () => {
|
|
160
|
+
let resolveSend!: () => void;
|
|
161
|
+
const adapter = makeAdapter({
|
|
162
|
+
add: async ({ file }: { file: File }): Promise<PendingAttachment> => ({
|
|
163
|
+
id: `att-${file.name}`,
|
|
164
|
+
type: "image",
|
|
165
|
+
name: file.name,
|
|
166
|
+
contentType: file.type,
|
|
167
|
+
file,
|
|
168
|
+
status: { type: "requires-action", reason: "composer-send" },
|
|
169
|
+
}),
|
|
170
|
+
send: (a) =>
|
|
171
|
+
new Promise((resolve) => {
|
|
172
|
+
resolveSend = () =>
|
|
173
|
+
resolve({ ...a, status: { type: "complete" }, content: [] });
|
|
174
|
+
}),
|
|
175
|
+
});
|
|
176
|
+
const { composer } = makeComposer(adapter);
|
|
177
|
+
|
|
178
|
+
await composer.addAttachment(textFile());
|
|
179
|
+
|
|
180
|
+
const sendPromise = composer.send();
|
|
181
|
+
await composer.addAttachment(new File(["x"], "later.txt"));
|
|
182
|
+
resolveSend();
|
|
183
|
+
await sendPromise;
|
|
184
|
+
|
|
185
|
+
expect(composer.attachments.map((a) => a.name)).toEqual(["later.txt"]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("ignores a second send while the first upload is still running", async () => {
|
|
189
|
+
let sendCalls = 0;
|
|
190
|
+
let resolveSend!: () => void;
|
|
191
|
+
const adapter = makeAdapter({
|
|
192
|
+
send: (a) => {
|
|
193
|
+
sendCalls += 1;
|
|
194
|
+
return new Promise((resolve) => {
|
|
195
|
+
resolveSend = () =>
|
|
196
|
+
resolve({ ...a, status: { type: "complete" }, content: [] });
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
const { composer, append } = makeComposer(adapter);
|
|
201
|
+
|
|
202
|
+
await composer.addAttachment(textFile());
|
|
203
|
+
|
|
204
|
+
const sendPromise = composer.send();
|
|
205
|
+
await composer.send();
|
|
206
|
+
resolveSend();
|
|
207
|
+
await sendPromise;
|
|
208
|
+
|
|
209
|
+
expect(sendCalls).toBe(1);
|
|
210
|
+
expect(append).toHaveBeenCalledTimes(1);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("reports canSend as false while a send is in flight", async () => {
|
|
214
|
+
let resolveSend!: () => void;
|
|
215
|
+
const adapter = makeAdapter({
|
|
216
|
+
send: (a) =>
|
|
217
|
+
new Promise((resolve) => {
|
|
218
|
+
resolveSend = () =>
|
|
219
|
+
resolve({ ...a, status: { type: "complete" }, content: [] });
|
|
220
|
+
}),
|
|
221
|
+
});
|
|
222
|
+
const { composer } = makeComposer(adapter);
|
|
223
|
+
|
|
224
|
+
composer.setText("hello");
|
|
225
|
+
await composer.addAttachment(textFile());
|
|
226
|
+
expect(composer.canSend).toBe(true);
|
|
227
|
+
|
|
228
|
+
const sendPromise = composer.send();
|
|
229
|
+
await Promise.resolve();
|
|
230
|
+
|
|
231
|
+
expect(composer.canSend).toBe(false);
|
|
232
|
+
|
|
233
|
+
resolveSend();
|
|
234
|
+
await sendPromise;
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("keeps sending blocked until every upload in a failed batch settles", async () => {
|
|
238
|
+
let resolveA!: () => void;
|
|
239
|
+
let sendCallsForA = 0;
|
|
240
|
+
const adapter = makeAdapter({
|
|
241
|
+
add: async ({ file }: { file: File }): Promise<PendingAttachment> => ({
|
|
242
|
+
id: `att-${file.name}`,
|
|
243
|
+
type: "image",
|
|
244
|
+
name: file.name,
|
|
245
|
+
contentType: file.type,
|
|
246
|
+
file,
|
|
247
|
+
status: { type: "requires-action", reason: "composer-send" },
|
|
248
|
+
}),
|
|
249
|
+
send: (a) => {
|
|
250
|
+
if (a.id === "att-a.txt") {
|
|
251
|
+
sendCallsForA += 1;
|
|
252
|
+
return new Promise((resolve) => {
|
|
253
|
+
resolveA = () =>
|
|
254
|
+
resolve({ ...a, status: { type: "complete" }, content: [] });
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
return Promise.reject(new Error("b failed"));
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
const { composer } = makeComposer(adapter);
|
|
261
|
+
|
|
262
|
+
await composer.addAttachment(new File(["a"], "a.txt"));
|
|
263
|
+
await composer.addAttachment(new File(["b"], "b.txt"));
|
|
264
|
+
|
|
265
|
+
const sendPromise = composer.send();
|
|
266
|
+
const caught = sendPromise.catch(() => {});
|
|
267
|
+
|
|
268
|
+
// b's rejection has already surfaced from Promise.all, but a's upload is
|
|
269
|
+
// still running — sending must stay blocked so a retry can't fire a second
|
|
270
|
+
// adapter.send for the attachment that's still in flight.
|
|
271
|
+
await Promise.resolve();
|
|
272
|
+
await Promise.resolve();
|
|
273
|
+
await Promise.resolve();
|
|
274
|
+
expect(composer.canSend).toBe(false);
|
|
275
|
+
|
|
276
|
+
resolveA();
|
|
277
|
+
await caught;
|
|
278
|
+
await vi.waitFor(() => expect(composer.canSend).toBe(true));
|
|
279
|
+
expect(sendCallsForA).toBe(1);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("restores the draft before a failed batch's stragglers settle", async () => {
|
|
283
|
+
let resolveA!: () => void;
|
|
284
|
+
const adapter = makeAdapter({
|
|
285
|
+
add: async ({ file }: { file: File }): Promise<PendingAttachment> => ({
|
|
286
|
+
id: `att-${file.name}`,
|
|
287
|
+
type: "image",
|
|
288
|
+
name: file.name,
|
|
289
|
+
contentType: file.type,
|
|
290
|
+
file,
|
|
291
|
+
status: { type: "requires-action", reason: "composer-send" },
|
|
292
|
+
}),
|
|
293
|
+
send: (a) => {
|
|
294
|
+
if (a.id === "att-a.txt") {
|
|
295
|
+
return new Promise((resolve) => {
|
|
296
|
+
resolveA = () =>
|
|
297
|
+
resolve({ ...a, status: { type: "complete" }, content: [] });
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return Promise.reject(new Error("b failed"));
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
const { composer } = makeComposer(adapter);
|
|
304
|
+
|
|
305
|
+
composer.setText("hello");
|
|
306
|
+
await composer.addAttachment(new File(["a"], "a.txt"));
|
|
307
|
+
await composer.addAttachment(new File(["b"], "b.txt"));
|
|
308
|
+
|
|
309
|
+
const sendPromise = composer.send();
|
|
310
|
+
await expect(sendPromise).rejects.toThrow("b failed");
|
|
311
|
+
|
|
312
|
+
expect(composer.text).toBe("hello");
|
|
313
|
+
expect(composer.canSend).toBe(false);
|
|
314
|
+
|
|
315
|
+
resolveA();
|
|
316
|
+
await vi.waitFor(() => expect(composer.canSend).toBe(true));
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("excludes a removed attachment even when the upload settles before the adapter remove", async () => {
|
|
320
|
+
let resolveSend!: () => void;
|
|
321
|
+
let resolveRemove!: () => void;
|
|
322
|
+
const adapter = makeAdapter({
|
|
323
|
+
send: (a) =>
|
|
324
|
+
new Promise((resolve) => {
|
|
325
|
+
resolveSend = () =>
|
|
326
|
+
resolve({ ...a, status: { type: "complete" }, content: [] });
|
|
327
|
+
}),
|
|
328
|
+
remove: () =>
|
|
329
|
+
new Promise((resolve) => {
|
|
330
|
+
resolveRemove = () => resolve();
|
|
331
|
+
}),
|
|
332
|
+
});
|
|
333
|
+
const { composer, append } = makeComposer(adapter);
|
|
334
|
+
|
|
335
|
+
composer.setText("hello");
|
|
336
|
+
await composer.addAttachment(textFile());
|
|
337
|
+
|
|
338
|
+
const sendPromise = composer.send();
|
|
339
|
+
const removePromise = composer.removeAttachment("att-1");
|
|
340
|
+
resolveSend();
|
|
341
|
+
await sendPromise;
|
|
342
|
+
|
|
343
|
+
expect(append).toHaveBeenCalledTimes(1);
|
|
344
|
+
expect(append.mock.calls[0]![0].attachments).toHaveLength(0);
|
|
345
|
+
|
|
346
|
+
resolveRemove();
|
|
347
|
+
await removePromise;
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it("releases the in-flight lock on reset so a stalled send cannot brick the composer", async () => {
|
|
351
|
+
const adapter = makeAdapter({
|
|
352
|
+
send: () => new Promise(() => {}),
|
|
353
|
+
});
|
|
354
|
+
const { composer, append } = makeComposer(adapter);
|
|
355
|
+
|
|
356
|
+
await composer.addAttachment(textFile());
|
|
357
|
+
void composer.send();
|
|
358
|
+
await Promise.resolve();
|
|
359
|
+
expect(composer.canSend).toBe(false);
|
|
360
|
+
|
|
361
|
+
await composer.reset();
|
|
362
|
+
composer.setText("again");
|
|
363
|
+
expect(composer.canSend).toBe(true);
|
|
364
|
+
|
|
365
|
+
await composer.send();
|
|
366
|
+
expect(append).toHaveBeenCalledTimes(1);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("discards a stalled send that settles after reset instead of appending it", async () => {
|
|
370
|
+
let resolveSend!: () => void;
|
|
371
|
+
const adapter = makeAdapter({
|
|
372
|
+
send: (a) =>
|
|
373
|
+
new Promise((resolve) => {
|
|
374
|
+
resolveSend = () =>
|
|
375
|
+
resolve({ ...a, status: { type: "complete" }, content: [] });
|
|
376
|
+
}),
|
|
377
|
+
});
|
|
378
|
+
const { composer, append } = makeComposer(adapter);
|
|
379
|
+
|
|
380
|
+
composer.setText("stale draft");
|
|
381
|
+
await composer.addAttachment(textFile());
|
|
382
|
+
void composer.send();
|
|
383
|
+
await Promise.resolve();
|
|
384
|
+
|
|
385
|
+
await composer.reset();
|
|
386
|
+
composer.setText("fresh draft");
|
|
387
|
+
await composer.send();
|
|
388
|
+
expect(append).toHaveBeenCalledTimes(1);
|
|
389
|
+
|
|
390
|
+
resolveSend();
|
|
391
|
+
await Promise.resolve();
|
|
392
|
+
await Promise.resolve();
|
|
393
|
+
|
|
394
|
+
expect(append).toHaveBeenCalledTimes(1);
|
|
395
|
+
expect(composer.canSend).toBe(false);
|
|
396
|
+
composer.setText("still unlocked");
|
|
397
|
+
expect(composer.canSend).toBe(true);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it("excludes an attachment removed while its upload was still in flight", async () => {
|
|
401
|
+
let resolveSend!: () => void;
|
|
402
|
+
const adapter = makeAdapter({
|
|
403
|
+
send: (a) =>
|
|
404
|
+
new Promise((resolve) => {
|
|
405
|
+
resolveSend = () =>
|
|
406
|
+
resolve({ ...a, status: { type: "complete" }, content: [] });
|
|
407
|
+
}),
|
|
408
|
+
});
|
|
409
|
+
const { composer, append } = makeComposer(adapter);
|
|
410
|
+
|
|
411
|
+
await composer.addAttachment(textFile());
|
|
412
|
+
|
|
413
|
+
const sendPromise = composer.send();
|
|
414
|
+
await composer.removeAttachment("att-1");
|
|
415
|
+
resolveSend();
|
|
416
|
+
await sendPromise;
|
|
417
|
+
|
|
418
|
+
expect(append).toHaveBeenCalledTimes(1);
|
|
419
|
+
const message = append.mock.calls[0]![0];
|
|
420
|
+
expect(message.attachments).toHaveLength(0);
|
|
421
|
+
});
|
|
422
|
+
|
|
132
423
|
it("sends a text-only message with no attachment adapter", async () => {
|
|
133
424
|
const { composer, append } = makeComposer();
|
|
134
425
|
|
|
@@ -142,4 +433,141 @@ describe("BaseComposerRuntimeCore.send restore-on-failure", () => {
|
|
|
142
433
|
{ type: "text", text: "hello" },
|
|
143
434
|
]);
|
|
144
435
|
});
|
|
436
|
+
|
|
437
|
+
it("observes asynchronous send tasks without waiting for them", async () => {
|
|
438
|
+
const sendTask = new Promise<void>(() => {});
|
|
439
|
+
const catchSpy = vi.spyOn(sendTask, "catch");
|
|
440
|
+
const { composer } = makeComposer();
|
|
441
|
+
vi.spyOn(composer, "handleSend").mockReturnValue(sendTask);
|
|
442
|
+
|
|
443
|
+
composer.setText("hello");
|
|
444
|
+
await expect(composer.send()).resolves.toBeUndefined();
|
|
445
|
+
|
|
446
|
+
expect(catchSpy).toHaveBeenCalledTimes(1);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it("tracks the append task returned by the thread runtime", async () => {
|
|
450
|
+
let resolveAppend!: () => void;
|
|
451
|
+
const appendTask = new Promise<void>((resolve) => {
|
|
452
|
+
resolveAppend = resolve;
|
|
453
|
+
});
|
|
454
|
+
const { composer } = makeComposer(
|
|
455
|
+
undefined,
|
|
456
|
+
vi.fn(() => appendTask),
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
const sendTask = composer.handleSend({
|
|
460
|
+
createdAt: new Date(),
|
|
461
|
+
role: "user",
|
|
462
|
+
content: [{ type: "text", text: "hello" }],
|
|
463
|
+
attachments: [],
|
|
464
|
+
runConfig: {},
|
|
465
|
+
metadata: { custom: {} },
|
|
466
|
+
});
|
|
467
|
+
let settled = false;
|
|
468
|
+
void sendTask.then(() => {
|
|
469
|
+
settled = true;
|
|
470
|
+
});
|
|
471
|
+
await Promise.resolve();
|
|
472
|
+
|
|
473
|
+
expect(settled).toBe(false);
|
|
474
|
+
|
|
475
|
+
resolveAppend();
|
|
476
|
+
await sendTask;
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it("does not leak a rejected append task as an unhandled rejection", async () => {
|
|
480
|
+
// A vi.fn mock attaches settled-result handlers to returned promises,
|
|
481
|
+
// marking the rejection as handled; a plain function keeps it unobserved.
|
|
482
|
+
let appendCalls = 0;
|
|
483
|
+
const runtime = {
|
|
484
|
+
append: () => {
|
|
485
|
+
appendCalls += 1;
|
|
486
|
+
return Promise.reject(new Error("append failed"));
|
|
487
|
+
},
|
|
488
|
+
cancelRun: () => {},
|
|
489
|
+
subscribe: () => () => {},
|
|
490
|
+
capabilities: { cancel: false },
|
|
491
|
+
messages: [],
|
|
492
|
+
getModelContext: () => ({ unstable_composerMetadata: undefined }),
|
|
493
|
+
} as unknown as Omit<ThreadRuntimeCore, "composer">;
|
|
494
|
+
const composer = new DefaultThreadComposerRuntimeCore(runtime);
|
|
495
|
+
|
|
496
|
+
const rejections: unknown[] = [];
|
|
497
|
+
const onUnhandledRejection = (reason: unknown) => {
|
|
498
|
+
rejections.push(reason);
|
|
499
|
+
};
|
|
500
|
+
const priorListeners = process.listeners("unhandledRejection");
|
|
501
|
+
process.removeAllListeners("unhandledRejection");
|
|
502
|
+
process.on("unhandledRejection", onUnhandledRejection);
|
|
503
|
+
try {
|
|
504
|
+
composer.setText("hello");
|
|
505
|
+
await composer.send();
|
|
506
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
507
|
+
} finally {
|
|
508
|
+
process.removeListener("unhandledRejection", onUnhandledRejection);
|
|
509
|
+
for (const listener of priorListeners) {
|
|
510
|
+
process.on("unhandledRejection", listener);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
expect(appendCalls).toBe(1);
|
|
515
|
+
expect(rejections).toEqual([]);
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
describe("BaseComposerRuntimeCore send event listener isolation", () => {
|
|
520
|
+
afterEach(() => {
|
|
521
|
+
vi.restoreAllMocks();
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it("isolates a throwing send listener so send() still resolves and later listeners run", async () => {
|
|
525
|
+
const listenerError = new Error("telemetry failed");
|
|
526
|
+
const consoleError = vi
|
|
527
|
+
.spyOn(console, "error")
|
|
528
|
+
.mockImplementation(() => {});
|
|
529
|
+
const laterListener = vi.fn();
|
|
530
|
+
const { composer, append } = makeComposer();
|
|
531
|
+
|
|
532
|
+
composer.unstable_on("send", () => {
|
|
533
|
+
throw listenerError;
|
|
534
|
+
});
|
|
535
|
+
composer.unstable_on("send", laterListener);
|
|
536
|
+
|
|
537
|
+
composer.setText("hello");
|
|
538
|
+
await expect(composer.send()).resolves.toBeUndefined();
|
|
539
|
+
|
|
540
|
+
expect(append).toHaveBeenCalledTimes(1);
|
|
541
|
+
expect(laterListener).toHaveBeenCalledOnce();
|
|
542
|
+
expect(consoleError).toHaveBeenCalledWith(
|
|
543
|
+
'[assistant-ui] Composer runtime "send" listener threw an error',
|
|
544
|
+
listenerError,
|
|
545
|
+
);
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it("isolates an async-rejecting send listener so send() still resolves and later listeners run", async () => {
|
|
549
|
+
const listenerError = new Error("async telemetry failed");
|
|
550
|
+
const consoleError = vi
|
|
551
|
+
.spyOn(console, "error")
|
|
552
|
+
.mockImplementation(() => {});
|
|
553
|
+
const laterListener = vi.fn();
|
|
554
|
+
const { composer, append } = makeComposer();
|
|
555
|
+
|
|
556
|
+
composer.unstable_on("send", async () => {
|
|
557
|
+
throw listenerError;
|
|
558
|
+
});
|
|
559
|
+
composer.unstable_on("send", laterListener);
|
|
560
|
+
|
|
561
|
+
composer.setText("hello");
|
|
562
|
+
await expect(composer.send()).resolves.toBeUndefined();
|
|
563
|
+
|
|
564
|
+
expect(append).toHaveBeenCalledTimes(1);
|
|
565
|
+
expect(laterListener).toHaveBeenCalledOnce();
|
|
566
|
+
await vi.waitFor(() => {
|
|
567
|
+
expect(consoleError).toHaveBeenCalledWith(
|
|
568
|
+
'[assistant-ui] Composer runtime "send" listener threw an error',
|
|
569
|
+
listenerError,
|
|
570
|
+
);
|
|
571
|
+
});
|
|
572
|
+
});
|
|
145
573
|
});
|