@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,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { AssistantFrameProvider } from "./provider";
|
|
6
|
+
import { FRAME_MESSAGE_CHANNEL } from "./types";
|
|
7
|
+
|
|
8
|
+
describe("AssistantFrameProvider", () => {
|
|
9
|
+
let messageHandler: ((event: MessageEvent) => void) | undefined;
|
|
10
|
+
let parentWindow: Window;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
parentWindow = {
|
|
14
|
+
postMessage: vi.fn(),
|
|
15
|
+
} as unknown as Window;
|
|
16
|
+
|
|
17
|
+
Object.defineProperty(window, "parent", {
|
|
18
|
+
value: parentWindow,
|
|
19
|
+
configurable: true,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
vi.spyOn(window, "addEventListener").mockImplementation(
|
|
23
|
+
(event, listener) => {
|
|
24
|
+
if (event === "message" && typeof listener === "function") {
|
|
25
|
+
messageHandler = listener as (event: MessageEvent) => void;
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
vi.spyOn(window, "removeEventListener").mockImplementation(() => {});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
AssistantFrameProvider.dispose();
|
|
34
|
+
vi.restoreAllMocks();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("only accepts tool calls from the parent window", async () => {
|
|
38
|
+
const execute = vi.fn(async () => "result");
|
|
39
|
+
AssistantFrameProvider.addModelContextProvider(
|
|
40
|
+
{
|
|
41
|
+
getModelContext: () => ({
|
|
42
|
+
tools: {
|
|
43
|
+
sensitiveTool: { execute },
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
},
|
|
47
|
+
"https://parent.example",
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const toolCall = {
|
|
51
|
+
channel: FRAME_MESSAGE_CHANNEL,
|
|
52
|
+
message: {
|
|
53
|
+
type: "tool-call",
|
|
54
|
+
id: "tool-call-1",
|
|
55
|
+
toolName: "sensitiveTool",
|
|
56
|
+
args: {},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
const otherWindow = {
|
|
60
|
+
postMessage: vi.fn(),
|
|
61
|
+
} as unknown as Window;
|
|
62
|
+
|
|
63
|
+
messageHandler?.(
|
|
64
|
+
new MessageEvent("message", {
|
|
65
|
+
data: toolCall,
|
|
66
|
+
origin: "https://parent.example",
|
|
67
|
+
source: otherWindow,
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
expect(execute).not.toHaveBeenCalled();
|
|
72
|
+
|
|
73
|
+
messageHandler?.(
|
|
74
|
+
new MessageEvent("message", {
|
|
75
|
+
data: toolCall,
|
|
76
|
+
origin: "https://parent.example",
|
|
77
|
+
source: parentWindow,
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
await vi.waitFor(() => expect(execute).toHaveBeenCalledOnce());
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -59,6 +59,7 @@ export class AssistantFrameProvider {
|
|
|
59
59
|
private handleMessage(event: MessageEvent) {
|
|
60
60
|
if (this._targetOrigin !== "*" && event.origin !== this._targetOrigin)
|
|
61
61
|
return;
|
|
62
|
+
if (event.source !== window.parent) return;
|
|
62
63
|
if (event.data?.channel !== FRAME_MESSAGE_CHANNEL) return;
|
|
63
64
|
|
|
64
65
|
const message = event.data.message as FrameMessage;
|
|
@@ -55,6 +55,12 @@ export type AssistantContextConfig = {
|
|
|
55
55
|
disabled?: boolean | undefined;
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
const stripOverwrite = (tool: Tool<any, any>): Tool<any, any> => {
|
|
59
|
+
if (!tool.overwrite) return tool;
|
|
60
|
+
const { overwrite: _, ...rest } = tool;
|
|
61
|
+
return rest as Tool<any, any>;
|
|
62
|
+
};
|
|
63
|
+
|
|
58
64
|
export const mergeModelContexts = (
|
|
59
65
|
configSet: Set<ModelContextProvider>,
|
|
60
66
|
): ModelContext => {
|
|
@@ -79,25 +85,29 @@ export const mergeModelContexts = (
|
|
|
79
85
|
if (existing && existing !== tool) {
|
|
80
86
|
const existingPriority = toolPriorities[name]!;
|
|
81
87
|
if (existingPriority === priority) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
88
|
+
if (!tool.overwrite) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`You tried to define a tool with the name ${name}, but it already exists.`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
acc.tools![name] = stripOverwrite(tool);
|
|
94
|
+
continue;
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
const higherPriorityTool =
|
|
88
98
|
existingPriority > priority ? existing : tool;
|
|
89
99
|
const lowerPriorityTool =
|
|
90
100
|
existingPriority > priority ? tool : existing;
|
|
91
|
-
acc.tools![name] = {
|
|
101
|
+
acc.tools![name] = stripOverwrite({
|
|
92
102
|
...lowerPriorityTool,
|
|
93
103
|
...higherPriorityTool,
|
|
94
|
-
} as Tool<any, any
|
|
104
|
+
} as Tool<any, any>);
|
|
95
105
|
toolPriorities[name] = Math.max(existingPriority, priority);
|
|
96
106
|
continue;
|
|
97
107
|
}
|
|
98
108
|
|
|
99
109
|
if (!acc.tools) acc.tools = {};
|
|
100
|
-
acc.tools[name] = tool;
|
|
110
|
+
acc.tools[name] = stripOverwrite(tool);
|
|
101
111
|
toolPriorities[name] ??= priority;
|
|
102
112
|
}
|
|
103
113
|
}
|
|
@@ -207,6 +207,88 @@ describe("createLocalStorageAdapter", () => {
|
|
|
207
207
|
]);
|
|
208
208
|
});
|
|
209
209
|
|
|
210
|
+
it("preserves concurrent metadata mutations across adapters", async () => {
|
|
211
|
+
const threadsKey = "@assistant-ui:threads";
|
|
212
|
+
const values = new Map<string, string>();
|
|
213
|
+
let metadataReads = 0;
|
|
214
|
+
let metadataWrites = 0;
|
|
215
|
+
let markFirstWriteStarted!: () => void;
|
|
216
|
+
let releaseFirstWrite!: () => void;
|
|
217
|
+
const firstWriteStarted = new Promise<void>((resolve) => {
|
|
218
|
+
markFirstWriteStarted = resolve;
|
|
219
|
+
});
|
|
220
|
+
const firstWriteCanFinish = new Promise<void>((resolve) => {
|
|
221
|
+
releaseFirstWrite = resolve;
|
|
222
|
+
});
|
|
223
|
+
const storage: AsyncStorageLike = {
|
|
224
|
+
getItem: async (key) => {
|
|
225
|
+
if (key === threadsKey) metadataReads += 1;
|
|
226
|
+
return values.get(key) ?? null;
|
|
227
|
+
},
|
|
228
|
+
setItem: async (key, value) => {
|
|
229
|
+
if (key === threadsKey) {
|
|
230
|
+
metadataWrites += 1;
|
|
231
|
+
if (metadataWrites === 1) {
|
|
232
|
+
markFirstWriteStarted();
|
|
233
|
+
await firstWriteCanFinish;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
values.set(key, value);
|
|
237
|
+
},
|
|
238
|
+
removeItem: async (key) => {
|
|
239
|
+
values.delete(key);
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
const firstAdapter = createLocalStorageAdapter({ storage });
|
|
243
|
+
const secondAdapter = createLocalStorageAdapter({ storage });
|
|
244
|
+
|
|
245
|
+
const firstInitialization = firstAdapter.initialize("thread-1");
|
|
246
|
+
await firstWriteStarted;
|
|
247
|
+
const secondInitialization = secondAdapter.initialize("thread-2");
|
|
248
|
+
const readsWhileFirstWritePending = metadataReads;
|
|
249
|
+
|
|
250
|
+
releaseFirstWrite();
|
|
251
|
+
await Promise.all([firstInitialization, secondInitialization]);
|
|
252
|
+
|
|
253
|
+
expect(readsWhileFirstWritePending).toBe(1);
|
|
254
|
+
expect(JSON.parse(values.get(threadsKey) ?? "")).toEqual([
|
|
255
|
+
{ remoteId: "thread-2", status: "regular" },
|
|
256
|
+
{ remoteId: "thread-1", status: "regular" },
|
|
257
|
+
]);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("continues processing mutations after a storage failure", async () => {
|
|
261
|
+
const threadsKey = "@assistant-ui:threads";
|
|
262
|
+
const values = new Map<string, string>();
|
|
263
|
+
let shouldFail = true;
|
|
264
|
+
const storage: AsyncStorageLike = {
|
|
265
|
+
getItem: async (key) => values.get(key) ?? null,
|
|
266
|
+
setItem: async (key, value) => {
|
|
267
|
+
if (shouldFail) {
|
|
268
|
+
shouldFail = false;
|
|
269
|
+
throw new Error("Storage unavailable");
|
|
270
|
+
}
|
|
271
|
+
values.set(key, value);
|
|
272
|
+
},
|
|
273
|
+
removeItem: async (key) => {
|
|
274
|
+
values.delete(key);
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
const adapter = createLocalStorageAdapter({ storage });
|
|
278
|
+
|
|
279
|
+
await expect(adapter.initialize("thread-1")).rejects.toThrow(
|
|
280
|
+
"Storage unavailable",
|
|
281
|
+
);
|
|
282
|
+
await expect(adapter.initialize("thread-2")).resolves.toEqual({
|
|
283
|
+
remoteId: "thread-2",
|
|
284
|
+
externalId: undefined,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
expect(JSON.parse(values.get(threadsKey) ?? "")).toEqual([
|
|
288
|
+
{ remoteId: "thread-2", status: "regular" },
|
|
289
|
+
]);
|
|
290
|
+
});
|
|
291
|
+
|
|
210
292
|
it("includes the thread id when a stored thread cannot be fetched", async () => {
|
|
211
293
|
const storage = createStorage({
|
|
212
294
|
"@assistant-ui:threads": JSON.stringify([
|
|
@@ -23,6 +23,37 @@ export type AsyncStorageLike = {
|
|
|
23
23
|
removeItem(key: string): Promise<void>;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
class KeyedMutationQueue {
|
|
27
|
+
private readonly tails = new Map<string, Promise<void>>();
|
|
28
|
+
|
|
29
|
+
run<T>(key: string, mutation: () => Promise<T>): Promise<T> {
|
|
30
|
+
const previous = this.tails.get(key);
|
|
31
|
+
const result = previous ? previous.then(mutation) : mutation();
|
|
32
|
+
const tail = result.then(
|
|
33
|
+
() => undefined,
|
|
34
|
+
() => undefined,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
this.tails.set(key, tail);
|
|
38
|
+
void tail.then(() => {
|
|
39
|
+
if (this.tails.get(key) === tail) this.tails.delete(key);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const mutationQueues = new WeakMap<AsyncStorageLike, KeyedMutationQueue>();
|
|
47
|
+
|
|
48
|
+
const getMutationQueue = (storage: AsyncStorageLike): KeyedMutationQueue => {
|
|
49
|
+
let queue = mutationQueues.get(storage);
|
|
50
|
+
if (!queue) {
|
|
51
|
+
queue = new KeyedMutationQueue();
|
|
52
|
+
mutationQueues.set(storage, queue);
|
|
53
|
+
}
|
|
54
|
+
return queue;
|
|
55
|
+
};
|
|
56
|
+
|
|
26
57
|
type LocalStorageAdapterOptions = {
|
|
27
58
|
storage: AsyncStorageLike;
|
|
28
59
|
prefix?: string | undefined;
|
|
@@ -244,11 +275,22 @@ export const parseStoredMessageRepository = (
|
|
|
244
275
|
};
|
|
245
276
|
|
|
246
277
|
class AsyncStorageHistoryAdapter implements ThreadHistoryAdapter {
|
|
278
|
+
private storage: AsyncStorageLike;
|
|
279
|
+
private aui: ReturnType<typeof useAui>;
|
|
280
|
+
private prefix: string;
|
|
281
|
+
private mutationQueue: KeyedMutationQueue;
|
|
282
|
+
|
|
247
283
|
constructor(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
284
|
+
storage: AsyncStorageLike,
|
|
285
|
+
aui: ReturnType<typeof useAui>,
|
|
286
|
+
prefix: string,
|
|
287
|
+
mutationQueue: KeyedMutationQueue,
|
|
288
|
+
) {
|
|
289
|
+
this.storage = storage;
|
|
290
|
+
this.aui = aui;
|
|
291
|
+
this.prefix = prefix;
|
|
292
|
+
this.mutationQueue = mutationQueue;
|
|
293
|
+
}
|
|
252
294
|
|
|
253
295
|
private _messagesKey(remoteId: string) {
|
|
254
296
|
return `${this.prefix}messages:${remoteId}`;
|
|
@@ -266,31 +308,34 @@ class AsyncStorageHistoryAdapter implements ThreadHistoryAdapter {
|
|
|
266
308
|
const { remoteId } = await this.aui.threadListItem().initialize();
|
|
267
309
|
|
|
268
310
|
const key = this._messagesKey(remoteId);
|
|
269
|
-
|
|
270
|
-
|
|
311
|
+
await this.mutationQueue.run(key, async () => {
|
|
312
|
+
const raw = await this.storage.getItem(key);
|
|
313
|
+
const repo = parseStoredMessageRepository(raw);
|
|
314
|
+
|
|
315
|
+
const idx = repo.messages.findIndex(
|
|
316
|
+
(m) => m.message.id === item.message.id,
|
|
317
|
+
);
|
|
318
|
+
if (idx >= 0) {
|
|
319
|
+
repo.messages[idx] = item;
|
|
320
|
+
} else {
|
|
321
|
+
repo.messages.push(item);
|
|
322
|
+
}
|
|
323
|
+
repo.headId = item.message.id;
|
|
271
324
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
);
|
|
275
|
-
if (idx >= 0) {
|
|
276
|
-
repo.messages[idx] = item;
|
|
277
|
-
} else {
|
|
278
|
-
repo.messages.push(item);
|
|
279
|
-
}
|
|
280
|
-
repo.headId = item.message.id;
|
|
281
|
-
|
|
282
|
-
await this.storage.setItem(key, JSON.stringify(repo));
|
|
325
|
+
await this.storage.setItem(key, JSON.stringify(repo));
|
|
326
|
+
});
|
|
283
327
|
}
|
|
284
328
|
}
|
|
285
329
|
|
|
286
330
|
const createHistoryProvider = (
|
|
287
331
|
storage: AsyncStorageLike,
|
|
288
332
|
prefix: string,
|
|
333
|
+
mutationQueue: KeyedMutationQueue,
|
|
289
334
|
): FC<PropsWithChildren> => {
|
|
290
335
|
const Provider: FC<PropsWithChildren> = ({ children }) => {
|
|
291
336
|
const aui = useAui();
|
|
292
337
|
const history = useMemo(
|
|
293
|
-
() => new AsyncStorageHistoryAdapter(storage, aui, prefix),
|
|
338
|
+
() => new AsyncStorageHistoryAdapter(storage, aui, prefix, mutationQueue),
|
|
294
339
|
[aui],
|
|
295
340
|
);
|
|
296
341
|
const adapters = useMemo(() => ({ history }), [history]);
|
|
@@ -311,6 +356,7 @@ export const createLocalStorageAdapter = (
|
|
|
311
356
|
|
|
312
357
|
const threadsKey = `${prefix}threads`;
|
|
313
358
|
const messagesKey = (threadId: string) => `${prefix}messages:${threadId}`;
|
|
359
|
+
const mutationQueue = getMutationQueue(storage);
|
|
314
360
|
|
|
315
361
|
const loadThreadMetadata = async (): Promise<StoredThreadMetadata[]> => {
|
|
316
362
|
const raw = await storage.getItem(threadsKey);
|
|
@@ -323,8 +369,22 @@ export const createLocalStorageAdapter = (
|
|
|
323
369
|
await storage.setItem(threadsKey, JSON.stringify(threads));
|
|
324
370
|
};
|
|
325
371
|
|
|
372
|
+
const updateThreadMetadata = async (
|
|
373
|
+
remoteId: string,
|
|
374
|
+
update: (thread: StoredThreadMetadata) => void,
|
|
375
|
+
): Promise<void> => {
|
|
376
|
+
await mutationQueue.run(threadsKey, async () => {
|
|
377
|
+
const threads = await loadThreadMetadata();
|
|
378
|
+
const thread = threads.find((item) => item.remoteId === remoteId);
|
|
379
|
+
if (thread) {
|
|
380
|
+
update(thread);
|
|
381
|
+
await saveThreadMetadata(threads);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
};
|
|
385
|
+
|
|
326
386
|
const adapter: RemoteThreadListAdapter = {
|
|
327
|
-
unstable_Provider: createHistoryProvider(storage, prefix),
|
|
387
|
+
unstable_Provider: createHistoryProvider(storage, prefix, mutationQueue),
|
|
328
388
|
|
|
329
389
|
async list(): Promise<RemoteThreadListResponse> {
|
|
330
390
|
const threads = await loadThreadMetadata();
|
|
@@ -343,64 +403,57 @@ export const createLocalStorageAdapter = (
|
|
|
343
403
|
threadId: string,
|
|
344
404
|
): Promise<RemoteThreadInitializeResponse> {
|
|
345
405
|
const remoteId = threadId;
|
|
346
|
-
|
|
406
|
+
return mutationQueue.run(threadsKey, async () => {
|
|
407
|
+
const threads = await loadThreadMetadata();
|
|
347
408
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
409
|
+
// Only add if not already present
|
|
410
|
+
if (!threads.some((t) => t.remoteId === remoteId)) {
|
|
411
|
+
threads.unshift({
|
|
412
|
+
remoteId,
|
|
413
|
+
status: "regular",
|
|
414
|
+
});
|
|
415
|
+
await saveThreadMetadata(threads);
|
|
416
|
+
}
|
|
356
417
|
|
|
357
|
-
|
|
418
|
+
return { remoteId, externalId: undefined };
|
|
419
|
+
});
|
|
358
420
|
},
|
|
359
421
|
|
|
360
422
|
async rename(remoteId: string, newTitle: string): Promise<void> {
|
|
361
|
-
|
|
362
|
-
const thread = threads.find((t) => t.remoteId === remoteId);
|
|
363
|
-
if (thread) {
|
|
423
|
+
await updateThreadMetadata(remoteId, (thread) => {
|
|
364
424
|
thread.title = newTitle;
|
|
365
|
-
|
|
366
|
-
}
|
|
425
|
+
});
|
|
367
426
|
},
|
|
368
427
|
|
|
369
428
|
async updateCustom(
|
|
370
429
|
remoteId: string,
|
|
371
430
|
custom: Record<string, unknown> | undefined,
|
|
372
431
|
): Promise<void> {
|
|
373
|
-
|
|
374
|
-
const thread = threads.find((t) => t.remoteId === remoteId);
|
|
375
|
-
if (thread) {
|
|
432
|
+
await updateThreadMetadata(remoteId, (thread) => {
|
|
376
433
|
thread.custom = custom;
|
|
377
|
-
|
|
378
|
-
}
|
|
434
|
+
});
|
|
379
435
|
},
|
|
380
436
|
|
|
381
437
|
async archive(remoteId: string): Promise<void> {
|
|
382
|
-
|
|
383
|
-
const thread = threads.find((t) => t.remoteId === remoteId);
|
|
384
|
-
if (thread) {
|
|
438
|
+
await updateThreadMetadata(remoteId, (thread) => {
|
|
385
439
|
thread.status = "archived";
|
|
386
|
-
|
|
387
|
-
}
|
|
440
|
+
});
|
|
388
441
|
},
|
|
389
442
|
|
|
390
443
|
async unarchive(remoteId: string): Promise<void> {
|
|
391
|
-
|
|
392
|
-
const thread = threads.find((t) => t.remoteId === remoteId);
|
|
393
|
-
if (thread) {
|
|
444
|
+
await updateThreadMetadata(remoteId, (thread) => {
|
|
394
445
|
thread.status = "regular";
|
|
395
|
-
|
|
396
|
-
}
|
|
446
|
+
});
|
|
397
447
|
},
|
|
398
448
|
|
|
399
449
|
async delete(remoteId: string): Promise<void> {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
450
|
+
await mutationQueue.run(threadsKey, async () => {
|
|
451
|
+
const threads = await loadThreadMetadata();
|
|
452
|
+
const filtered = threads.filter((t) => t.remoteId !== remoteId);
|
|
453
|
+
await saveThreadMetadata(filtered);
|
|
454
|
+
});
|
|
455
|
+
const key = messagesKey(remoteId);
|
|
456
|
+
await mutationQueue.run(key, () => storage.removeItem(key));
|
|
404
457
|
},
|
|
405
458
|
|
|
406
459
|
async fetch(threadId: string): Promise<RemoteThreadMetadata> {
|
|
@@ -427,12 +480,9 @@ export const createLocalStorageAdapter = (
|
|
|
427
480
|
const title = await titleGenerator.generateTitle(messages);
|
|
428
481
|
|
|
429
482
|
// Update the stored title
|
|
430
|
-
|
|
431
|
-
const thread = threads.find((t) => t.remoteId === remoteId);
|
|
432
|
-
if (thread) {
|
|
483
|
+
await updateThreadMetadata(remoteId, (thread) => {
|
|
433
484
|
thread.title = title;
|
|
434
|
-
|
|
435
|
-
}
|
|
485
|
+
});
|
|
436
486
|
|
|
437
487
|
// Return a stream with a single text part
|
|
438
488
|
return createAssistantStream((controller) => {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
const mocks = vi.hoisted(() => {
|
|
4
|
+
const setIsCopied = vi.fn();
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
setIsCopied,
|
|
8
|
+
state: {
|
|
9
|
+
message: {
|
|
10
|
+
role: "assistant",
|
|
11
|
+
status: { type: "complete", reason: "stop" },
|
|
12
|
+
parts: [{ type: "text", text: "Hello" }],
|
|
13
|
+
isCopied: false,
|
|
14
|
+
},
|
|
15
|
+
composer: {
|
|
16
|
+
isEditing: false,
|
|
17
|
+
text: "",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
aui: {
|
|
21
|
+
message: () => ({
|
|
22
|
+
getCopyText: () => "Hello",
|
|
23
|
+
setIsCopied,
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
vi.mock("react", async (importOriginal) => ({
|
|
30
|
+
...(await importOriginal<typeof import("react")>()),
|
|
31
|
+
useCallback: ((callback: unknown) =>
|
|
32
|
+
callback) as typeof import("react").useCallback,
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
vi.mock("@assistant-ui/store", async (importOriginal) => ({
|
|
36
|
+
...(await importOriginal<typeof import("@assistant-ui/store")>()),
|
|
37
|
+
useAui: () => mocks.aui,
|
|
38
|
+
useAuiState: ((selector: (state: typeof mocks.state) => unknown) =>
|
|
39
|
+
selector(mocks.state)) as typeof import("@assistant-ui/store").useAuiState,
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
import { useActionBarCopy } from "./useActionBarCopy";
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
vi.clearAllMocks();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("useActionBarCopy", () => {
|
|
49
|
+
it("does not report copy success without a clipboard handler", async () => {
|
|
50
|
+
const { copy, disabled } = useActionBarCopy();
|
|
51
|
+
|
|
52
|
+
copy();
|
|
53
|
+
await Promise.resolve();
|
|
54
|
+
|
|
55
|
+
expect(disabled).toBe(true);
|
|
56
|
+
expect(mocks.setIsCopied).not.toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("reports copy success after the clipboard handler resolves", async () => {
|
|
60
|
+
const copyToClipboard = vi.fn();
|
|
61
|
+
const { copy } = useActionBarCopy({ copyToClipboard });
|
|
62
|
+
|
|
63
|
+
copy();
|
|
64
|
+
await Promise.resolve();
|
|
65
|
+
|
|
66
|
+
expect(copyToClipboard).toHaveBeenCalledWith("Hello");
|
|
67
|
+
expect(mocks.setIsCopied).toHaveBeenCalledWith(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("does not report copy success when the clipboard handler rejects", async () => {
|
|
71
|
+
const copyToClipboard = vi.fn().mockRejectedValue(new Error("denied"));
|
|
72
|
+
const { copy } = useActionBarCopy({ copyToClipboard });
|
|
73
|
+
|
|
74
|
+
copy();
|
|
75
|
+
await Promise.resolve();
|
|
76
|
+
|
|
77
|
+
expect(mocks.setIsCopied).not.toHaveBeenCalled();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -23,13 +23,14 @@ export const useActionBarCopy = ({
|
|
|
23
23
|
const composerValue = useAuiState((s) => s.composer.text);
|
|
24
24
|
|
|
25
25
|
const copy = useCallback(() => {
|
|
26
|
+
if (!copyToClipboard) return;
|
|
27
|
+
|
|
26
28
|
const valueToCopy = isEditing ? composerValue : aui.message().getCopyText();
|
|
27
29
|
if (!valueToCopy) return;
|
|
28
30
|
|
|
29
|
-
const write = copyToClipboard ?? (() => {});
|
|
30
31
|
// The rejection handler swallows clipboard write failures (permission denied,
|
|
31
32
|
// API unavailable) so they don't surface as unhandled promise rejections.
|
|
32
|
-
Promise.resolve(
|
|
33
|
+
Promise.resolve(copyToClipboard(valueToCopy)).then(
|
|
33
34
|
() => {
|
|
34
35
|
aui.message().setIsCopied(true);
|
|
35
36
|
setTimeout(() => aui.message().setIsCopied(false), copiedDuration);
|
|
@@ -38,5 +39,5 @@ export const useActionBarCopy = ({
|
|
|
38
39
|
);
|
|
39
40
|
}, [aui, isEditing, composerValue, copiedDuration, copyToClipboard]);
|
|
40
41
|
|
|
41
|
-
return { copy, disabled, isCopied };
|
|
42
|
+
return { copy, disabled: disabled || !copyToClipboard, isCopied };
|
|
42
43
|
};
|
|
@@ -51,9 +51,7 @@ const ChainOfThoughtPrimitivePartsInner: FC<{
|
|
|
51
51
|
Array.from({ length: partsLength }, (_, index) => (
|
|
52
52
|
<ChainOfThoughtPartByIndexProvider key={index} index={index}>
|
|
53
53
|
<RenderChildrenWithAccessor
|
|
54
|
-
getItemState={(aui) =>
|
|
55
|
-
aui.chainOfThought().part({ index }).getState()
|
|
56
|
-
}
|
|
54
|
+
getItemState={(aui) => aui.part().getState()}
|
|
57
55
|
>
|
|
58
56
|
{(getItem) =>
|
|
59
57
|
children({
|