@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
|
@@ -68,6 +68,45 @@ export class LocalThreadRuntimeCore
|
|
|
68
68
|
private _queue: MessageQueueController | null = null;
|
|
69
69
|
private _queueRunInFlight = false;
|
|
70
70
|
|
|
71
|
+
private _historyWrites = new Map<string, Promise<void>>();
|
|
72
|
+
|
|
73
|
+
// Writes for one message id must land in issue order; an earlier paused
|
|
74
|
+
// snapshot arriving after the terminal write would resurrect the pause.
|
|
75
|
+
private _chainHistoryWrite(
|
|
76
|
+
id: string,
|
|
77
|
+
write: () => Promise<void>,
|
|
78
|
+
): Promise<void> {
|
|
79
|
+
const next = (this._historyWrites.get(id) ?? Promise.resolve()).then(
|
|
80
|
+
write,
|
|
81
|
+
write,
|
|
82
|
+
);
|
|
83
|
+
const stored = next.then(
|
|
84
|
+
() => {},
|
|
85
|
+
() => {},
|
|
86
|
+
);
|
|
87
|
+
this._historyWrites.set(id, stored);
|
|
88
|
+
void stored.then(() => {
|
|
89
|
+
if (this._historyWrites.get(id) === stored) {
|
|
90
|
+
this._historyWrites.delete(id);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
return next;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// A decision recorded on a still-paused message must reach history before
|
|
97
|
+
// the run resumes, or a refresh would restore the message without it.
|
|
98
|
+
private _persistPausedMessage(
|
|
99
|
+
parentId: string | null,
|
|
100
|
+
message: ThreadAssistantMessage,
|
|
101
|
+
) {
|
|
102
|
+
if (message.status?.type !== "requires-action") return;
|
|
103
|
+
const history = this._options.adapters.history;
|
|
104
|
+
if (!history?.update) return;
|
|
105
|
+
const update = history.update.bind(history);
|
|
106
|
+
const item = { parentId, message, runConfig: this._lastRunConfig };
|
|
107
|
+
this._chainHistoryWrite(message.id, () => update(item)).catch(() => {});
|
|
108
|
+
}
|
|
109
|
+
|
|
71
110
|
public readonly isDisabled = false;
|
|
72
111
|
public readonly isSendDisabled = false;
|
|
73
112
|
|
|
@@ -351,6 +390,12 @@ export class LocalThreadRuntimeCore
|
|
|
351
390
|
): Promise<void> {
|
|
352
391
|
this._notifyEventSubscribers("runStart", {});
|
|
353
392
|
|
|
393
|
+
// A run entered on a requires-action message resumes a pause an
|
|
394
|
+
// update-capable adapter already holds (written at pause time or loaded).
|
|
395
|
+
const alreadyPersisted =
|
|
396
|
+
message.status?.type === "requires-action" &&
|
|
397
|
+
this._options.adapters.history?.update !== undefined;
|
|
398
|
+
|
|
354
399
|
try {
|
|
355
400
|
// mark busy for runs not started through the queue (regenerate, resume)
|
|
356
401
|
this._queue?.notifyBusy();
|
|
@@ -364,6 +409,7 @@ export class LocalThreadRuntimeCore
|
|
|
364
409
|
parentId,
|
|
365
410
|
message,
|
|
366
411
|
runConfig,
|
|
412
|
+
alreadyPersisted,
|
|
367
413
|
runCallback,
|
|
368
414
|
);
|
|
369
415
|
runCallback = undefined;
|
|
@@ -406,6 +452,7 @@ export class LocalThreadRuntimeCore
|
|
|
406
452
|
parentId: string | null,
|
|
407
453
|
message: ThreadAssistantMessage,
|
|
408
454
|
runConfig: RunConfig | undefined,
|
|
455
|
+
alreadyPersisted: boolean,
|
|
409
456
|
runCallback?: ChatModelAdapter["run"],
|
|
410
457
|
) {
|
|
411
458
|
const messages = parentId ? this.repository.getMessages(parentId) : [];
|
|
@@ -442,7 +489,7 @@ export class LocalThreadRuntimeCore
|
|
|
442
489
|
? {
|
|
443
490
|
metadata: {
|
|
444
491
|
...message.metadata,
|
|
445
|
-
...(m.metadata.unstable_state
|
|
492
|
+
...(m.metadata.unstable_state !== undefined
|
|
446
493
|
? { unstable_state: m.metadata.unstable_state }
|
|
447
494
|
: undefined),
|
|
448
495
|
...(annotations
|
|
@@ -471,17 +518,18 @@ export class LocalThreadRuntimeCore
|
|
|
471
518
|
|
|
472
519
|
const maxSteps = this._options.maxSteps ?? 2;
|
|
473
520
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
521
|
+
try {
|
|
522
|
+
const steps = message.metadata?.steps?.length ?? 0;
|
|
523
|
+
if (steps >= maxSteps) {
|
|
524
|
+
updateMessage({
|
|
525
|
+
status: {
|
|
526
|
+
type: "incomplete",
|
|
527
|
+
reason: "tool-calls",
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
return message;
|
|
531
|
+
}
|
|
532
|
+
|
|
485
533
|
updateMessage({
|
|
486
534
|
status: {
|
|
487
535
|
type: "running",
|
|
@@ -491,9 +539,7 @@ export class LocalThreadRuntimeCore
|
|
|
491
539
|
// Switch to the new message branch right after adding it for the first time
|
|
492
540
|
this.repository.resetHead(message.id);
|
|
493
541
|
this._notifySubscribers();
|
|
494
|
-
}
|
|
495
542
|
|
|
496
|
-
try {
|
|
497
543
|
this._lastRunConfig = runConfig ?? {};
|
|
498
544
|
// unstable_composerMetadata is composer-only (stamped onto the outgoing
|
|
499
545
|
// message); never expose it to the chat-model adapter's run context.
|
|
@@ -537,7 +583,9 @@ export class LocalThreadRuntimeCore
|
|
|
537
583
|
|
|
538
584
|
if (message.status.type === "running") {
|
|
539
585
|
updateMessage({
|
|
540
|
-
status:
|
|
586
|
+
status: abortSignal.aborted
|
|
587
|
+
? { type: "incomplete", reason: "cancelled" }
|
|
588
|
+
: { type: "complete", reason: "unknown" },
|
|
541
589
|
});
|
|
542
590
|
}
|
|
543
591
|
} catch (e) {
|
|
@@ -563,15 +611,29 @@ export class LocalThreadRuntimeCore
|
|
|
563
611
|
} finally {
|
|
564
612
|
this.abortController = null;
|
|
565
613
|
|
|
566
|
-
|
|
614
|
+
const history = this._options.adapters.history;
|
|
615
|
+
const item = {
|
|
616
|
+
parentId,
|
|
617
|
+
message,
|
|
618
|
+
runConfig: this._lastRunConfig,
|
|
619
|
+
};
|
|
620
|
+
const isTerminal =
|
|
567
621
|
message.status.type === "complete" ||
|
|
568
|
-
message.status.type === "incomplete"
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
622
|
+
message.status.type === "incomplete";
|
|
623
|
+
const isPausing =
|
|
624
|
+
message.status.type === "requires-action" &&
|
|
625
|
+
!shouldContinue(message, this._options.unstable_humanToolNames);
|
|
626
|
+
|
|
627
|
+
// Pauses are written only for adapters that can rewrite the entry later;
|
|
628
|
+
// an append-only adapter would strand a half-finished run in history.
|
|
629
|
+
if (isTerminal || (isPausing && history?.update)) {
|
|
630
|
+
const write =
|
|
631
|
+
alreadyPersisted && history?.update
|
|
632
|
+
? history.update.bind(history)
|
|
633
|
+
: history?.append.bind(history);
|
|
634
|
+
if (write) {
|
|
635
|
+
await this._chainHistoryWrite(message.id, () => write(item));
|
|
636
|
+
}
|
|
575
637
|
}
|
|
576
638
|
}
|
|
577
639
|
return message;
|
|
@@ -641,6 +703,8 @@ export class LocalThreadRuntimeCore
|
|
|
641
703
|
shouldContinue(message, this._options.unstable_humanToolNames)
|
|
642
704
|
) {
|
|
643
705
|
this._runLoop(parentId, message, this._lastRunConfig).catch(() => {});
|
|
706
|
+
} else if (added) {
|
|
707
|
+
this._persistPausedMessage(parentId, message);
|
|
644
708
|
}
|
|
645
709
|
}
|
|
646
710
|
|
|
@@ -719,6 +783,8 @@ export class LocalThreadRuntimeCore
|
|
|
719
783
|
shouldContinue(message, this._options.unstable_humanToolNames)
|
|
720
784
|
) {
|
|
721
785
|
this._runLoop(parentId, message, this._lastRunConfig).catch(() => {});
|
|
786
|
+
} else {
|
|
787
|
+
this._persistPausedMessage(parentId, message);
|
|
722
788
|
}
|
|
723
789
|
}
|
|
724
790
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { ThreadAssistantMessage } from "../../types/message";
|
|
3
|
+
import { shouldContinue } from "./should-continue";
|
|
4
|
+
|
|
5
|
+
const makeMessage = (
|
|
6
|
+
overrides: Partial<ThreadAssistantMessage>,
|
|
7
|
+
): ThreadAssistantMessage => ({
|
|
8
|
+
id: "msg-1",
|
|
9
|
+
role: "assistant",
|
|
10
|
+
createdAt: new Date(),
|
|
11
|
+
content: [],
|
|
12
|
+
status: { type: "complete", reason: "stop" },
|
|
13
|
+
metadata: {
|
|
14
|
+
unstable_state: null,
|
|
15
|
+
unstable_annotations: [],
|
|
16
|
+
unstable_data: [],
|
|
17
|
+
steps: [],
|
|
18
|
+
custom: {},
|
|
19
|
+
},
|
|
20
|
+
...overrides,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const toolCall = (toolName: string, result?: unknown) => ({
|
|
24
|
+
type: "tool-call" as const,
|
|
25
|
+
toolCallId: `call-${toolName}`,
|
|
26
|
+
toolName,
|
|
27
|
+
args: {},
|
|
28
|
+
argsText: "{}",
|
|
29
|
+
...(result !== undefined ? { result } : {}),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("shouldContinue", () => {
|
|
33
|
+
it("returns false when status is not requires-action", () => {
|
|
34
|
+
const msg = makeMessage({
|
|
35
|
+
status: { type: "complete", reason: "stop" },
|
|
36
|
+
});
|
|
37
|
+
expect(shouldContinue(msg, undefined)).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("returns false when reason is not tool-calls", () => {
|
|
41
|
+
const msg = makeMessage({
|
|
42
|
+
status: { type: "requires-action", reason: "interrupt" },
|
|
43
|
+
});
|
|
44
|
+
expect(shouldContinue(msg, undefined)).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns true when all tool calls have results (no humanToolNames)", () => {
|
|
48
|
+
const msg = makeMessage({
|
|
49
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
50
|
+
content: [toolCall("search", "found it"), toolCall("calculate", 42)],
|
|
51
|
+
});
|
|
52
|
+
expect(shouldContinue(msg, undefined)).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("returns false when some tool calls have no result (no humanToolNames)", () => {
|
|
56
|
+
const msg = makeMessage({
|
|
57
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
58
|
+
content: [toolCall("search", "found it"), toolCall("calculate")],
|
|
59
|
+
});
|
|
60
|
+
expect(shouldContinue(msg, undefined)).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("returns true when unresolved tool call is not a human tool", () => {
|
|
64
|
+
const msg = makeMessage({
|
|
65
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
66
|
+
content: [toolCall("auto-tool")],
|
|
67
|
+
});
|
|
68
|
+
expect(shouldContinue(msg, ["human-approval"])).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("returns false when unresolved tool call IS a human tool", () => {
|
|
72
|
+
const msg = makeMessage({
|
|
73
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
74
|
+
content: [toolCall("human-approval")],
|
|
75
|
+
});
|
|
76
|
+
expect(shouldContinue(msg, ["human-approval"])).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("returns true for non-tool-call content parts", () => {
|
|
80
|
+
const msg = makeMessage({
|
|
81
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
82
|
+
content: [
|
|
83
|
+
{ type: "text", text: "Hello" } as any,
|
|
84
|
+
toolCall("search", "done"),
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
expect(shouldContinue(msg, undefined)).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("returns false while a tool call has a pending approval", () => {
|
|
91
|
+
const msg = makeMessage({
|
|
92
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
93
|
+
content: [{ ...toolCall("deploy"), approval: { id: "a1" } }],
|
|
94
|
+
});
|
|
95
|
+
expect(shouldContinue(msg, undefined)).toBe(false);
|
|
96
|
+
expect(shouldContinue(msg, ["human-approval"])).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("returns true when a decided approval has no result", () => {
|
|
100
|
+
const msg = makeMessage({
|
|
101
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
102
|
+
content: [
|
|
103
|
+
{ ...toolCall("deploy"), approval: { id: "a1", approved: true } },
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
expect(shouldContinue(msg, undefined)).toBe(true);
|
|
107
|
+
expect(shouldContinue(msg, ["human-approval"])).toBe(true);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("exempts approval-gated tool calls from the human tool result requirement", () => {
|
|
111
|
+
const msg = makeMessage({
|
|
112
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
113
|
+
content: [
|
|
114
|
+
{
|
|
115
|
+
...toolCall("human-approval"),
|
|
116
|
+
approval: { id: "a1", approved: true },
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
});
|
|
120
|
+
expect(shouldContinue(msg, ["human-approval"])).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { OptimisticState } from "./optimistic-state";
|
|
3
|
+
|
|
4
|
+
const deferred = () => {
|
|
5
|
+
let resolve!: () => void;
|
|
6
|
+
const promise = new Promise<void>((resolvePromise) => {
|
|
7
|
+
resolve = resolvePromise;
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
return { promise, resolve };
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe("OptimisticState", () => {
|
|
14
|
+
it("preserves invocation order when optimistic updates resolve in order", async () => {
|
|
15
|
+
const state = new OptimisticState({ title: "Untitled" });
|
|
16
|
+
const firstRequest = deferred();
|
|
17
|
+
const secondRequest = deferred();
|
|
18
|
+
|
|
19
|
+
const firstUpdate = state.optimisticUpdate({
|
|
20
|
+
execute: () => firstRequest.promise,
|
|
21
|
+
optimistic: (value) => ({ ...value, title: "Project Alpha" }),
|
|
22
|
+
});
|
|
23
|
+
const secondUpdate = state.optimisticUpdate({
|
|
24
|
+
execute: () => secondRequest.promise,
|
|
25
|
+
optimistic: (value) => ({ ...value, title: "Project Beta" }),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(state.value.title).toBe("Project Beta");
|
|
29
|
+
|
|
30
|
+
firstRequest.resolve();
|
|
31
|
+
await firstUpdate;
|
|
32
|
+
expect(state.value.title).toBe("Project Beta");
|
|
33
|
+
|
|
34
|
+
secondRequest.resolve();
|
|
35
|
+
await secondUpdate;
|
|
36
|
+
|
|
37
|
+
expect(state.value.title).toBe("Project Beta");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("preserves invocation order when optimistic updates resolve out of order", async () => {
|
|
41
|
+
const state = new OptimisticState({ title: "Untitled" });
|
|
42
|
+
const firstRequest = deferred();
|
|
43
|
+
const secondRequest = deferred();
|
|
44
|
+
|
|
45
|
+
const firstUpdate = state.optimisticUpdate({
|
|
46
|
+
execute: () => firstRequest.promise,
|
|
47
|
+
optimistic: (value) => ({ ...value, title: "Project Alpha" }),
|
|
48
|
+
});
|
|
49
|
+
const secondUpdate = state.optimisticUpdate({
|
|
50
|
+
execute: () => secondRequest.promise,
|
|
51
|
+
optimistic: (value) => ({ ...value, title: "Project Beta" }),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
secondRequest.resolve();
|
|
55
|
+
await secondUpdate;
|
|
56
|
+
expect(state.value.title).toBe("Project Beta");
|
|
57
|
+
|
|
58
|
+
firstRequest.resolve();
|
|
59
|
+
await firstUpdate;
|
|
60
|
+
|
|
61
|
+
expect(state.value.title).toBe("Project Beta");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -14,9 +14,15 @@ type Transform<TState, TResult> = {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
type PendingTransform<TState, TResult> = Transform<TState, TResult> & {
|
|
17
|
+
order: number;
|
|
17
18
|
task: Promise<TResult>;
|
|
18
19
|
};
|
|
19
20
|
|
|
21
|
+
type CompletedOptimistic<TState> = {
|
|
22
|
+
order: number;
|
|
23
|
+
optimistic: (state: TState) => TState;
|
|
24
|
+
};
|
|
25
|
+
|
|
20
26
|
const pipeTransforms = <TState, TExtra>(
|
|
21
27
|
initialState: TState,
|
|
22
28
|
extraParam: TExtra,
|
|
@@ -32,14 +38,16 @@ export class OptimisticState<TState> extends BaseSubscribable {
|
|
|
32
38
|
[];
|
|
33
39
|
|
|
34
40
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* (e.g. delete). Cleared when no pending transforms remain.
|
|
41
|
+
* Completed optimistic callbacks remain active while any transform is
|
|
42
|
+
* pending, so later state replacements cannot erase them. Invocation order
|
|
43
|
+
* determines which overlapping optimistic update wins.
|
|
39
44
|
*
|
|
40
45
|
* Correctness requirement: `optimistic` callbacks must be idempotent.
|
|
41
46
|
*/
|
|
42
|
-
private readonly _completedOptimistics: Array<
|
|
47
|
+
private readonly _completedOptimistics: Array<CompletedOptimistic<TState>> =
|
|
48
|
+
[];
|
|
49
|
+
|
|
50
|
+
private _nextTransformOrder = 0;
|
|
43
51
|
|
|
44
52
|
private _baseValue: TState;
|
|
45
53
|
private _cachedValue: TState;
|
|
@@ -51,12 +59,25 @@ export class OptimisticState<TState> extends BaseSubscribable {
|
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
private _updateState(): void {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
transform.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
const activeTransforms = [
|
|
63
|
+
...this._pendingTransforms.map((transform) => ({
|
|
64
|
+
order: transform.order,
|
|
65
|
+
apply: (state: TState) =>
|
|
66
|
+
pipeTransforms(state, transform.task, [
|
|
67
|
+
transform.loading,
|
|
68
|
+
transform.optimistic,
|
|
69
|
+
]),
|
|
70
|
+
})),
|
|
71
|
+
...this._completedOptimistics.map(({ order, optimistic }) => ({
|
|
72
|
+
order,
|
|
73
|
+
apply: optimistic,
|
|
74
|
+
})),
|
|
75
|
+
].sort((a, b) => a.order - b.order);
|
|
76
|
+
|
|
77
|
+
this._cachedValue = activeTransforms.reduce(
|
|
78
|
+
(state, transform) => transform.apply(state),
|
|
79
|
+
this._baseValue,
|
|
80
|
+
);
|
|
60
81
|
|
|
61
82
|
this._notifySubscribers();
|
|
62
83
|
}
|
|
@@ -77,8 +98,13 @@ export class OptimisticState<TState> extends BaseSubscribable {
|
|
|
77
98
|
public async optimisticUpdate<TResult>(
|
|
78
99
|
transform: Transform<TState, TResult>,
|
|
79
100
|
): Promise<TResult> {
|
|
101
|
+
const order = this._nextTransformOrder++;
|
|
80
102
|
const task = transform.execute();
|
|
81
|
-
const pendingTransform = {
|
|
103
|
+
const pendingTransform = {
|
|
104
|
+
...transform,
|
|
105
|
+
order,
|
|
106
|
+
task,
|
|
107
|
+
};
|
|
82
108
|
try {
|
|
83
109
|
this._pendingTransforms.push(pendingTransform);
|
|
84
110
|
this._updateState();
|
|
@@ -89,14 +115,20 @@ export class OptimisticState<TState> extends BaseSubscribable {
|
|
|
89
115
|
transform.then,
|
|
90
116
|
]);
|
|
91
117
|
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
for (const
|
|
95
|
-
|
|
118
|
+
// `then` can replace state with a stale snapshot, so replay every
|
|
119
|
+
// completed effect; otherwise replay only newer overlapping effects.
|
|
120
|
+
for (const completed of this._completedOptimistics) {
|
|
121
|
+
if (transform.then || completed.order > pendingTransform.order) {
|
|
122
|
+
this._baseValue = completed.optimistic(this._baseValue);
|
|
123
|
+
}
|
|
96
124
|
}
|
|
97
125
|
|
|
98
126
|
if (transform.optimistic) {
|
|
99
|
-
this._completedOptimistics.push(
|
|
127
|
+
this._completedOptimistics.push({
|
|
128
|
+
order: pendingTransform.order,
|
|
129
|
+
optimistic: transform.optimistic,
|
|
130
|
+
});
|
|
131
|
+
this._completedOptimistics.sort((a, b) => a.order - b.order);
|
|
100
132
|
}
|
|
101
133
|
|
|
102
134
|
return result;
|
|
@@ -41,6 +41,15 @@ Subsequent snapshots that *are* prefixes of the new (regressed) snapshot
|
|
|
41
41
|
also won't be appended, because `entry.argsText` still points at the
|
|
42
42
|
pre-regression value used for delta calculation.
|
|
43
43
|
|
|
44
|
+
The args stream closes only when the controller's *streamed* content
|
|
45
|
+
(`entry.argsText`) is complete, not when a later snapshot is. A divergent
|
|
46
|
+
snapshot can be complete while the controller still holds an incomplete
|
|
47
|
+
stale prefix; closing on the snapshot would parse that stale prefix and
|
|
48
|
+
auto-submit a bogus parse-error result (resuming the host graph and
|
|
49
|
+
abandoning a pending interrupt). Gating the close on the streamed content
|
|
50
|
+
leaves the stream open until the prefix itself completes, so no stale
|
|
51
|
+
parse runs and no error result is fabricated from divergent args.
|
|
52
|
+
|
|
44
53
|
### A.3. Args complete then equivalent-JSON key reorder
|
|
45
54
|
Both old and new `argsText` parse to equivalent JSON values (e.g. keys
|
|
46
55
|
reordered by the backend). The tracker updates its tracked `argsText`
|
|
@@ -53,9 +62,10 @@ existing `streamCall` keeps its original args view.
|
|
|
53
62
|
|
|
54
63
|
### A.5. First resolution (`result` becomes defined)
|
|
55
64
|
The tracker calls `setResponse` on the active controller and closes it.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
The backend result is emitted before the args stream closes, so a stale
|
|
66
|
+
args parse failure cannot replace it. `reader.response.get()` resolves.
|
|
67
|
+
If the tool also had a frontend `execute`, the executor is short-circuited
|
|
68
|
+
via `_skipExecuteStreamIds`. Single fire.
|
|
59
69
|
|
|
60
70
|
### A.6. Previously-resolved tool's `result` is replaced
|
|
61
71
|
Silently ignored — `entry.hasResult` short-circuits both the
|
|
@@ -167,22 +177,6 @@ keep the tracker dead with a visible error to avoid restart loops.
|
|
|
167
177
|
|
|
168
178
|
## Known limitations
|
|
169
179
|
|
|
170
|
-
### Result delivery after args regression (A.2 + A.5 in the same snapshot)
|
|
171
|
-
When a snapshot has both a regressed `argsText` *and* a backend result
|
|
172
|
-
on the same tool call, `activeController.setResponse(result)` closes
|
|
173
|
-
`argsText` before enqueueing the result chunk. The args-text-finish
|
|
174
|
-
chunk reaches `ToolExecutionStream` first, attempts to parse the
|
|
175
|
-
(stale) accumulated argsText, fails, and emits a parse-error result
|
|
176
|
-
that beats the backend result to the reader's response promise.
|
|
177
|
-
|
|
178
|
-
The tracker's `entry.hasResult` short-circuit *does* suppress both
|
|
179
|
-
result chunks at the `onResult` callback level (no double-fire), but
|
|
180
|
-
the reader's `response.get()` already resolved with the parse error.
|
|
181
|
-
|
|
182
|
-
Fixable upstream in `ToolCallStreamControllerImpl.setResponse` by
|
|
183
|
-
enqueueing the result chunk before closing argsText. Tracked separately;
|
|
184
|
-
out of scope for the tracker layer.
|
|
185
|
-
|
|
186
180
|
### Host callback throws
|
|
187
181
|
`onResult` and `onStatusesChange` are invoked through wrappers that
|
|
188
182
|
catch and log. The tracker continues to function; the host's bad
|