@assistant-ui/core 0.3.13 → 0.3.14
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/speech.d.ts.map +1 -1
- package/dist/adapters/speech.js +9 -15
- package/dist/adapters/speech.js.map +1 -1
- package/dist/adapters/thread-history.d.ts +2 -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 +27 -15
- package/dist/adapters/voice.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +6 -6
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +2 -1
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/registry.d.ts.map +1 -1
- package/dist/model-context/registry.js +16 -4
- package/dist/model-context/registry.js.map +1 -1
- package/dist/react/RuntimeAdapter.d.ts +8 -1
- package/dist/react/RuntimeAdapter.d.ts.map +1 -1
- package/dist/react/RuntimeAdapter.js +23 -18
- package/dist/react/RuntimeAdapter.js.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.js +28 -12
- package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
- package/dist/react/client/AdaptedRemoteThread.d.ts +11 -0
- package/dist/react/client/AdaptedRemoteThread.d.ts.map +1 -0
- package/dist/react/client/AdaptedRemoteThread.js +27 -0
- package/dist/react/client/AdaptedRemoteThread.js.map +1 -0
- package/dist/react/client/InMemoryThreadList.d.ts +19 -0
- package/dist/react/client/InMemoryThreadList.d.ts.map +1 -0
- package/dist/react/client/InMemoryThreadList.js +418 -0
- package/dist/react/client/InMemoryThreadList.js.map +1 -0
- package/dist/react/client/Interactables.d.ts.map +1 -1
- package/dist/react/client/Interactables.js +159 -102
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/RemoteThreadList.d.ts +34 -0
- package/dist/react/client/RemoteThreadList.d.ts.map +1 -0
- package/dist/react/client/RemoteThreadList.js +826 -0
- package/dist/react/client/RemoteThreadList.js.map +1 -0
- package/dist/react/client/Tools.d.ts.map +1 -1
- package/dist/react/client/Tools.js +38 -27
- package/dist/react/client/Tools.js.map +1 -1
- package/dist/react/index.d.ts +7 -2
- package/dist/react/index.js +5 -2
- package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
- package/dist/react/interactables-legacy/Interactables.js +145 -110
- package/dist/react/interactables-legacy/Interactables.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useActionBarCopy.js +13 -18
- package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarEdit.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useActionBarEdit.js +2 -4
- package/dist/react/primitive-hooks/useActionBarEdit.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarReload.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useActionBarReload.js +2 -4
- package/dist/react/primitive-hooks/useActionBarReload.js.map +1 -1
- package/dist/react/primitive-hooks/useBranchPickerNext.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useBranchPickerNext.js +2 -6
- package/dist/react/primitive-hooks/useBranchPickerNext.js.map +1 -1
- package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useBranchPickerPrevious.js +2 -6
- package/dist/react/primitive-hooks/useBranchPickerPrevious.js.map +1 -1
- package/dist/react/primitive-hooks/useComposerCancel.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useComposerCancel.js +2 -4
- package/dist/react/primitive-hooks/useComposerCancel.js.map +1 -1
- package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useComposerSend.js +2 -4
- package/dist/react/primitive-hooks/useComposerSend.js.map +1 -1
- package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useSuggestionTrigger.js +6 -2
- package/dist/react/primitive-hooks/useSuggestionTrigger.js.map +1 -1
- package/dist/react/primitives/message/MessageParts.d.ts +13 -0
- package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageParts.js.map +1 -1
- package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
- package/dist/react/primitives/thread/ThreadMessages.js +1 -1
- package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +22 -10
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +109 -71
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +8 -2
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +40 -13
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.d.ts +30 -0
- package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -0
- package/dist/react/runtimes/RemoteThreadResource.js +134 -0
- package/dist/react/runtimes/RemoteThreadResource.js.map +1 -0
- package/dist/react/runtimes/RuntimeAdapterProvider.d.ts +3 -10
- package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
- package/dist/react/runtimes/RuntimeAdapterProvider.js +3 -6
- package/dist/react/runtimes/RuntimeAdapterProvider.js.map +1 -1
- package/dist/react/runtimes/assistant-transport/commandQueue.d.ts +17 -0
- package/dist/react/runtimes/assistant-transport/commandQueue.d.ts.map +1 -0
- package/dist/react/runtimes/assistant-transport/commandQueue.js +55 -0
- package/dist/react/runtimes/assistant-transport/commandQueue.js.map +1 -0
- package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts +11 -0
- package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts.map +1 -0
- package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js +165 -0
- package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js.map +1 -0
- package/dist/react/runtimes/assistant-transport/runManager.d.ts +15 -0
- package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -0
- package/dist/react/runtimes/assistant-transport/runManager.js +86 -0
- package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -0
- package/dist/react/runtimes/assistant-transport/types.d.ts +130 -0
- package/dist/react/runtimes/assistant-transport/types.d.ts.map +1 -0
- package/dist/react/runtimes/assistant-transport/types.js +0 -0
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts +14 -0
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -0
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +296 -0
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -0
- package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts +7 -0
- package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts.map +1 -0
- package/dist/react/runtimes/assistant-transport/useConvertedState.js +25 -0
- package/dist/react/runtimes/assistant-transport/useConvertedState.js.map +1 -0
- package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts +7 -0
- package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts.map +1 -0
- package/dist/react/runtimes/assistant-transport/useLatestRef.js +27 -0
- package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -0
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +39 -13
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +2 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +3 -3
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts +4 -0
- package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.js +7 -0
- package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +22 -12
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts +16 -0
- package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts.map +1 -0
- package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js +36 -0
- package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -0
- package/dist/react/runtimes/external-message-converter.js +14 -14
- package/dist/react/runtimes/external-message-converter.js.map +1 -1
- package/dist/react/runtimes/useExternalStoreRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/useExternalStoreRuntime.js +44 -28
- package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
- package/dist/react/runtimes/useExternalStoreSharedOptions.js.map +1 -1
- package/dist/react/runtimes/useLocalRuntime.js +1 -1
- package/dist/react/runtimes/useRuntimeAdapters.d.ts +9 -0
- package/dist/react/runtimes/useRuntimeAdapters.d.ts.map +1 -0
- package/dist/react/runtimes/useRuntimeAdapters.js +20 -0
- package/dist/react/runtimes/useRuntimeAdapters.js.map +1 -0
- package/dist/runtime/api/composer-runtime.d.ts +5 -0
- package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
- package/dist/runtime/api/composer-runtime.js.map +1 -1
- package/dist/runtime/api/message-runtime.js +1 -1
- package/dist/runtime/api/thread-list-runtime.d.ts +1 -0
- package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-runtime.js +4 -2
- package/dist/runtime/api/thread-list-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +12 -2
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-runtime.js +8 -2
- package/dist/runtime/api/thread-runtime.js.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +8 -7
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +1 -0
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +3 -3
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +0 -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 +10 -4
- package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
- package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/internal.js +5 -5
- package/dist/runtime/queue/message-queue.d.ts.map +1 -1
- package/dist/runtime/queue/message-queue.js +2 -1
- package/dist/runtime/queue/message-queue.js.map +1 -1
- package/dist/runtime/utils/thread-runtime-lifecycle.d.ts +8 -0
- package/dist/runtime/utils/thread-runtime-lifecycle.d.ts.map +1 -0
- package/dist/runtime/utils/thread-runtime-lifecycle.js +11 -0
- package/dist/runtime/utils/thread-runtime-lifecycle.js.map +1 -0
- package/dist/runtimes/external-store/external-store-shared-options.js.map +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 +4 -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 +8 -0
- 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 +35 -13
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/internal.d.ts +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 +53 -13
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +1 -0
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +3 -0
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
- package/dist/runtimes/remote-thread-list/empty-thread-core.js +3 -0
- package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +29 -1
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +1 -12
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +67 -38
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
- package/dist/store/clients/suggestions.js +2 -2
- package/dist/store/clients/suggestions.js.map +1 -1
- package/dist/store/clients/thread-selection-events.d.ts +10 -0
- package/dist/store/clients/thread-selection-events.d.ts.map +1 -0
- package/dist/store/clients/thread-selection-events.js +40 -0
- package/dist/store/clients/thread-selection-events.js.map +1 -0
- package/dist/store/env.d.ts +5 -0
- package/dist/store/env.d.ts.map +1 -0
- package/dist/store/env.js +12 -0
- package/dist/store/env.js.map +1 -0
- package/dist/store/index.d.ts +5 -3
- package/dist/store/index.js +4 -2
- package/dist/store/internal.d.ts +4 -1
- package/dist/store/internal.js +4 -1
- package/dist/store/primitive-predicates.d.ts +19 -0
- package/dist/store/primitive-predicates.d.ts.map +1 -0
- package/dist/store/primitive-predicates.js +19 -0
- package/dist/store/primitive-predicates.js.map +1 -0
- package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/attachment-runtime-client.js +18 -11
- package/dist/store/runtime-clients/attachment-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/handle-runtime-action.d.ts +5 -0
- package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -0
- package/dist/store/runtime-clients/handle-runtime-action.js +12 -0
- package/dist/store/runtime-clients/handle-runtime-action.js.map +1 -0
- package/dist/store/runtime-clients/handle-thread-list-action.d.ts +5 -0
- package/dist/store/runtime-clients/handle-thread-list-action.d.ts.map +1 -0
- package/dist/store/runtime-clients/handle-thread-list-action.js +7 -0
- package/dist/store/runtime-clients/handle-thread-list-action.js.map +1 -0
- package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js +62 -29
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js.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 +5 -6
- package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
- package/dist/store/scopes/composer.d.ts +9 -4
- package/dist/store/scopes/composer.d.ts.map +1 -1
- package/dist/store/scopes/thread-list-item.d.ts +7 -6
- package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
- package/dist/store/scopes/thread.d.ts +8 -7
- package/dist/store/scopes/thread.d.ts.map +1 -1
- package/dist/store/scopes/threads.d.ts +17 -1
- package/dist/store/scopes/threads.d.ts.map +1 -1
- package/dist/subscribable/subscribable.d.ts +2 -1
- package/dist/subscribable/subscribable.d.ts.map +1 -1
- package/dist/subscribable/subscribable.js +23 -18
- package/dist/subscribable/subscribable.js.map +1 -1
- package/dist/utils/composite-context-provider.d.ts +1 -0
- package/dist/utils/composite-context-provider.d.ts.map +1 -1
- package/dist/utils/composite-context-provider.js +25 -9
- package/dist/utils/composite-context-provider.js.map +1 -1
- package/dist/utils/notify-event-listeners.d.ts +2 -2
- package/dist/utils/notify-event-listeners.d.ts.map +1 -1
- package/dist/utils/notify-event-listeners.js +2 -2
- package/dist/utils/notify-event-listeners.js.map +1 -1
- package/package.json +7 -7
- package/src/adapters/speech.test.ts +118 -1
- package/src/adapters/speech.ts +21 -22
- package/src/adapters/thread-history.ts +2 -0
- package/src/adapters/voice.test.ts +69 -0
- package/src/adapters/voice.ts +27 -27
- package/src/index.ts +1 -0
- package/src/model-context/frame/host.test.ts +23 -0
- package/src/model-context/frame/host.ts +2 -1
- package/src/model-context/registry.test.ts +131 -0
- package/src/model-context/registry.ts +16 -6
- package/src/react/RuntimeAdapter.test.ts +2 -0
- package/src/react/RuntimeAdapter.ts +20 -8
- package/src/react/adapters/LocalStorageThreadListAdapter.tsx +34 -17
- package/src/react/client/AdaptedRemoteThread.ts +41 -0
- package/src/react/client/InMemoryThreadList.test.tsx +148 -0
- package/src/react/client/InMemoryThreadList.ts +290 -0
- package/src/react/client/Interactables.test.ts +326 -2
- package/src/react/client/Interactables.ts +227 -110
- package/src/react/client/RemoteThreadList.test.ts +1026 -0
- package/src/react/client/RemoteThreadList.ts +1067 -0
- package/src/react/client/Tools.ts +36 -29
- package/src/react/index.ts +13 -0
- package/src/react/interactables-legacy/Interactables.test.ts +317 -0
- package/src/react/interactables-legacy/Interactables.ts +204 -128
- package/src/react/primitive-hooks/useActionBarCopy.ts +2 -7
- package/src/react/primitive-hooks/useActionBarEdit.ts +2 -1
- package/src/react/primitive-hooks/useActionBarReload.ts +2 -6
- package/src/react/primitive-hooks/useBranchPickerNext.ts +2 -7
- package/src/react/primitive-hooks/useBranchPickerPrevious.ts +2 -7
- package/src/react/primitive-hooks/useComposerCancel.ts +2 -1
- package/src/react/primitive-hooks/useComposerSend.ts +2 -5
- package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +11 -0
- package/src/react/primitive-hooks/useSuggestionTrigger.ts +4 -5
- package/src/react/primitives/message/MessageParts.tsx +0 -1
- package/src/react/primitives/thread/ThreadMessages.tsx +2 -1
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.retry.test.tsx +116 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +1 -1
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +57 -6
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.title.test.tsx +272 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +159 -124
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +59 -20
- package/src/react/runtimes/RemoteThreadResource.test.ts +141 -0
- package/src/react/runtimes/RemoteThreadResource.ts +231 -0
- package/src/react/runtimes/RuntimeAdapterProvider.tsx +8 -20
- package/src/react/runtimes/assistant-transport/commandQueue.ts +68 -0
- package/src/react/runtimes/assistant-transport/replayBoundaryStream.test.ts +455 -0
- package/src/react/runtimes/assistant-transport/replayBoundaryStream.ts +172 -0
- package/src/react/runtimes/assistant-transport/runManager.ts +124 -0
- package/src/react/runtimes/assistant-transport/transport-delivery.test.tsx +79 -0
- package/src/react/runtimes/assistant-transport/transport-parentid.test.tsx +97 -0
- package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +414 -0
- package/src/react/runtimes/assistant-transport/types.ts +170 -0
- package/src/react/runtimes/assistant-transport/useAssistantTransport.spec.md +141 -0
- package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +625 -0
- package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +481 -0
- package/src/react/runtimes/assistant-transport/useConvertedState.ts +20 -0
- package/src/react/runtimes/assistant-transport/useLatestRef.ts +9 -0
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +131 -6
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +61 -15
- package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +6 -4
- package/src/react/runtimes/cloud/auiV0.ts +17 -1
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +36 -21
- package/src/react/runtimes/cloud/useCloudThreadListRuntime.test.tsx +56 -0
- package/src/react/runtimes/cloud/useCloudThreadListRuntime.ts +31 -0
- package/src/react/runtimes/external-message-converter.test.ts +143 -0
- package/src/react/runtimes/external-message-converter.ts +41 -41
- package/src/react/runtimes/remote-thread-hook-on-tap.test.ts +58 -0
- package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +83 -0
- package/src/react/runtimes/useExternalStoreRuntime.suggestions.test.tsx +13 -3
- package/src/react/runtimes/useExternalStoreRuntime.ts +7 -0
- package/src/react/runtimes/useExternalStoreSharedOptions.ts +3 -1
- package/src/react/runtimes/useLocalRuntime.test.tsx +130 -4
- package/src/react/runtimes/useRuntimeAdapters.test.tsx +62 -0
- package/src/react/runtimes/useRuntimeAdapters.ts +28 -0
- package/src/runtime/api/composer-runtime.ts +5 -0
- package/src/runtime/api/thread-list-runtime.ts +3 -1
- package/src/runtime/api/thread-runtime.ts +20 -2
- package/src/runtime/base/base-composer-runtime-core.ts +6 -5
- package/src/runtime/base/base-thread-runtime-core.test.ts +191 -0
- package/src/runtime/base/base-thread-runtime-core.ts +11 -2
- package/src/runtime/base/default-thread-composer-runtime-core.ts +12 -4
- package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
- package/src/runtime/queue/message-queue.ts +2 -1
- package/src/runtime/utils/thread-runtime-lifecycle.ts +18 -0
- package/src/runtimes/external-store/external-store-shared-options.ts +3 -1
- 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 +99 -34
- package/src/runtimes/local/local-thread-runtime-core.test.ts +373 -4
- package/src/runtimes/local/local-thread-runtime-core.ts +79 -14
- package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +4 -0
- package/src/runtimes/remote-thread-list/empty-thread-core.ts +4 -0
- package/src/runtimes/remote-thread-list/types.ts +30 -0
- package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -6
- package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +243 -0
- package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +127 -61
- package/src/store/clients/model-context-client.test.ts +16 -8
- package/src/store/clients/suggestions.ts +2 -2
- package/src/store/clients/thread-selection-events.ts +21 -0
- package/src/store/env.ts +11 -0
- package/src/store/index.ts +14 -1
- package/src/store/internal.ts +13 -0
- package/src/store/primitive-predicates.test.ts +184 -0
- package/src/store/primitive-predicates.ts +43 -0
- package/src/store/runtime-clients/attachment-runtime-client.test.ts +41 -0
- package/src/store/runtime-clients/attachment-runtime-client.ts +2 -1
- package/src/store/runtime-clients/handle-runtime-action.ts +11 -0
- package/src/store/runtime-clients/handle-thread-list-action.test.ts +69 -0
- package/src/store/runtime-clients/handle-thread-list-action.ts +6 -0
- package/src/store/runtime-clients/thread-list-item-runtime-client.ts +15 -7
- package/src/store/runtime-clients/thread-list-runtime-client.ts +9 -6
- package/src/store/scopes/composer.ts +9 -4
- package/src/store/scopes/thread-list-item.ts +7 -6
- package/src/store/scopes/thread.ts +8 -7
- package/src/store/scopes/threads.ts +14 -0
- package/src/subscribable/subscribable.test.ts +77 -1
- package/src/subscribable/subscribable.ts +38 -23
- package/src/tests/RemoteThreadListThreadListRuntimeCore-switchToThread-unarchive.test.ts +25 -0
- package/src/tests/auiV0Encode.test.ts +66 -0
- package/src/tests/base-composer-runtime-core-send.test.ts +30 -0
- package/src/tests/base-composer-runtime-core.test.ts +28 -0
- package/src/tests/composer-can-cancel.test.ts +101 -0
- package/src/tests/external-store-thread-list-runtime-core.test.ts +101 -0
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +191 -1
- package/src/tests/external-store-thread-runtime-core.test.ts +124 -0
- package/src/tests/interactables-toolui-migration.test.tsx +66 -0
- package/src/tests/message-queue.test.ts +28 -0
- package/src/tests/session-reset-notification.test.ts +145 -0
- package/src/tests/thread-switch-events.test.tsx +99 -1
- package/src/tests/tools-scope-migration.test.tsx +58 -0
- package/src/utils/composite-context-provider.test.ts +139 -0
- package/src/utils/composite-context-provider.ts +26 -8
- package/src/utils/notify-event-listeners.ts +10 -5
|
@@ -8,6 +8,8 @@ import type {
|
|
|
8
8
|
import type { AppendMessage } from "../../types/message";
|
|
9
9
|
import type { LocalRuntimeOptionsBase } from "./local-runtime-options";
|
|
10
10
|
import type { ExportedMessageRepositoryItem } from "../../runtime/utils/message-repository";
|
|
11
|
+
import type { ThreadSuggestion } from "../../runtime/interfaces/thread-runtime-core";
|
|
12
|
+
import { isMessageNotSentError } from "../../types/error";
|
|
11
13
|
|
|
12
14
|
const flush = () => new Promise((resolve) => setTimeout(resolve, 10));
|
|
13
15
|
|
|
@@ -143,6 +145,180 @@ describe("LocalThreadRuntimeCore events", () => {
|
|
|
143
145
|
});
|
|
144
146
|
});
|
|
145
147
|
|
|
148
|
+
describe("LocalThreadRuntimeCore history persistence", () => {
|
|
149
|
+
it("surfaces failed user persistence without abandoning the run", async () => {
|
|
150
|
+
const persistenceError = new Error("history unavailable");
|
|
151
|
+
const run = vi.fn(async () => ({
|
|
152
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
153
|
+
}));
|
|
154
|
+
const append = vi.fn(async (item: ExportedMessageRepositoryItem) => {
|
|
155
|
+
if (item.message.role === "user") throw persistenceError;
|
|
156
|
+
});
|
|
157
|
+
const thread = createThread(
|
|
158
|
+
{ run },
|
|
159
|
+
{
|
|
160
|
+
history: {
|
|
161
|
+
async load() {
|
|
162
|
+
return { messages: [] };
|
|
163
|
+
},
|
|
164
|
+
append,
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
await expect(thread.append(userMessage("hello"))).rejects.toBe(
|
|
170
|
+
persistenceError,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
expect(append).toHaveBeenCalledTimes(2);
|
|
174
|
+
expect(run).toHaveBeenCalledOnce();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("surfaces failed persistence for messages that do not start a run", async () => {
|
|
178
|
+
const persistenceError = new Error("history unavailable");
|
|
179
|
+
const run = vi.fn();
|
|
180
|
+
const thread = createThread(
|
|
181
|
+
{ run },
|
|
182
|
+
{
|
|
183
|
+
history: {
|
|
184
|
+
async load() {
|
|
185
|
+
return { messages: [] };
|
|
186
|
+
},
|
|
187
|
+
async append() {
|
|
188
|
+
throw persistenceError;
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
await expect(
|
|
195
|
+
thread.append({ ...userMessage("hello"), startRun: false }),
|
|
196
|
+
).rejects.toBe(persistenceError);
|
|
197
|
+
|
|
198
|
+
expect(run).not.toHaveBeenCalled();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("handles failed persistence before notifying no-run subscribers", async () => {
|
|
202
|
+
const persistenceError = new Error("history unavailable");
|
|
203
|
+
const listenerError = new Error("listener unavailable");
|
|
204
|
+
const thread = createThread(
|
|
205
|
+
{ run: vi.fn() },
|
|
206
|
+
{
|
|
207
|
+
history: {
|
|
208
|
+
async load() {
|
|
209
|
+
return { messages: [] };
|
|
210
|
+
},
|
|
211
|
+
async append() {
|
|
212
|
+
throw persistenceError;
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
thread.subscribe(() => {
|
|
218
|
+
throw listenerError;
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
await expect(
|
|
222
|
+
thread.append({ ...userMessage("hello"), startRun: false }),
|
|
223
|
+
).rejects.toBe(listenerError);
|
|
224
|
+
await flush();
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe("LocalThreadRuntimeCore - detach", () => {
|
|
229
|
+
it("drops a pending append when detached", async () => {
|
|
230
|
+
let resolveInitialization!: () => void;
|
|
231
|
+
const initialization = new Promise<void>((resolve) => {
|
|
232
|
+
resolveInitialization = resolve;
|
|
233
|
+
});
|
|
234
|
+
const run = vi.fn(async () => ({
|
|
235
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
236
|
+
}));
|
|
237
|
+
const thread = createThread({ run });
|
|
238
|
+
thread.__internal_setGetInitializePromise(() => initialization);
|
|
239
|
+
|
|
240
|
+
const appendPromise = thread.append(userMessage("hello"));
|
|
241
|
+
await Promise.resolve();
|
|
242
|
+
thread.detach();
|
|
243
|
+
resolveInitialization();
|
|
244
|
+
|
|
245
|
+
await appendPromise;
|
|
246
|
+
expect(run).not.toHaveBeenCalled();
|
|
247
|
+
expect(thread.messages).toEqual([]);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe("LocalThreadRuntimeCore optimistic append", () => {
|
|
252
|
+
it("paints the appended message before initialization resolves", async () => {
|
|
253
|
+
let resolveInitialization!: () => void;
|
|
254
|
+
const initialization = new Promise<void>((resolve) => {
|
|
255
|
+
resolveInitialization = resolve;
|
|
256
|
+
});
|
|
257
|
+
const run = vi.fn(async () => ({
|
|
258
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
259
|
+
}));
|
|
260
|
+
const thread = createThread({ run });
|
|
261
|
+
thread.__internal_setGetInitializePromise(() => initialization);
|
|
262
|
+
const onUpdate = vi.fn();
|
|
263
|
+
thread.subscribe(onUpdate);
|
|
264
|
+
|
|
265
|
+
const appendPromise = thread.append(userMessage("hello"));
|
|
266
|
+
await Promise.resolve();
|
|
267
|
+
|
|
268
|
+
expect(thread.messages).toHaveLength(1);
|
|
269
|
+
expect(thread.messages[0]?.role).toBe("user");
|
|
270
|
+
expect(onUpdate).toHaveBeenCalled();
|
|
271
|
+
expect(run).not.toHaveBeenCalled();
|
|
272
|
+
|
|
273
|
+
resolveInitialization();
|
|
274
|
+
await appendPromise;
|
|
275
|
+
expect(run).toHaveBeenCalledTimes(1);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("silently drops a detached append even when initialization rejects", async () => {
|
|
279
|
+
let rejectInitialization!: (error: unknown) => void;
|
|
280
|
+
const initialization = new Promise<void>((_, reject) => {
|
|
281
|
+
rejectInitialization = reject;
|
|
282
|
+
});
|
|
283
|
+
const run = vi.fn(async () => ({
|
|
284
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
285
|
+
}));
|
|
286
|
+
const thread = createThread({ run });
|
|
287
|
+
thread.__internal_setGetInitializePromise(() => initialization);
|
|
288
|
+
|
|
289
|
+
const appendPromise = thread.append(userMessage("hello"));
|
|
290
|
+
await Promise.resolve();
|
|
291
|
+
thread.detach();
|
|
292
|
+
rejectInitialization(new Error("initialization failed"));
|
|
293
|
+
|
|
294
|
+
await expect(appendPromise).resolves.toBeUndefined();
|
|
295
|
+
expect(thread.messages).toEqual([]);
|
|
296
|
+
expect(run).not.toHaveBeenCalled();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("rolls the optimistic message back when initialization rejects", async () => {
|
|
300
|
+
const initializationError = new Error("initialization failed");
|
|
301
|
+
const run = vi.fn(async () => ({
|
|
302
|
+
content: [{ type: "text" as const, text: "done" }],
|
|
303
|
+
}));
|
|
304
|
+
const thread = createThread({ run });
|
|
305
|
+
thread.__internal_setGetInitializePromise(() =>
|
|
306
|
+
Promise.reject(initializationError),
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const error = await thread.append(userMessage("hello")).then(
|
|
310
|
+
() => {
|
|
311
|
+
throw new Error("expected the append to reject");
|
|
312
|
+
},
|
|
313
|
+
(e: unknown) => e,
|
|
314
|
+
);
|
|
315
|
+
expect(isMessageNotSentError(error)).toBe(true);
|
|
316
|
+
expect((error as Error).cause).toBe(initializationError);
|
|
317
|
+
expect(thread.messages).toEqual([]);
|
|
318
|
+
expect(run).not.toHaveBeenCalled();
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
146
322
|
describe("LocalThreadRuntimeCore human-in-the-loop tools", () => {
|
|
147
323
|
it("pauses on requires-action while a listed tool call has no result", async () => {
|
|
148
324
|
const { thread, runs } = createApprovalThread(toolCallResult("send_email"));
|
|
@@ -603,6 +779,48 @@ describe("LocalThreadRuntimeCore cancellation", () => {
|
|
|
603
779
|
});
|
|
604
780
|
|
|
605
781
|
describe("LocalThreadRuntimeCore suggestions", () => {
|
|
782
|
+
it("ignores suggestion generation from a superseded run", async () => {
|
|
783
|
+
let releaseFirst!: () => void;
|
|
784
|
+
let releaseSecond!: () => void;
|
|
785
|
+
const firstGate = new Promise<void>((resolve) => {
|
|
786
|
+
releaseFirst = resolve;
|
|
787
|
+
});
|
|
788
|
+
const secondGate = new Promise<void>((resolve) => {
|
|
789
|
+
releaseSecond = resolve;
|
|
790
|
+
});
|
|
791
|
+
let runCount = 0;
|
|
792
|
+
const generate = vi.fn().mockResolvedValue([{ prompt: "follow up" }]);
|
|
793
|
+
const thread = createThread(
|
|
794
|
+
{
|
|
795
|
+
async run() {
|
|
796
|
+
runCount += 1;
|
|
797
|
+
await (runCount === 1 ? firstGate : secondGate);
|
|
798
|
+
return { content: [{ type: "text", text: "done" }] };
|
|
799
|
+
},
|
|
800
|
+
},
|
|
801
|
+
{ suggestion: { generate } },
|
|
802
|
+
);
|
|
803
|
+
|
|
804
|
+
const firstAppend = thread.append(userMessage("first"));
|
|
805
|
+
await flush();
|
|
806
|
+
const secondAppend = thread.append(userMessage("second"));
|
|
807
|
+
await flush();
|
|
808
|
+
|
|
809
|
+
releaseFirst();
|
|
810
|
+
await firstAppend;
|
|
811
|
+
await flush();
|
|
812
|
+
|
|
813
|
+
expect(generate).not.toHaveBeenCalled();
|
|
814
|
+
expect(thread.suggestions).toEqual([]);
|
|
815
|
+
|
|
816
|
+
releaseSecond();
|
|
817
|
+
await secondAppend;
|
|
818
|
+
await flush();
|
|
819
|
+
|
|
820
|
+
expect(generate).toHaveBeenCalledOnce();
|
|
821
|
+
expect(thread.suggestions).toEqual([{ prompt: "follow up" }]);
|
|
822
|
+
});
|
|
823
|
+
|
|
606
824
|
it("cancelRun aborts pending suggestion generation", async () => {
|
|
607
825
|
const generate = vi.fn().mockImplementation(
|
|
608
826
|
({ signal }: { signal?: AbortSignal }) =>
|
|
@@ -656,8 +874,8 @@ describe("LocalThreadRuntimeCore suggestions", () => {
|
|
|
656
874
|
});
|
|
657
875
|
|
|
658
876
|
it("resolves append before suggestion generation completes", async () => {
|
|
659
|
-
let resolveSuggestions!: (value: readonly
|
|
660
|
-
const suggestionsDeferred = new Promise<readonly
|
|
877
|
+
let resolveSuggestions!: (value: readonly ThreadSuggestion[]) => void;
|
|
878
|
+
const suggestionsDeferred = new Promise<readonly ThreadSuggestion[]>(
|
|
661
879
|
(resolve) => {
|
|
662
880
|
resolveSuggestions = resolve;
|
|
663
881
|
},
|
|
@@ -681,9 +899,15 @@ describe("LocalThreadRuntimeCore suggestions", () => {
|
|
|
681
899
|
expect(generate).toHaveBeenCalledTimes(1);
|
|
682
900
|
expect(thread.suggestions).toEqual([]);
|
|
683
901
|
|
|
684
|
-
resolveSuggestions([
|
|
902
|
+
resolveSuggestions([
|
|
903
|
+
{ title: "Weather", label: "in SF", prompt: "What's the weather?" },
|
|
904
|
+
{ prompt: "follow up" },
|
|
905
|
+
]);
|
|
685
906
|
await new Promise((r) => setTimeout(r, 0));
|
|
686
|
-
expect(thread.suggestions).toEqual([
|
|
907
|
+
expect(thread.suggestions).toEqual([
|
|
908
|
+
{ title: "Weather", label: "in SF", prompt: "What's the weather?" },
|
|
909
|
+
{ prompt: "follow up" },
|
|
910
|
+
]);
|
|
687
911
|
});
|
|
688
912
|
});
|
|
689
913
|
|
|
@@ -1313,4 +1537,149 @@ describe("LocalThreadRuntimeCore runs", () => {
|
|
|
1313
1537
|
expect(thread.capabilities.attachments).toBe(false);
|
|
1314
1538
|
expect(thread.capabilities.feedback).toBe(false);
|
|
1315
1539
|
});
|
|
1540
|
+
|
|
1541
|
+
it("loads history when the adapter arrives after the first load", async () => {
|
|
1542
|
+
const adapter: ChatModelAdapter = {
|
|
1543
|
+
run: async () => ({ content: [] }),
|
|
1544
|
+
};
|
|
1545
|
+
const thread = createPlainThread(adapter);
|
|
1546
|
+
const load = vi.fn(async () => ({
|
|
1547
|
+
headId: "restored",
|
|
1548
|
+
messages: [
|
|
1549
|
+
{
|
|
1550
|
+
parentId: null,
|
|
1551
|
+
message: {
|
|
1552
|
+
id: "restored",
|
|
1553
|
+
role: "user" as const,
|
|
1554
|
+
content: [{ type: "text" as const, text: "hello" }],
|
|
1555
|
+
createdAt: new Date(0),
|
|
1556
|
+
metadata: { custom: {} },
|
|
1557
|
+
},
|
|
1558
|
+
},
|
|
1559
|
+
],
|
|
1560
|
+
}));
|
|
1561
|
+
|
|
1562
|
+
thread.__internal_load();
|
|
1563
|
+
expect(load).not.toHaveBeenCalled();
|
|
1564
|
+
expect(thread.messages).toHaveLength(0);
|
|
1565
|
+
|
|
1566
|
+
thread.__internal_setOptions({
|
|
1567
|
+
adapters: {
|
|
1568
|
+
chatModel: adapter,
|
|
1569
|
+
history: { load, append: async () => {} },
|
|
1570
|
+
},
|
|
1571
|
+
});
|
|
1572
|
+
await flush();
|
|
1573
|
+
|
|
1574
|
+
expect(load).toHaveBeenCalledOnce();
|
|
1575
|
+
expect(thread.messages.map((message) => message.id)).toEqual(["restored"]);
|
|
1576
|
+
});
|
|
1577
|
+
|
|
1578
|
+
it("does not load late history over a thread that already has messages", async () => {
|
|
1579
|
+
const adapter: ChatModelAdapter = {
|
|
1580
|
+
run: async () => ({ content: [] }),
|
|
1581
|
+
};
|
|
1582
|
+
const thread = createPlainThread(adapter);
|
|
1583
|
+
const load = vi.fn(async () => ({
|
|
1584
|
+
headId: "restored",
|
|
1585
|
+
messages: [
|
|
1586
|
+
{
|
|
1587
|
+
parentId: null,
|
|
1588
|
+
message: {
|
|
1589
|
+
id: "restored",
|
|
1590
|
+
role: "user" as const,
|
|
1591
|
+
content: [{ type: "text" as const, text: "hello" }],
|
|
1592
|
+
createdAt: new Date(0),
|
|
1593
|
+
metadata: { custom: {} },
|
|
1594
|
+
},
|
|
1595
|
+
},
|
|
1596
|
+
],
|
|
1597
|
+
}));
|
|
1598
|
+
|
|
1599
|
+
thread.__internal_load();
|
|
1600
|
+
await thread.append({ ...userMessage("typed"), startRun: false });
|
|
1601
|
+
expect(thread.messages).toHaveLength(1);
|
|
1602
|
+
|
|
1603
|
+
thread.__internal_setOptions({
|
|
1604
|
+
adapters: {
|
|
1605
|
+
chatModel: adapter,
|
|
1606
|
+
history: { load, append: async () => {} },
|
|
1607
|
+
},
|
|
1608
|
+
});
|
|
1609
|
+
await flush();
|
|
1610
|
+
|
|
1611
|
+
expect(load).not.toHaveBeenCalled();
|
|
1612
|
+
expect(thread.messages.map((message) => message.content)).toEqual([
|
|
1613
|
+
[{ type: "text", text: "typed" }],
|
|
1614
|
+
]);
|
|
1615
|
+
});
|
|
1616
|
+
|
|
1617
|
+
it("logs a rejected late history load", async () => {
|
|
1618
|
+
const adapter: ChatModelAdapter = {
|
|
1619
|
+
run: async () => ({ content: [] }),
|
|
1620
|
+
};
|
|
1621
|
+
const thread = createPlainThread(adapter);
|
|
1622
|
+
const error = new Error("history unavailable");
|
|
1623
|
+
const consoleError = vi
|
|
1624
|
+
.spyOn(console, "error")
|
|
1625
|
+
.mockImplementation(() => {});
|
|
1626
|
+
|
|
1627
|
+
thread.__internal_load();
|
|
1628
|
+
thread.__internal_setOptions({
|
|
1629
|
+
adapters: {
|
|
1630
|
+
chatModel: adapter,
|
|
1631
|
+
history: {
|
|
1632
|
+
load: async () => {
|
|
1633
|
+
throw error;
|
|
1634
|
+
},
|
|
1635
|
+
append: async () => {},
|
|
1636
|
+
},
|
|
1637
|
+
},
|
|
1638
|
+
});
|
|
1639
|
+
await flush();
|
|
1640
|
+
|
|
1641
|
+
expect(consoleError).toHaveBeenCalledWith(
|
|
1642
|
+
"[assistant-ui] local thread history load failed:",
|
|
1643
|
+
error,
|
|
1644
|
+
);
|
|
1645
|
+
});
|
|
1646
|
+
});
|
|
1647
|
+
|
|
1648
|
+
describe("LocalRuntimeCore composer.canCancel", () => {
|
|
1649
|
+
it("is true after append when the thread was created with initial messages", async () => {
|
|
1650
|
+
const core = new LocalRuntimeCore(
|
|
1651
|
+
{
|
|
1652
|
+
adapters: {
|
|
1653
|
+
chatModel: {
|
|
1654
|
+
async *run({ abortSignal }) {
|
|
1655
|
+
await new Promise<void>((resolve) => {
|
|
1656
|
+
if (abortSignal.aborted) {
|
|
1657
|
+
resolve();
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1660
|
+
abortSignal.addEventListener("abort", () => resolve(), {
|
|
1661
|
+
once: true,
|
|
1662
|
+
});
|
|
1663
|
+
});
|
|
1664
|
+
},
|
|
1665
|
+
},
|
|
1666
|
+
},
|
|
1667
|
+
},
|
|
1668
|
+
[
|
|
1669
|
+
{
|
|
1670
|
+
role: "assistant",
|
|
1671
|
+
content: [{ type: "text", text: "Hello" }],
|
|
1672
|
+
status: { type: "complete", reason: "stop" },
|
|
1673
|
+
},
|
|
1674
|
+
],
|
|
1675
|
+
);
|
|
1676
|
+
const thread = core.threads.getMainThreadRuntimeCore();
|
|
1677
|
+
expect(thread.composer.canCancel).toBe(false);
|
|
1678
|
+
|
|
1679
|
+
void thread.append(userMessage("Run"));
|
|
1680
|
+
await flush();
|
|
1681
|
+
|
|
1682
|
+
expect(thread.composer.canCancel).toBe(true);
|
|
1683
|
+
thread.cancelRun();
|
|
1684
|
+
});
|
|
1316
1685
|
});
|
|
@@ -22,7 +22,7 @@ import type {
|
|
|
22
22
|
ThreadAssistantMessage,
|
|
23
23
|
} from "../../types/message";
|
|
24
24
|
import type { RunConfig } from "../../types/message";
|
|
25
|
-
import { toAssistantError } from "../../types/error";
|
|
25
|
+
import { MessageNotSentError, toAssistantError } from "../../types/error";
|
|
26
26
|
import type { ModelContextProvider } from "../../model-context/types";
|
|
27
27
|
import {
|
|
28
28
|
createMessageQueue,
|
|
@@ -33,6 +33,11 @@ import {
|
|
|
33
33
|
EMPTY_QUEUE_ITEMS,
|
|
34
34
|
type QueueItemState,
|
|
35
35
|
} from "../../store/scopes/queue-item";
|
|
36
|
+
import {
|
|
37
|
+
captureThreadRuntimeGeneration,
|
|
38
|
+
invalidateThreadRuntime,
|
|
39
|
+
isThreadRuntimeGenerationCurrent,
|
|
40
|
+
} from "../../runtime/utils/thread-runtime-lifecycle";
|
|
36
41
|
|
|
37
42
|
class AbortError extends Error {
|
|
38
43
|
override name = "AbortError";
|
|
@@ -162,6 +167,7 @@ export class LocalThreadRuntimeCore
|
|
|
162
167
|
public __internal_setOptions(options: LocalRuntimeOptionsBase) {
|
|
163
168
|
if (this._options === options) return;
|
|
164
169
|
|
|
170
|
+
const previousHistory = this._options?.adapters.history;
|
|
165
171
|
this._options = options;
|
|
166
172
|
|
|
167
173
|
let hasUpdates = false;
|
|
@@ -234,13 +240,31 @@ export class LocalThreadRuntimeCore
|
|
|
234
240
|
}
|
|
235
241
|
|
|
236
242
|
if (hasUpdates) this._notifySubscribers();
|
|
243
|
+
|
|
244
|
+
if (
|
|
245
|
+
this._loadRequested &&
|
|
246
|
+
!this._loadPromise &&
|
|
247
|
+
!previousHistory &&
|
|
248
|
+
options.adapters.history &&
|
|
249
|
+
this.messages.length === 0
|
|
250
|
+
) {
|
|
251
|
+
void this.__internal_load().catch((error: unknown) => {
|
|
252
|
+
console.error(
|
|
253
|
+
"[assistant-ui] local thread history load failed:",
|
|
254
|
+
error,
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
237
258
|
}
|
|
238
259
|
|
|
239
260
|
private _loadPromise: Promise<void> | undefined;
|
|
261
|
+
private _loadRequested = false;
|
|
240
262
|
public __internal_load() {
|
|
263
|
+
this._loadRequested = true;
|
|
241
264
|
if (this._loadPromise) return this._loadPromise;
|
|
265
|
+
if (!this.adapters.history) return Promise.resolve();
|
|
242
266
|
|
|
243
|
-
const promise = this.adapters.history
|
|
267
|
+
const promise = this.adapters.history.load();
|
|
244
268
|
|
|
245
269
|
this._isLoading = true;
|
|
246
270
|
this._notifySubscribers();
|
|
@@ -312,38 +336,72 @@ export class LocalThreadRuntimeCore
|
|
|
312
336
|
this._queue?.adapter.remove(queueItemId);
|
|
313
337
|
}
|
|
314
338
|
|
|
339
|
+
private _rollbackAppend(messageId: string) {
|
|
340
|
+
try {
|
|
341
|
+
this.repository.deleteMessage(messageId);
|
|
342
|
+
} catch {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
this._notifySubscribers();
|
|
346
|
+
}
|
|
347
|
+
|
|
315
348
|
private async _runAppend(rawMessage: AppendMessage): Promise<void> {
|
|
316
349
|
// Stamped here rather than in `append` so a queued message is gated after
|
|
317
350
|
// the flush re-pointed its parentId at the current tail.
|
|
351
|
+
const generation = captureThreadRuntimeGeneration(this);
|
|
318
352
|
const message = this.enrichAppendMetadata(rawMessage);
|
|
319
353
|
this.ensureInitialized();
|
|
320
354
|
|
|
321
|
-
const initPromise = this._getInitializePromise?.();
|
|
322
|
-
if (initPromise) {
|
|
323
|
-
await initPromise;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
355
|
const newMessage = fromThreadMessageLike(message, generateId(), {
|
|
327
356
|
type: "complete",
|
|
328
357
|
reason: "unknown",
|
|
329
358
|
});
|
|
330
359
|
this.repository.addOrUpdateMessage(message.parentId, newMessage);
|
|
331
|
-
this.
|
|
360
|
+
this._notifySubscribers();
|
|
361
|
+
|
|
362
|
+
// Initialization only gates the history write and the run; the message
|
|
363
|
+
// is already on screen. A failed barrier rolls the optimistic message
|
|
364
|
+
// back and rejects as an unsent message so the composer restores the
|
|
365
|
+
// draft; a thread invalidated mid-wait rolls back silently.
|
|
366
|
+
try {
|
|
367
|
+
const initPromise = this._getInitializePromise?.();
|
|
368
|
+
if (initPromise) {
|
|
369
|
+
await initPromise;
|
|
370
|
+
}
|
|
371
|
+
} catch (error) {
|
|
372
|
+
this._rollbackAppend(newMessage.id);
|
|
373
|
+
if (!isThreadRuntimeGenerationCurrent(this, generation)) return;
|
|
374
|
+
const notSent = new MessageNotSentError();
|
|
375
|
+
notSent.cause = error;
|
|
376
|
+
throw notSent;
|
|
377
|
+
}
|
|
378
|
+
if (!isThreadRuntimeGenerationCurrent(this, generation)) {
|
|
379
|
+
this._rollbackAppend(newMessage.id);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const historyWrite = this._options.adapters.history?.append({
|
|
332
383
|
parentId: message.parentId,
|
|
333
384
|
message: newMessage,
|
|
334
385
|
...(message.runConfig !== undefined && { runConfig: message.runConfig }),
|
|
335
386
|
});
|
|
387
|
+
void historyWrite?.catch(() => {});
|
|
336
388
|
|
|
337
389
|
const startRun = message.startRun ?? message.role === "user";
|
|
338
390
|
if (startRun) {
|
|
339
|
-
await
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
391
|
+
const [runResult, historyResult] = await Promise.allSettled([
|
|
392
|
+
this.startRun({
|
|
393
|
+
parentId: newMessage.id,
|
|
394
|
+
sourceId: message.sourceId,
|
|
395
|
+
runConfig: message.runConfig ?? {},
|
|
396
|
+
}),
|
|
397
|
+
historyWrite,
|
|
398
|
+
]);
|
|
399
|
+
if (runResult.status === "rejected") throw runResult.reason;
|
|
400
|
+
if (historyResult.status === "rejected") throw historyResult.reason;
|
|
344
401
|
} else {
|
|
345
402
|
this.repository.resetHead(newMessage.id);
|
|
346
403
|
this._notifySubscribers();
|
|
404
|
+
await historyWrite;
|
|
347
405
|
}
|
|
348
406
|
}
|
|
349
407
|
|
|
@@ -380,6 +438,10 @@ export class LocalThreadRuntimeCore
|
|
|
380
438
|
throw new Error("Runtime does not support importing external states.");
|
|
381
439
|
}
|
|
382
440
|
|
|
441
|
+
public unstable_notifySessionReset(): void {
|
|
442
|
+
throw new Error("Runtime does not support resetting sessions.");
|
|
443
|
+
}
|
|
444
|
+
|
|
383
445
|
public async startRun(
|
|
384
446
|
{ parentId, runConfig }: StartRunConfig,
|
|
385
447
|
runCallback?: ChatModelAdapter["run"],
|
|
@@ -424,6 +486,7 @@ export class LocalThreadRuntimeCore
|
|
|
424
486
|
this._activeRun = run;
|
|
425
487
|
this._runGeneration++;
|
|
426
488
|
|
|
489
|
+
let active = false;
|
|
427
490
|
try {
|
|
428
491
|
// mark busy for runs not started through the queue (regenerate, resume)
|
|
429
492
|
this._queue?.notifyBusy();
|
|
@@ -447,7 +510,7 @@ export class LocalThreadRuntimeCore
|
|
|
447
510
|
// the settle belongs to this run only while it is still the active run
|
|
448
511
|
// or was cancelled (the engine expects a cancelled run's settle); a run
|
|
449
512
|
// superseded by a newer one stays silent
|
|
450
|
-
|
|
513
|
+
active = this._activeRun === run;
|
|
451
514
|
if (active) this._activeRun = null;
|
|
452
515
|
if (active || run.cancelled) {
|
|
453
516
|
queueMicrotask(() => this._queue?.notifyIdle());
|
|
@@ -455,6 +518,7 @@ export class LocalThreadRuntimeCore
|
|
|
455
518
|
}
|
|
456
519
|
|
|
457
520
|
if (
|
|
521
|
+
active &&
|
|
458
522
|
this.adapters.suggestion &&
|
|
459
523
|
message.status?.type !== "requires-action"
|
|
460
524
|
) {
|
|
@@ -674,6 +738,7 @@ export class LocalThreadRuntimeCore
|
|
|
674
738
|
}
|
|
675
739
|
|
|
676
740
|
public detach() {
|
|
741
|
+
invalidateThreadRuntime(this);
|
|
677
742
|
// drop the queue so pending items cannot dispatch on a detached thread
|
|
678
743
|
this._queue = null;
|
|
679
744
|
const error = new AbortError(true);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ThreadMessage } from "../../types/message";
|
|
2
2
|
import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
|
|
3
3
|
import type { AssistantStream } from "assistant-stream";
|
|
4
|
+
import type { ThreadHistoryAdapter } from "../../adapters/thread-history";
|
|
5
|
+
import type { AttachmentAdapter } from "../../adapters/attachment";
|
|
6
|
+
import type { ModelContextProvider } from "../../model-context/types";
|
|
4
7
|
|
|
5
8
|
/* oxlint-disable typescript/no-explicit-any -- structural stand-in for ComponentType without depending on react types */
|
|
6
9
|
type RemoteThreadListProviderProps = { children?: any };
|
|
@@ -33,6 +36,12 @@ export type RemoteThreadListPageOptions = {
|
|
|
33
36
|
after?: string | undefined;
|
|
34
37
|
};
|
|
35
38
|
|
|
39
|
+
export type RuntimeAdapters = {
|
|
40
|
+
modelContext?: ModelContextProvider | undefined;
|
|
41
|
+
history?: ThreadHistoryAdapter | undefined;
|
|
42
|
+
attachments?: AttachmentAdapter | undefined;
|
|
43
|
+
};
|
|
44
|
+
|
|
36
45
|
export type RemoteThreadListAdapter = {
|
|
37
46
|
list(params?: RemoteThreadListPageOptions): Promise<RemoteThreadListResponse>;
|
|
38
47
|
|
|
@@ -56,12 +65,33 @@ export type RemoteThreadListAdapter = {
|
|
|
56
65
|
* inject per-thread context such as a history or attachments adapter (see
|
|
57
66
|
* `useCloudThreadListAdapter` for the canonical shape).
|
|
58
67
|
*
|
|
68
|
+
* `useRemoteThreadListRuntime` renders this component when present. If it
|
|
69
|
+
* is omitted, that host synthesizes a `RuntimeAdapterProvider` from
|
|
70
|
+
* `unstable_useAdapters`. The `RemoteThreadList` store entry ignores it;
|
|
71
|
+
* expose `unstable_useAdapters` for that host.
|
|
72
|
+
*
|
|
59
73
|
* The Provider must render `children` on its first commit; deferring them
|
|
60
74
|
* behind a loading state, a Suspense boundary, or a `useEffect`-gated render
|
|
61
75
|
* is unsupported and leaves thread context unavailable to downstream
|
|
62
76
|
* consumers. Load data inside an always-mounted child instead.
|
|
63
77
|
*/
|
|
64
78
|
unstable_Provider?: RemoteThreadListProviderComponent | undefined;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Hook the `RemoteThreadList` store entry calls once for the main-thread
|
|
82
|
+
* slot, then provides to the `thread` factory. This is not mounted per
|
|
83
|
+
* listed thread. `useRemoteThreadListRuntime` also calls it when
|
|
84
|
+
* `unstable_Provider` is omitted. Resolve `threadListItem` lazily on each
|
|
85
|
+
* adapter call; do not capture it at hook mount. The hook must keep a
|
|
86
|
+
* stable hook count across adapter swaps; a different count throws.
|
|
87
|
+
* Memoize the returned object.
|
|
88
|
+
*
|
|
89
|
+
* Per-thread history also requires the `thread` factory to be keyed by
|
|
90
|
+
* thread id (`withKey(id, thread(...))`). History loaders such as
|
|
91
|
+
* `useExternalHistory` run once per mount; an unkeyed factory keeps one
|
|
92
|
+
* instance across switches and the next thread's messages never load.
|
|
93
|
+
*/
|
|
94
|
+
unstable_useAdapters?: (() => RuntimeAdapters | null | undefined) | undefined;
|
|
65
95
|
};
|
|
66
96
|
|
|
67
97
|
export type RemoteThreadListOptions = {
|