@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
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import type { RemoteThreadMetadata } from "../runtimes/remote-thread-list/types";
|
|
3
|
+
import {
|
|
4
|
+
createCore,
|
|
5
|
+
deferred,
|
|
6
|
+
makeAdapter,
|
|
7
|
+
setStartThreadRuntime,
|
|
8
|
+
} from "./remote-thread-list-test-helpers";
|
|
9
|
+
|
|
10
|
+
const thread = (id: string): RemoteThreadMetadata => ({
|
|
11
|
+
status: "regular",
|
|
12
|
+
remoteId: id,
|
|
13
|
+
externalId: id,
|
|
14
|
+
title: id,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("RemoteThreadListThreadListRuntimeCore.switchToThread order", () => {
|
|
18
|
+
it("preserves the latest switch when an earlier fetch resolves last", async () => {
|
|
19
|
+
const fetchB = deferred<RemoteThreadMetadata>();
|
|
20
|
+
const fetchC = deferred<RemoteThreadMetadata>();
|
|
21
|
+
const adapter = makeAdapter({
|
|
22
|
+
fetch: vi.fn((id: string) =>
|
|
23
|
+
id === "thread-b" ? fetchB.promise : fetchC.promise,
|
|
24
|
+
),
|
|
25
|
+
});
|
|
26
|
+
const core = createCore(adapter);
|
|
27
|
+
|
|
28
|
+
const switchToB = core.switchToThread("thread-b");
|
|
29
|
+
const switchToC = core.switchToThread("thread-c");
|
|
30
|
+
|
|
31
|
+
fetchC.resolve(thread("thread-c"));
|
|
32
|
+
await switchToC;
|
|
33
|
+
fetchB.resolve(thread("thread-b"));
|
|
34
|
+
await switchToB;
|
|
35
|
+
|
|
36
|
+
expect(core.mainThreadId).toBe(core.getItemById("thread-c")?.id);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("preserves the latest switch when an earlier runtime starts last", async () => {
|
|
40
|
+
const runtimeB = deferred<unknown>();
|
|
41
|
+
const runtimeC = deferred<unknown>();
|
|
42
|
+
const adapter = makeAdapter({
|
|
43
|
+
list: vi.fn(async () => ({
|
|
44
|
+
threads: [thread("thread-b"), thread("thread-c")],
|
|
45
|
+
})),
|
|
46
|
+
});
|
|
47
|
+
const core = createCore(adapter);
|
|
48
|
+
await core.getLoadThreadsPromise();
|
|
49
|
+
|
|
50
|
+
setStartThreadRuntime(core, (id) =>
|
|
51
|
+
id === core.getItemById("thread-b")?.id
|
|
52
|
+
? runtimeB.promise
|
|
53
|
+
: runtimeC.promise,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const switchToB = core.switchToThread("thread-b");
|
|
57
|
+
const switchToC = core.switchToThread("thread-c");
|
|
58
|
+
|
|
59
|
+
runtimeC.resolve({});
|
|
60
|
+
await switchToC;
|
|
61
|
+
runtimeB.resolve({});
|
|
62
|
+
await switchToB;
|
|
63
|
+
|
|
64
|
+
expect(core.mainThreadId).toBe(core.getItemById("thread-c")?.id);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("invalidates an earlier switch while a new thread request is pending", async () => {
|
|
68
|
+
const initializeThread = deferred<{
|
|
69
|
+
remoteId: string;
|
|
70
|
+
externalId: string;
|
|
71
|
+
}>();
|
|
72
|
+
const runtimeB = deferred<unknown>();
|
|
73
|
+
const adapter = makeAdapter({
|
|
74
|
+
initialize: vi.fn(() => initializeThread.promise),
|
|
75
|
+
list: vi.fn(async () => ({ threads: [thread("thread-b")] })),
|
|
76
|
+
});
|
|
77
|
+
const core = createCore(adapter);
|
|
78
|
+
await core.getLoadThreadsPromise();
|
|
79
|
+
|
|
80
|
+
const initialNewThreadId = core.newThreadId;
|
|
81
|
+
if (!initialNewThreadId) throw new Error("Expected an initial new thread");
|
|
82
|
+
|
|
83
|
+
const threadBId = core.getItemById("thread-b")?.id;
|
|
84
|
+
setStartThreadRuntime(core, (id) =>
|
|
85
|
+
id === threadBId ? runtimeB.promise : Promise.resolve({}),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const initialize = core.initialize(initialNewThreadId);
|
|
89
|
+
const switchToB = core.switchToThread("thread-b");
|
|
90
|
+
const switchToNewThread = core.switchToNewThread();
|
|
91
|
+
|
|
92
|
+
runtimeB.resolve({});
|
|
93
|
+
await switchToB;
|
|
94
|
+
|
|
95
|
+
expect(core.mainThreadId).not.toBe(threadBId);
|
|
96
|
+
|
|
97
|
+
initializeThread.resolve({
|
|
98
|
+
remoteId: "initialized-thread",
|
|
99
|
+
externalId: "initialized-thread",
|
|
100
|
+
});
|
|
101
|
+
await initialize;
|
|
102
|
+
await switchToNewThread;
|
|
103
|
+
|
|
104
|
+
expect(core.mainThreadId).toBe(core.getItemById(core.newThreadId!)?.id);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("does not let a pending new thread request override a later switch", async () => {
|
|
108
|
+
const initializeThread = deferred<{
|
|
109
|
+
remoteId: string;
|
|
110
|
+
externalId: string;
|
|
111
|
+
}>();
|
|
112
|
+
const adapter = makeAdapter({
|
|
113
|
+
initialize: vi.fn(() => initializeThread.promise),
|
|
114
|
+
list: vi.fn(async () => ({ threads: [thread("thread-c")] })),
|
|
115
|
+
});
|
|
116
|
+
const core = createCore(adapter);
|
|
117
|
+
await core.getLoadThreadsPromise();
|
|
118
|
+
|
|
119
|
+
const initialNewThreadId = core.newThreadId;
|
|
120
|
+
if (!initialNewThreadId) throw new Error("Expected an initial new thread");
|
|
121
|
+
|
|
122
|
+
const initialize = core.initialize(initialNewThreadId);
|
|
123
|
+
const switchToNewThread = core.switchToNewThread();
|
|
124
|
+
await core.switchToThread("thread-c");
|
|
125
|
+
|
|
126
|
+
initializeThread.resolve({
|
|
127
|
+
remoteId: "initialized-thread",
|
|
128
|
+
externalId: "initialized-thread",
|
|
129
|
+
});
|
|
130
|
+
await initialize;
|
|
131
|
+
await switchToNewThread;
|
|
132
|
+
|
|
133
|
+
expect(core.mainThreadId).toBe(core.getItemById("thread-c")?.id);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("keeps mutations waiting when a newer switch fails", async () => {
|
|
137
|
+
const fetchB = deferred<RemoteThreadMetadata>();
|
|
138
|
+
const newThreadRuntime = deferred<unknown>();
|
|
139
|
+
const adapter = makeAdapter({
|
|
140
|
+
fetch: vi.fn(() => fetchB.promise),
|
|
141
|
+
list: vi.fn(async () => ({ threads: [thread("thread-a")] })),
|
|
142
|
+
});
|
|
143
|
+
const core = createCore(adapter);
|
|
144
|
+
await core.getLoadThreadsPromise();
|
|
145
|
+
await core.switchToThread("thread-a");
|
|
146
|
+
|
|
147
|
+
const threadAId = core.getItemById("thread-a")?.id;
|
|
148
|
+
const newThreadId = core.newThreadId;
|
|
149
|
+
if (!newThreadId) throw new Error("Expected an initial new thread");
|
|
150
|
+
|
|
151
|
+
setStartThreadRuntime(core, (id) =>
|
|
152
|
+
id === core.getItemById(newThreadId)?.id
|
|
153
|
+
? newThreadRuntime.promise
|
|
154
|
+
: Promise.resolve({}),
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const deleteA = core.delete("thread-a");
|
|
158
|
+
const switchToB = core.switchToThread("thread-b");
|
|
159
|
+
const switchToBResult = expect(switchToB).rejects.toThrow("fetch failed");
|
|
160
|
+
|
|
161
|
+
newThreadRuntime.resolve({});
|
|
162
|
+
fetchB.reject(new Error("fetch failed"));
|
|
163
|
+
|
|
164
|
+
await switchToBResult;
|
|
165
|
+
await deleteA;
|
|
166
|
+
|
|
167
|
+
expect(core.mainThreadId).not.toBe(threadAId);
|
|
168
|
+
expect(core.mainThreadId).toBe(core.getItemById(core.newThreadId!)?.id);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("lets a newer successful switch move the mutation target away", async () => {
|
|
172
|
+
const fetchB = deferred<RemoteThreadMetadata>();
|
|
173
|
+
const newThreadRuntime = deferred<unknown>();
|
|
174
|
+
const adapter = makeAdapter({
|
|
175
|
+
fetch: vi.fn(() => fetchB.promise),
|
|
176
|
+
list: vi.fn(async () => ({ threads: [thread("thread-a")] })),
|
|
177
|
+
});
|
|
178
|
+
const core = createCore(adapter);
|
|
179
|
+
await core.getLoadThreadsPromise();
|
|
180
|
+
await core.switchToThread("thread-a");
|
|
181
|
+
|
|
182
|
+
const newThreadId = core.newThreadId;
|
|
183
|
+
if (!newThreadId) throw new Error("Expected an initial new thread");
|
|
184
|
+
|
|
185
|
+
setStartThreadRuntime(core, (id) =>
|
|
186
|
+
id === core.getItemById(newThreadId)?.id
|
|
187
|
+
? newThreadRuntime.promise
|
|
188
|
+
: Promise.resolve({}),
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const deleteA = core.delete("thread-a");
|
|
192
|
+
const switchToB = core.switchToThread("thread-b");
|
|
193
|
+
|
|
194
|
+
newThreadRuntime.resolve({});
|
|
195
|
+
fetchB.resolve(thread("thread-b"));
|
|
196
|
+
|
|
197
|
+
await switchToB;
|
|
198
|
+
await deleteA;
|
|
199
|
+
|
|
200
|
+
expect(core.mainThreadId).toBe(core.getItemById("thread-b")?.id);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("does not let a mutation cancel an existing pending switch", async () => {
|
|
204
|
+
const fetchB = deferred<RemoteThreadMetadata>();
|
|
205
|
+
const adapter = makeAdapter({
|
|
206
|
+
fetch: vi.fn(() => fetchB.promise),
|
|
207
|
+
list: vi.fn(async () => ({ threads: [thread("thread-a")] })),
|
|
208
|
+
});
|
|
209
|
+
const core = createCore(adapter);
|
|
210
|
+
await core.getLoadThreadsPromise();
|
|
211
|
+
await core.switchToThread("thread-a");
|
|
212
|
+
|
|
213
|
+
const switchToB = core.switchToThread("thread-b");
|
|
214
|
+
const deleteA = core.delete("thread-a");
|
|
215
|
+
|
|
216
|
+
fetchB.resolve(thread("thread-b"));
|
|
217
|
+
|
|
218
|
+
await switchToB;
|
|
219
|
+
await deleteA;
|
|
220
|
+
|
|
221
|
+
expect(core.mainThreadId).toBe(core.getItemById("thread-b")?.id);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import type {
|
|
3
|
+
RemoteThreadListResponse,
|
|
4
|
+
RemoteThreadMetadata,
|
|
5
|
+
} from "../runtimes/remote-thread-list/types";
|
|
6
|
+
import {
|
|
7
|
+
createCore,
|
|
8
|
+
deferred,
|
|
9
|
+
makeAdapter,
|
|
10
|
+
} from "./remote-thread-list-test-helpers";
|
|
11
|
+
|
|
12
|
+
const thread = (id: string): RemoteThreadMetadata => ({
|
|
13
|
+
status: "regular",
|
|
14
|
+
remoteId: id,
|
|
15
|
+
externalId: id,
|
|
16
|
+
title: id,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const archivedThread = (id: string): RemoteThreadMetadata => ({
|
|
20
|
+
status: "archived",
|
|
21
|
+
remoteId: id,
|
|
22
|
+
externalId: id,
|
|
23
|
+
title: id,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("RemoteThreadListThreadListRuntimeCore.switchToThread position", () => {
|
|
27
|
+
it("keeps the position a concurrent list() already gave the thread", async () => {
|
|
28
|
+
const TARGET = "thread-target";
|
|
29
|
+
const listDeferred = deferred<RemoteThreadListResponse>();
|
|
30
|
+
const fetchDeferred = deferred<RemoteThreadMetadata>();
|
|
31
|
+
|
|
32
|
+
const adapter = makeAdapter({
|
|
33
|
+
list: vi.fn(() => listDeferred.promise),
|
|
34
|
+
fetch: vi.fn(() => fetchDeferred.promise),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const core = createCore(adapter, TARGET);
|
|
38
|
+
|
|
39
|
+
const loadPromise = core.getLoadThreadsPromise();
|
|
40
|
+
const switchPromise = core.switchToThread(TARGET);
|
|
41
|
+
|
|
42
|
+
// `list()` resolves first and places TARGET at the top, as the most recent.
|
|
43
|
+
listDeferred.resolve({
|
|
44
|
+
threads: [thread(TARGET), thread("thread-a"), thread("thread-b")],
|
|
45
|
+
});
|
|
46
|
+
await loadPromise;
|
|
47
|
+
|
|
48
|
+
expect(core.threadIds).toEqual([TARGET, "thread-a", "thread-b"]);
|
|
49
|
+
|
|
50
|
+
fetchDeferred.resolve(thread(TARGET));
|
|
51
|
+
await switchPromise;
|
|
52
|
+
|
|
53
|
+
// The fetch path must not filter TARGET out of position and re-append it.
|
|
54
|
+
expect(core.threadIds).toEqual([TARGET, "thread-a", "thread-b"]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("appends a thread that is genuinely absent from the loaded list", async () => {
|
|
58
|
+
const TARGET = "thread-target";
|
|
59
|
+
const listDeferred = deferred<RemoteThreadListResponse>();
|
|
60
|
+
const fetchDeferred = deferred<RemoteThreadMetadata>();
|
|
61
|
+
|
|
62
|
+
const adapter = makeAdapter({
|
|
63
|
+
list: vi.fn(() => listDeferred.promise),
|
|
64
|
+
fetch: vi.fn(() => fetchDeferred.promise),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const core = createCore(adapter, TARGET);
|
|
68
|
+
|
|
69
|
+
const loadPromise = core.getLoadThreadsPromise();
|
|
70
|
+
const switchPromise = core.switchToThread(TARGET);
|
|
71
|
+
|
|
72
|
+
// `list()` resolves first but does not include TARGET (e.g. it is past the
|
|
73
|
+
// first page).
|
|
74
|
+
listDeferred.resolve({
|
|
75
|
+
threads: [thread("thread-a"), thread("thread-b")],
|
|
76
|
+
});
|
|
77
|
+
await loadPromise;
|
|
78
|
+
|
|
79
|
+
expect(core.threadIds).toEqual(["thread-a", "thread-b"]);
|
|
80
|
+
|
|
81
|
+
fetchDeferred.resolve(thread(TARGET));
|
|
82
|
+
await switchPromise;
|
|
83
|
+
|
|
84
|
+
// An absent thread may live on an unloaded page; appending keeps it below
|
|
85
|
+
// every loaded (newer) thread instead of pinning it to the top.
|
|
86
|
+
expect(core.threadIds).toEqual(["thread-a", "thread-b", TARGET]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("keeps the position a concurrent list() gave an archived thread", async () => {
|
|
90
|
+
const TARGET = "thread-target";
|
|
91
|
+
const listDeferred = deferred<RemoteThreadListResponse>();
|
|
92
|
+
const fetchDeferred = deferred<RemoteThreadMetadata>();
|
|
93
|
+
|
|
94
|
+
const adapter = makeAdapter({
|
|
95
|
+
list: vi.fn(() => listDeferred.promise),
|
|
96
|
+
fetch: vi.fn(() => fetchDeferred.promise),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const core = createCore(adapter, TARGET);
|
|
100
|
+
// Stub unarchive so auto-unarchive doesn't move the thread into `threadIds`
|
|
101
|
+
// via `updateStatusReducer`, hiding the position written by the fetch path.
|
|
102
|
+
(
|
|
103
|
+
core as unknown as { unarchive: (id: string) => Promise<void> }
|
|
104
|
+
).unarchive = async () => {};
|
|
105
|
+
|
|
106
|
+
const loadPromise = core.getLoadThreadsPromise();
|
|
107
|
+
const switchPromise = core.switchToThread(TARGET);
|
|
108
|
+
|
|
109
|
+
listDeferred.resolve({
|
|
110
|
+
threads: [archivedThread(TARGET), archivedThread("thread-a")],
|
|
111
|
+
});
|
|
112
|
+
await loadPromise;
|
|
113
|
+
|
|
114
|
+
expect(core.archivedThreadIds).toEqual([TARGET, "thread-a"]);
|
|
115
|
+
|
|
116
|
+
fetchDeferred.resolve(archivedThread(TARGET));
|
|
117
|
+
await switchPromise;
|
|
118
|
+
|
|
119
|
+
expect(core.archivedThreadIds).toEqual([TARGET, "thread-a"]);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -53,6 +53,35 @@ describe("auiV0Encode", () => {
|
|
|
53
53
|
]);
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
+
it("preserves a pending tool-call approval in the core cloud encoder", () => {
|
|
57
|
+
const encoded = auiV0Encode({
|
|
58
|
+
id: "m1",
|
|
59
|
+
createdAt: new Date("2026-03-15T00:00:00.000Z"),
|
|
60
|
+
role: "assistant",
|
|
61
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
62
|
+
metadata: {
|
|
63
|
+
unstable_state: null,
|
|
64
|
+
unstable_annotations: [],
|
|
65
|
+
unstable_data: [],
|
|
66
|
+
steps: [],
|
|
67
|
+
custom: {},
|
|
68
|
+
},
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: "tool-call",
|
|
72
|
+
toolCallId: "call-1",
|
|
73
|
+
toolName: "send_email",
|
|
74
|
+
args: {},
|
|
75
|
+
argsText: "{}",
|
|
76
|
+
approval: { id: "a1" },
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const toolCall = encoded.content.find((p) => p.type === "tool-call");
|
|
82
|
+
expect(toolCall).toMatchObject({ approval: { id: "a1" } });
|
|
83
|
+
});
|
|
84
|
+
|
|
56
85
|
it("preserves user attachments in the core cloud encoder", () => {
|
|
57
86
|
const encoded = auiV0Encode({
|
|
58
87
|
id: "m1",
|
|
@@ -129,6 +158,51 @@ describe("auiV0Encode", () => {
|
|
|
129
158
|
});
|
|
130
159
|
|
|
131
160
|
describe("auiV0Decode", () => {
|
|
161
|
+
it("round-trips a pending tool-call approval so a paused run stays resumable", () => {
|
|
162
|
+
const content = auiV0Encode({
|
|
163
|
+
id: "local",
|
|
164
|
+
createdAt: new Date("2026-03-15T00:00:00.000Z"),
|
|
165
|
+
role: "assistant",
|
|
166
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
167
|
+
metadata: {
|
|
168
|
+
unstable_state: null,
|
|
169
|
+
unstable_annotations: [],
|
|
170
|
+
unstable_data: [],
|
|
171
|
+
steps: [],
|
|
172
|
+
custom: {},
|
|
173
|
+
},
|
|
174
|
+
content: [
|
|
175
|
+
{
|
|
176
|
+
type: "tool-call",
|
|
177
|
+
toolCallId: "call-1",
|
|
178
|
+
toolName: "send_email",
|
|
179
|
+
args: {},
|
|
180
|
+
argsText: "{}",
|
|
181
|
+
approval: { id: "a1" },
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const decoded = auiV0Decode({
|
|
187
|
+
id: "cloud",
|
|
188
|
+
parent_id: null,
|
|
189
|
+
height: 0,
|
|
190
|
+
created_at: new Date("2026-03-15T00:00:00.000Z"),
|
|
191
|
+
updated_at: new Date("2026-03-15T00:00:00.000Z"),
|
|
192
|
+
format: "aui/v0",
|
|
193
|
+
content: content as never,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (decoded.message.role !== "assistant")
|
|
197
|
+
throw new Error("expected assistant");
|
|
198
|
+
const toolCall = decoded.message.content.find(
|
|
199
|
+
(p) => p.type === "tool-call",
|
|
200
|
+
);
|
|
201
|
+
expect(toolCall?.type === "tool-call" && toolCall.approval).toEqual({
|
|
202
|
+
id: "a1",
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
132
206
|
it("restores user attachments from core cloud history", () => {
|
|
133
207
|
const content = auiV0Encode({
|
|
134
208
|
id: "local",
|
|
@@ -128,6 +128,50 @@ describe("BaseComposerRuntimeCore.addAttachment error events", () => {
|
|
|
128
128
|
expect(onError).not.toHaveBeenCalled();
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
+
it("adds prepared attachments when File is unavailable", async () => {
|
|
132
|
+
const composer = makeComposer();
|
|
133
|
+
const fileConstructor = globalThis.File;
|
|
134
|
+
vi.stubGlobal("File", undefined);
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
await composer.addAttachment({
|
|
138
|
+
name: "notes.txt",
|
|
139
|
+
contentType: "text/plain",
|
|
140
|
+
content: [{ type: "text", text: "hello" }],
|
|
141
|
+
});
|
|
142
|
+
} finally {
|
|
143
|
+
vi.stubGlobal("File", fileConstructor);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
expect(composer.attachments[0]).toMatchObject({
|
|
147
|
+
type: "document",
|
|
148
|
+
name: "notes.txt",
|
|
149
|
+
contentType: "text/plain",
|
|
150
|
+
content: [{ type: "text", text: "hello" }],
|
|
151
|
+
status: { type: "complete" },
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("uses the adapter for foreign files that expose content", async () => {
|
|
156
|
+
const add = vi.fn(makeAdapter().add);
|
|
157
|
+
const composer = makeComposer(makeAdapter({ add }));
|
|
158
|
+
const foreignFile = {
|
|
159
|
+
name: "photo.png",
|
|
160
|
+
type: "image/png",
|
|
161
|
+
lastModified: 0,
|
|
162
|
+
content: [{ type: "text", text: "implementation detail" }],
|
|
163
|
+
} as File;
|
|
164
|
+
|
|
165
|
+
await composer.addAttachment(foreignFile);
|
|
166
|
+
|
|
167
|
+
expect(add).toHaveBeenCalledWith({ file: foreignFile });
|
|
168
|
+
expect(composer.attachments[0]).toMatchObject({
|
|
169
|
+
type: "image",
|
|
170
|
+
name: "photo.png",
|
|
171
|
+
contentType: "image/png",
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
131
175
|
it("accepts JSON files with the application/json media type", async () => {
|
|
132
176
|
const composer = makeComposer(new SimpleTextAttachmentAdapter());
|
|
133
177
|
|