@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
|
@@ -728,50 +728,63 @@ const EMPTY_RUNNING_TEXT_PART: Extract<EnrichedPartState, { type: "text" }> =
|
|
|
728
728
|
* `<MessagePrimitive.GroupedParts>`. Returns whatever `children`
|
|
729
729
|
* returns — callers decide how to handle a `null` return.
|
|
730
730
|
*/
|
|
731
|
-
|
|
731
|
+
type MessagePartChildrenProps = {
|
|
732
732
|
index: number;
|
|
733
733
|
children: (value: { part: EnrichedPartState }) => ReactNode;
|
|
734
|
-
}
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
const MessagePartChildrenInner: FC<
|
|
737
|
+
Pick<MessagePartChildrenProps, "children">
|
|
738
|
+
> = ({ children }) => {
|
|
735
739
|
const aui = useAui();
|
|
736
740
|
// Subscribed (not snapshotted like `tools`) so fallbacks registered
|
|
737
741
|
// after the first render trigger a re-render and `hasUI` re-evaluates.
|
|
738
742
|
const dataRenderers = useAuiState((s) => s.dataRenderers);
|
|
739
743
|
|
|
744
|
+
return (
|
|
745
|
+
<RenderChildrenWithAccessor
|
|
746
|
+
getItemState={(client) => client.part().getState()}
|
|
747
|
+
>
|
|
748
|
+
{(getItem) =>
|
|
749
|
+
children({
|
|
750
|
+
get part() {
|
|
751
|
+
const state = getItem();
|
|
752
|
+
if (state.type === "tool-call") {
|
|
753
|
+
const toolsState = aui.tools().getState();
|
|
754
|
+
const hasUI = resolveToolRender(toolsState, state) !== null;
|
|
755
|
+
const partMethods = aui.part();
|
|
756
|
+
return {
|
|
757
|
+
...state,
|
|
758
|
+
toolUI: hasUI ? <RegisteredToolUI /> : null,
|
|
759
|
+
addResult: partMethods.addToolResult,
|
|
760
|
+
resume: partMethods.resumeToolCall,
|
|
761
|
+
respondToApproval: partMethods.respondToToolApproval,
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
if (state.type === "data") {
|
|
765
|
+
const hasUI =
|
|
766
|
+
getDataRenderer(dataRenderers, state.name, undefined) !==
|
|
767
|
+
undefined;
|
|
768
|
+
return {
|
|
769
|
+
...state,
|
|
770
|
+
dataRendererUI: hasUI ? <RegisteredDataRendererUI /> : null,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
return state;
|
|
774
|
+
},
|
|
775
|
+
})
|
|
776
|
+
}
|
|
777
|
+
</RenderChildrenWithAccessor>
|
|
778
|
+
);
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
export const MessagePartChildren: FC<MessagePartChildrenProps> = ({
|
|
782
|
+
index,
|
|
783
|
+
children,
|
|
784
|
+
}) => {
|
|
740
785
|
return (
|
|
741
786
|
<PartByIndexProvider index={index}>
|
|
742
|
-
<
|
|
743
|
-
getItemState={(aui) => aui.message().part({ index }).getState()}
|
|
744
|
-
>
|
|
745
|
-
{(getItem) =>
|
|
746
|
-
children({
|
|
747
|
-
get part() {
|
|
748
|
-
const state = getItem();
|
|
749
|
-
if (state.type === "tool-call") {
|
|
750
|
-
const toolsState = aui.tools().getState();
|
|
751
|
-
const hasUI = resolveToolRender(toolsState, state) !== null;
|
|
752
|
-
const partMethods = aui.message().part({ index });
|
|
753
|
-
return {
|
|
754
|
-
...state,
|
|
755
|
-
toolUI: hasUI ? <RegisteredToolUI /> : null,
|
|
756
|
-
addResult: partMethods.addToolResult,
|
|
757
|
-
resume: partMethods.resumeToolCall,
|
|
758
|
-
respondToApproval: partMethods.respondToToolApproval,
|
|
759
|
-
};
|
|
760
|
-
}
|
|
761
|
-
if (state.type === "data") {
|
|
762
|
-
const hasUI =
|
|
763
|
-
getDataRenderer(dataRenderers, state.name, undefined) !==
|
|
764
|
-
undefined;
|
|
765
|
-
return {
|
|
766
|
-
...state,
|
|
767
|
-
dataRendererUI: hasUI ? <RegisteredDataRendererUI /> : null,
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
return state;
|
|
771
|
-
},
|
|
772
|
-
})
|
|
773
|
-
}
|
|
774
|
-
</RenderChildrenWithAccessor>
|
|
787
|
+
<MessagePartChildrenInner>{children}</MessagePartChildrenInner>
|
|
775
788
|
</PartByIndexProvider>
|
|
776
789
|
);
|
|
777
790
|
};
|
|
@@ -1,16 +1,33 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { useMemo, type FC, type PropsWithChildren } from "react";
|
|
2
2
|
import { useAui, AuiProvider, Derived } from "@assistant-ui/store";
|
|
3
|
+
import type { PartMethods } from "../../store/scopes/part";
|
|
3
4
|
|
|
4
5
|
export const ChainOfThoughtPartByIndexProvider: FC<
|
|
5
6
|
PropsWithChildren<{
|
|
6
7
|
index: number;
|
|
7
8
|
}>
|
|
8
9
|
> = ({ index, children }) => {
|
|
10
|
+
const lastPartRef = useMemo(
|
|
11
|
+
() => ({ index, current: null as PartMethods | null }),
|
|
12
|
+
[index],
|
|
13
|
+
);
|
|
9
14
|
const aui = useAui({
|
|
10
15
|
part: Derived({
|
|
11
16
|
source: "chainOfThought",
|
|
12
17
|
query: { type: "index", index },
|
|
13
|
-
get: (aui) =>
|
|
18
|
+
get: (aui) => {
|
|
19
|
+
const chainOfThought = aui.chainOfThought();
|
|
20
|
+
if (
|
|
21
|
+
index >= chainOfThought.getState().parts.length &&
|
|
22
|
+
lastPartRef.current
|
|
23
|
+
) {
|
|
24
|
+
return lastPartRef.current;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const part = chainOfThought.part({ index });
|
|
28
|
+
lastPartRef.current = part;
|
|
29
|
+
return part;
|
|
30
|
+
},
|
|
14
31
|
}),
|
|
15
32
|
});
|
|
16
33
|
|
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { useMemo, type FC, type PropsWithChildren } from "react";
|
|
2
2
|
import { useAui, AuiProvider, Derived } from "@assistant-ui/store";
|
|
3
|
+
import type { PartMethods } from "../../store/scopes/part";
|
|
3
4
|
|
|
4
5
|
export const PartByIndexProvider: FC<
|
|
5
6
|
PropsWithChildren<{
|
|
6
7
|
index: number;
|
|
7
8
|
}>
|
|
8
9
|
> = ({ index, children }) => {
|
|
10
|
+
const lastPartRef = useMemo(
|
|
11
|
+
() => ({ index, current: null as PartMethods | null }),
|
|
12
|
+
[index],
|
|
13
|
+
);
|
|
9
14
|
const aui = useAui({
|
|
10
15
|
part: Derived({
|
|
11
16
|
source: "message",
|
|
12
17
|
query: { type: "index", index },
|
|
13
|
-
get: (aui) =>
|
|
18
|
+
get: (aui) => {
|
|
19
|
+
const message = aui.message();
|
|
20
|
+
if (index >= message.getState().parts.length && lastPartRef.current) {
|
|
21
|
+
return lastPartRef.current;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const part = message.part({ index });
|
|
25
|
+
lastPartRef.current = part;
|
|
26
|
+
return part;
|
|
27
|
+
},
|
|
14
28
|
}),
|
|
15
29
|
});
|
|
16
30
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { ThreadListRuntimeCore } from "../../runtime/interfaces/thread-list-runtime-core";
|
|
3
|
+
import { RemoteThreadListHookInstanceManager } from "./RemoteThreadListHookInstanceManager";
|
|
4
|
+
|
|
5
|
+
describe("RemoteThreadListHookInstanceManager", () => {
|
|
6
|
+
it("rejects a pending start when the thread runtime is stopped", async () => {
|
|
7
|
+
const manager = new RemoteThreadListHookInstanceManager(() => {
|
|
8
|
+
throw new Error("Runtime hook should not render during this test");
|
|
9
|
+
}, {} as ThreadListRuntimeCore);
|
|
10
|
+
|
|
11
|
+
const startPromise = manager.startThreadRuntime("thread-1");
|
|
12
|
+
manager.stopThreadRuntime("thread-1");
|
|
13
|
+
|
|
14
|
+
await expect(startPromise).rejects.toThrow(
|
|
15
|
+
"Thread was deleted before runtime was started",
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -85,6 +85,7 @@ export class RemoteThreadListHookInstanceManager extends BaseSubscribable {
|
|
|
85
85
|
public stopThreadRuntime(threadId: string) {
|
|
86
86
|
this.instances.delete(threadId);
|
|
87
87
|
this.useAliveThreadsKeysChanged.setState({}, true);
|
|
88
|
+
this._notifySubscribers();
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
public setRuntimeHook(newRuntimeHook: RemoteThreadListHook) {
|
|
@@ -44,6 +44,8 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
44
44
|
private _loadThreadsPromise: Promise<void> | undefined;
|
|
45
45
|
private _loadMorePromise: Promise<void> | undefined;
|
|
46
46
|
private _loadGeneration = 0;
|
|
47
|
+
private _switchGeneration = 0;
|
|
48
|
+
private _switchTask: Promise<void> | undefined;
|
|
47
49
|
|
|
48
50
|
private _mainThreadId!: string;
|
|
49
51
|
private readonly _state = new OptimisticState<RemoteThreadState>({
|
|
@@ -307,15 +309,28 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
307
309
|
return getThreadData(this._state.value, threadIdOrRemoteId);
|
|
308
310
|
}
|
|
309
311
|
|
|
310
|
-
public
|
|
312
|
+
public switchToThread(
|
|
311
313
|
threadIdOrRemoteId: string,
|
|
312
314
|
options?: { unarchive?: boolean },
|
|
315
|
+
): Promise<void> {
|
|
316
|
+
const generation = ++this._switchGeneration;
|
|
317
|
+
const task = this._switchToThread(threadIdOrRemoteId, options, generation);
|
|
318
|
+
this._switchTask = task;
|
|
319
|
+
return task;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private async _switchToThread(
|
|
323
|
+
threadIdOrRemoteId: string,
|
|
324
|
+
options: { unarchive?: boolean } | undefined,
|
|
325
|
+
generation: number,
|
|
313
326
|
): Promise<void> {
|
|
314
327
|
let data = this.getItemById(threadIdOrRemoteId);
|
|
315
328
|
|
|
316
329
|
if (!data) {
|
|
317
330
|
const remoteMetadata =
|
|
318
331
|
await this._options.adapter.fetch(threadIdOrRemoteId);
|
|
332
|
+
if (generation !== this._switchGeneration) return;
|
|
333
|
+
|
|
319
334
|
const state = this._state.value;
|
|
320
335
|
const mappingId = createThreadMappingId(remoteMetadata.remoteId);
|
|
321
336
|
|
|
@@ -341,22 +356,36 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
341
356
|
[remoteMetadata.remoteId]: mappingId,
|
|
342
357
|
};
|
|
343
358
|
|
|
344
|
-
//
|
|
345
|
-
//
|
|
359
|
+
// A concurrent `list()` may already have placed this thread; keep that
|
|
360
|
+
// position and only merge metadata. A genuinely absent thread stays
|
|
361
|
+
// appended: it may live on an unloaded page, and a prepend would pin it
|
|
362
|
+
// above newer threads permanently since `classifyThreads` skips ids it
|
|
363
|
+
// has already seen. Filtering both arrays first still prevents
|
|
364
|
+
// duplication or a wrong-status entry from `list()`.
|
|
365
|
+
const remoteId = remoteMetadata.remoteId;
|
|
366
|
+
const wasInTarget =
|
|
367
|
+
remoteMetadata.status === "regular"
|
|
368
|
+
? state.threadIds.includes(remoteId)
|
|
369
|
+
: state.archivedThreadIds.includes(remoteId);
|
|
370
|
+
|
|
346
371
|
const threadIdsWithoutRemote = state.threadIds.filter(
|
|
347
|
-
(id) => id !==
|
|
372
|
+
(id) => id !== remoteId,
|
|
348
373
|
);
|
|
349
374
|
const archivedThreadIdsWithoutRemote = state.archivedThreadIds.filter(
|
|
350
|
-
(id) => id !==
|
|
375
|
+
(id) => id !== remoteId,
|
|
351
376
|
);
|
|
352
377
|
|
|
353
378
|
const newThreadIds =
|
|
354
379
|
remoteMetadata.status === "regular"
|
|
355
|
-
?
|
|
380
|
+
? wasInTarget
|
|
381
|
+
? state.threadIds
|
|
382
|
+
: [...threadIdsWithoutRemote, remoteId]
|
|
356
383
|
: threadIdsWithoutRemote;
|
|
357
384
|
const newArchivedThreadIds =
|
|
358
385
|
remoteMetadata.status === "archived"
|
|
359
|
-
?
|
|
386
|
+
? wasInTarget
|
|
387
|
+
? state.archivedThreadIds
|
|
388
|
+
: [...archivedThreadIdsWithoutRemote, remoteId]
|
|
360
389
|
: archivedThreadIdsWithoutRemote;
|
|
361
390
|
|
|
362
391
|
this._state.update({
|
|
@@ -380,8 +409,11 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
380
409
|
task.then(() => this._notifySubscribers());
|
|
381
410
|
}
|
|
382
411
|
|
|
412
|
+
if (generation !== this._switchGeneration) return;
|
|
413
|
+
|
|
383
414
|
if (data.status === "archived" && options?.unarchive !== false) {
|
|
384
415
|
await this.unarchive(data.id);
|
|
416
|
+
if (generation !== this._switchGeneration) return;
|
|
385
417
|
}
|
|
386
418
|
this._mainThreadId = data.id;
|
|
387
419
|
|
|
@@ -389,13 +421,21 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
389
421
|
this._notifyThreadIdChange();
|
|
390
422
|
}
|
|
391
423
|
|
|
392
|
-
public
|
|
424
|
+
public switchToNewThread(): Promise<void> {
|
|
425
|
+
const generation = ++this._switchGeneration;
|
|
426
|
+
const task = this._switchToNewThread(generation);
|
|
427
|
+
this._switchTask = task;
|
|
428
|
+
return task;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private async _switchToNewThread(generation: number): Promise<void> {
|
|
393
432
|
// an initialization transaction is in progress, wait for it to settle
|
|
394
433
|
while (
|
|
395
434
|
this._state.baseValue.newThreadId !== undefined &&
|
|
396
435
|
this._state.value.newThreadId === undefined
|
|
397
436
|
) {
|
|
398
437
|
await this._state.waitForUpdate();
|
|
438
|
+
if (generation !== this._switchGeneration) return;
|
|
399
439
|
}
|
|
400
440
|
|
|
401
441
|
const state = this._state.value;
|
|
@@ -427,7 +467,7 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
427
467
|
});
|
|
428
468
|
}
|
|
429
469
|
|
|
430
|
-
return this.
|
|
470
|
+
return this._switchToThread(id, undefined, generation);
|
|
431
471
|
}
|
|
432
472
|
|
|
433
473
|
public initialize = async (threadId: string) => {
|
|
@@ -436,10 +476,11 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
436
476
|
if (!data) throw threadNotFoundError(threadId, "initializing it");
|
|
437
477
|
if (data.status === "new")
|
|
438
478
|
throw threadStatusError(threadId, data.status, "be initialized here");
|
|
439
|
-
|
|
479
|
+
const { remoteId, externalId } = await data.initializeTask;
|
|
480
|
+
return { remoteId, externalId };
|
|
440
481
|
}
|
|
441
482
|
|
|
442
|
-
|
|
483
|
+
const { remoteId, externalId } = await this._state.optimisticUpdate({
|
|
443
484
|
execute: () => {
|
|
444
485
|
return this._options.adapter.initialize(threadId);
|
|
445
486
|
},
|
|
@@ -482,6 +523,7 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
482
523
|
};
|
|
483
524
|
},
|
|
484
525
|
});
|
|
526
|
+
return { remoteId, externalId };
|
|
485
527
|
};
|
|
486
528
|
|
|
487
529
|
public generateTitle = async (threadId: string) => {
|
|
@@ -604,8 +646,21 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
604
646
|
if (threadId === this.newThreadId)
|
|
605
647
|
throw new Error("Cannot ensure new thread is not main");
|
|
606
648
|
|
|
607
|
-
|
|
608
|
-
|
|
649
|
+
let lastAwaitedTask: Promise<void> | undefined;
|
|
650
|
+
|
|
651
|
+
while (threadId === this._mainThreadId) {
|
|
652
|
+
let switchTask = this._switchTask;
|
|
653
|
+
const startedFallback = !switchTask || switchTask === lastAwaitedTask;
|
|
654
|
+
if (startedFallback) switchTask = this.switchToNewThread();
|
|
655
|
+
lastAwaitedTask = switchTask;
|
|
656
|
+
|
|
657
|
+
try {
|
|
658
|
+
await switchTask;
|
|
659
|
+
} catch (error) {
|
|
660
|
+
if (startedFallback && this._switchTask === switchTask) {
|
|
661
|
+
throw error;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
609
664
|
}
|
|
610
665
|
}
|
|
611
666
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { extractAuiV0 } from "./AssistantCloudThreadHistoryAdapter";
|
|
3
|
+
|
|
4
|
+
const auiV0Message = (status: { type: string; reason?: string }) => ({
|
|
5
|
+
role: "assistant",
|
|
6
|
+
status,
|
|
7
|
+
content: [
|
|
8
|
+
{
|
|
9
|
+
type: "tool-call",
|
|
10
|
+
toolCallId: "call-1",
|
|
11
|
+
toolName: "send_email",
|
|
12
|
+
args: {},
|
|
13
|
+
argsText: "{}",
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
metadata: { steps: [{ usage: { inputTokens: 1, outputTokens: 2 } }] },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("extractAuiV0", () => {
|
|
20
|
+
it("returns null for a requires-action message so paused writes never report", () => {
|
|
21
|
+
expect(
|
|
22
|
+
extractAuiV0(
|
|
23
|
+
auiV0Message({ type: "requires-action", reason: "tool-calls" }),
|
|
24
|
+
),
|
|
25
|
+
).toBeNull();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("reports a terminal message once with its usage", () => {
|
|
29
|
+
const result = extractAuiV0(auiV0Message({ type: "complete" }));
|
|
30
|
+
expect(result?.status).toBe("completed");
|
|
31
|
+
expect(result?.inputTokens).toBe(1);
|
|
32
|
+
expect(result?.outputTokens).toBe(2);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { renderHook } from "@testing-library/react";
|
|
4
|
+
import type { AssistantCloud } from "assistant-cloud";
|
|
5
|
+
import { describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { useAssistantCloudThreadHistoryAdapter } from "./AssistantCloudThreadHistoryAdapter";
|
|
7
|
+
|
|
8
|
+
const mocks = vi.hoisted(() => {
|
|
9
|
+
const threadListItem = {
|
|
10
|
+
getState: () => ({ remoteId: "thread-1" }),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
assistantClient: {
|
|
15
|
+
threadListItem: () => threadListItem,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
vi.mock("@assistant-ui/store", async (importOriginal) => ({
|
|
21
|
+
...(await importOriginal<typeof import("@assistant-ui/store")>()),
|
|
22
|
+
useAui: () => mocks.assistantClient,
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
const makeCloud = () =>
|
|
26
|
+
({
|
|
27
|
+
threads: {
|
|
28
|
+
messages: {
|
|
29
|
+
list: vi.fn().mockResolvedValue({ messages: [] }),
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
}) as unknown as AssistantCloud;
|
|
33
|
+
|
|
34
|
+
describe("useAssistantCloudThreadHistoryAdapter", () => {
|
|
35
|
+
it("refreshes formatted persistence when the Cloud client changes", async () => {
|
|
36
|
+
const firstCloud = makeCloud();
|
|
37
|
+
const secondCloud = makeCloud();
|
|
38
|
+
const cloudRef = { current: firstCloud };
|
|
39
|
+
const { result } = renderHook(() =>
|
|
40
|
+
useAssistantCloudThreadHistoryAdapter(cloudRef),
|
|
41
|
+
);
|
|
42
|
+
const formatted = result.current.withFormat<
|
|
43
|
+
{ id: string },
|
|
44
|
+
Record<string, unknown>
|
|
45
|
+
>({
|
|
46
|
+
format: "test",
|
|
47
|
+
encode: ({ message }) => message,
|
|
48
|
+
decode: ({ parent_id, content }) => ({
|
|
49
|
+
parentId: parent_id,
|
|
50
|
+
message: content as { id: string },
|
|
51
|
+
}),
|
|
52
|
+
getId: (message) => message.id,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await formatted.load();
|
|
56
|
+
await formatted.load();
|
|
57
|
+
|
|
58
|
+
expect(firstCloud.threads.messages.list).toHaveBeenCalledTimes(2);
|
|
59
|
+
|
|
60
|
+
cloudRef.current = secondCloud;
|
|
61
|
+
await formatted.load();
|
|
62
|
+
|
|
63
|
+
expect(firstCloud.threads.messages.list).toHaveBeenCalledTimes(2);
|
|
64
|
+
expect(secondCloud.threads.messages.list).toHaveBeenCalledOnce();
|
|
65
|
+
expect(secondCloud.threads.messages.list).toHaveBeenCalledWith("thread-1", {
|
|
66
|
+
format: "test",
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -22,17 +22,20 @@ const globalPersistence = new WeakMap<
|
|
|
22
22
|
>();
|
|
23
23
|
|
|
24
24
|
class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
) {
|
|
25
|
+
private cloudRef: RefObject<AssistantCloud>;
|
|
26
|
+
private aui: AssistantClient;
|
|
27
|
+
|
|
28
|
+
constructor(cloudRef: RefObject<AssistantCloud>, aui: AssistantClient) {
|
|
29
|
+
this.cloudRef = cloudRef;
|
|
30
|
+
this.aui = aui;
|
|
31
|
+
}
|
|
29
32
|
|
|
30
33
|
private get _persistence(): CloudMessagePersistence {
|
|
31
34
|
const key = this.aui.threadListItem();
|
|
32
35
|
if (!globalPersistence.has(key)) {
|
|
33
36
|
globalPersistence.set(
|
|
34
37
|
key,
|
|
35
|
-
new CloudMessagePersistence(this.cloudRef.current),
|
|
38
|
+
new CloudMessagePersistence(() => this.cloudRef.current),
|
|
36
39
|
);
|
|
37
40
|
}
|
|
38
41
|
return globalPersistence.get(key)!;
|
|
@@ -102,6 +105,21 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
|
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
|
|
108
|
+
async update(item: ExportedMessageRepositoryItem) {
|
|
109
|
+
if (!this._persistence.isPersisted(item.message.id)) {
|
|
110
|
+
return this.append(item);
|
|
111
|
+
}
|
|
112
|
+
const { message } = item;
|
|
113
|
+
const remoteId = this.aui.threadListItem().getState().remoteId;
|
|
114
|
+
if (!remoteId) return;
|
|
115
|
+
const encoded = auiV0Encode(message);
|
|
116
|
+
await this._persistence.update(remoteId, message.id, "aui/v0", encoded);
|
|
117
|
+
|
|
118
|
+
if (this.cloudRef.current.telemetry.enabled) {
|
|
119
|
+
this._maybeReportRun(remoteId, "aui/v0", encoded);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
105
123
|
async delete() {
|
|
106
124
|
throw new Error(
|
|
107
125
|
"Assistant Cloud does not support deleting thread messages yet.",
|
|
@@ -341,7 +359,7 @@ const AUI_STATUS_MAP: Record<string, TelemetryData["status"]> = {
|
|
|
341
359
|
incomplete: "incomplete",
|
|
342
360
|
};
|
|
343
361
|
|
|
344
|
-
function extractAuiV0<T>(content: T): TelemetryData | null {
|
|
362
|
+
export function extractAuiV0<T>(content: T): TelemetryData | null {
|
|
345
363
|
const msg = content as {
|
|
346
364
|
role?: string;
|
|
347
365
|
status?: { type: string };
|
|
@@ -369,6 +387,9 @@ function extractAuiV0<T>(content: T): TelemetryData | null {
|
|
|
369
387
|
};
|
|
370
388
|
|
|
371
389
|
if (msg.role !== "assistant") return null;
|
|
390
|
+
// A paused (requires-action) write is not a finished run; reporting it would
|
|
391
|
+
// mislabel it "completed" and double-count steps once the terminal write reports.
|
|
392
|
+
if (msg.status?.type === "requires-action") return null;
|
|
372
393
|
|
|
373
394
|
const toolCalls = msg.content
|
|
374
395
|
?.filter((p) => p.type === "tool-call" && p.toolName && p.toolCallId)
|
|
@@ -57,6 +57,22 @@ describe("CloudFileAttachmentAdapter", () => {
|
|
|
57
57
|
]);
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
it("uploads when Web Crypto is unavailable", async () => {
|
|
61
|
+
vi.stubGlobal("crypto", undefined);
|
|
62
|
+
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true }));
|
|
63
|
+
const adapter = new CloudFileAttachmentAdapter(makeCloud());
|
|
64
|
+
|
|
65
|
+
const yields = await drain(adapter);
|
|
66
|
+
|
|
67
|
+
expect(yields).toHaveLength(2);
|
|
68
|
+
expect(yields[0]?.id).toBeTruthy();
|
|
69
|
+
expect(yields[1]?.id).toBe(yields[0]?.id);
|
|
70
|
+
expect(yields.at(-1)?.status).toEqual({
|
|
71
|
+
type: "requires-action",
|
|
72
|
+
reason: "composer-send",
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
60
76
|
it("marks the attachment incomplete when the upload returns an HTTP error", async () => {
|
|
61
77
|
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
62
78
|
vi.stubGlobal(
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
} from "../../../types/attachment";
|
|
7
7
|
import type { ThreadUserMessagePart } from "../../../types/message";
|
|
8
8
|
import type { AttachmentAdapter } from "../../../adapters/attachment";
|
|
9
|
+
import { generateId } from "../../../utils/id";
|
|
9
10
|
|
|
10
11
|
const guessAttachmentType = (
|
|
11
12
|
contentType: string,
|
|
@@ -18,7 +19,11 @@ const guessAttachmentType = (
|
|
|
18
19
|
export class CloudFileAttachmentAdapter implements AttachmentAdapter {
|
|
19
20
|
public accept = "*";
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
private cloud: AssistantCloud;
|
|
23
|
+
|
|
24
|
+
constructor(cloud: AssistantCloud) {
|
|
25
|
+
this.cloud = cloud;
|
|
26
|
+
}
|
|
22
27
|
|
|
23
28
|
private uploadedUrls = new Map<string, string>();
|
|
24
29
|
|
|
@@ -27,7 +32,7 @@ export class CloudFileAttachmentAdapter implements AttachmentAdapter {
|
|
|
27
32
|
}: {
|
|
28
33
|
file: File;
|
|
29
34
|
}): AsyncGenerator<PendingAttachment, void> {
|
|
30
|
-
const id =
|
|
35
|
+
const id = generateId();
|
|
31
36
|
const type = guessAttachmentType(file.type);
|
|
32
37
|
let attachment: PendingAttachment = {
|
|
33
38
|
id,
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
SourceProviderMetadata,
|
|
7
7
|
ThreadMessage,
|
|
8
8
|
TextMessagePart,
|
|
9
|
+
ToolApprovalOption,
|
|
9
10
|
Unstable_AudioMessagePart,
|
|
10
11
|
} from "../../../types/message";
|
|
11
12
|
import type { CompleteAttachment } from "../../../types/attachment";
|
|
@@ -18,6 +19,16 @@ import type {
|
|
|
18
19
|
} from "assistant-stream/utils";
|
|
19
20
|
import type { ExportedMessageRepositoryItem } from "../../../runtime/utils/message-repository";
|
|
20
21
|
|
|
22
|
+
type AuiV0ToolApproval = {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly approved?: boolean;
|
|
25
|
+
readonly reason?: string;
|
|
26
|
+
readonly isAutomatic?: boolean;
|
|
27
|
+
readonly options?: readonly ToolApprovalOption[];
|
|
28
|
+
readonly optionId?: string;
|
|
29
|
+
readonly resolution?: "cancelled" | "expired";
|
|
30
|
+
};
|
|
31
|
+
|
|
21
32
|
type AuiV0MessagePart =
|
|
22
33
|
| {
|
|
23
34
|
readonly type: "text";
|
|
@@ -51,6 +62,7 @@ type AuiV0MessagePart =
|
|
|
51
62
|
readonly args: ReadonlyJSONObject;
|
|
52
63
|
readonly result?: ReadonlyJSONValue;
|
|
53
64
|
readonly isError?: true;
|
|
65
|
+
readonly approval?: AuiV0ToolApproval;
|
|
54
66
|
}
|
|
55
67
|
| {
|
|
56
68
|
readonly type: "tool-call";
|
|
@@ -59,6 +71,7 @@ type AuiV0MessagePart =
|
|
|
59
71
|
readonly argsText: string;
|
|
60
72
|
readonly result?: ReadonlyJSONValue;
|
|
61
73
|
readonly isError?: true;
|
|
74
|
+
readonly approval?: AuiV0ToolApproval;
|
|
62
75
|
}
|
|
63
76
|
| {
|
|
64
77
|
readonly type: "image";
|
|
@@ -216,6 +229,7 @@ export function auiV0Encode(message: ThreadMessage): AuiV0Message {
|
|
|
216
229
|
? { result: part.result as ReadonlyJSONValue }
|
|
217
230
|
: undefined),
|
|
218
231
|
...(part.isError ? { isError: true } : undefined),
|
|
232
|
+
...(part.approval ? { approval: part.approval } : undefined),
|
|
219
233
|
};
|
|
220
234
|
}
|
|
221
235
|
|