@assistant-ui/core 0.3.14 → 0.3.16
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/voice.js +11 -0
- package/dist/adapters/voice.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +8 -2
- package/dist/internal.js +9 -3
- package/dist/model-context/frame/host.d.ts +1 -1
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +20 -5
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/frame/provider.d.ts +6 -0
- package/dist/model-context/frame/provider.d.ts.map +1 -1
- package/dist/model-context/frame/provider.js +126 -16
- package/dist/model-context/frame/provider.js.map +1 -1
- package/dist/model-context/frame/types.d.ts +4 -1
- package/dist/model-context/frame/types.d.ts.map +1 -1
- package/dist/model-context/frame/types.js.map +1 -1
- package/dist/model-context/interactable-composer-metadata.d.ts +1 -1
- package/dist/model-context/interactable-composer-metadata.js +1 -1
- package/dist/model-context/interactable-composer-metadata.js.map +1 -1
- package/dist/model-context/registry.d.ts +2 -2
- package/dist/model-context/registry.d.ts.map +1 -1
- package/dist/model-context/registry.js +9 -28
- package/dist/model-context/registry.js.map +1 -1
- package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
- package/dist/react/client/InMemoryThreadList.js +24 -20
- package/dist/react/client/InMemoryThreadList.js.map +1 -1
- package/dist/react/client/Interactables.d.ts.map +1 -1
- package/dist/react/client/Interactables.js +59 -177
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/RemoteThreadList.d.ts +1 -1
- package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
- package/dist/react/client/RemoteThreadList.js +117 -78
- package/dist/react/client/RemoteThreadList.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
- package/dist/react/interactables-legacy/Interactables.js +56 -174
- package/dist/react/interactables-legacy/Interactables.js.map +1 -1
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +24 -0
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -0
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +138 -0
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -0
- package/dist/react/primitive-hooks/useActionBarCopy.js +7 -1
- package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts +3 -0
- package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadIsEmpty.js +3 -0
- package/dist/react/primitive-hooks/useThreadIsEmpty.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadIsRunning.d.ts +3 -0
- package/dist/react/primitive-hooks/useThreadIsRunning.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadIsRunning.js +3 -0
- package/dist/react/primitive-hooks/useThreadIsRunning.js.map +1 -1
- package/dist/react/primitives/composer/ComposerIf.js.map +1 -1
- package/dist/react/primitives/composer/ComposerQueue.d.ts +2 -1
- package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageParts.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +3 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +6 -0
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +7 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +174 -64
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.js +2 -0
- package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
- package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/runManager.js +2 -12
- package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -1
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +1 -1
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +3 -10
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +20 -67
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +12 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js +10 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js.map +1 -0
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +38 -5
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -1
- package/dist/react/runtimes/createMessageConverter.d.ts +2 -1
- package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.d.ts +6 -25
- package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.js +33 -233
- package/dist/react/runtimes/external-message-converter.js.map +1 -1
- package/dist/runtime/api/composer-runtime.d.ts +1 -1
- package/dist/runtime/api/composer-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +1 -2
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +14 -29
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -4
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +3 -9
- package/dist/runtime/base/base-thread-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 +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
- package/dist/runtime/interfaces/composer-runtime-core.d.ts +1 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts +1 -1
- package/dist/runtime/internal.d.ts +2 -1
- package/dist/runtime/internal.js +2 -1
- package/dist/runtime/queue/external-thread-queue-adapter.d.ts +1 -1
- package/dist/runtime/queue/message-queue.d.ts.map +1 -1
- package/dist/runtime/queue/message-queue.js +1 -1
- package/dist/runtime/queue/message-queue.js.map +1 -1
- package/dist/runtime/queue/queue-item.d.ts +12 -0
- package/dist/runtime/queue/queue-item.d.ts.map +1 -0
- package/dist/runtime/queue/queue-item.js +6 -0
- package/dist/runtime/queue/queue-item.js.map +1 -0
- package/dist/runtime/utils/abortable-thread-load.d.ts +15 -0
- package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -0
- package/dist/runtime/utils/abortable-thread-load.js +35 -0
- package/dist/runtime/utils/abortable-thread-load.js.map +1 -0
- package/dist/runtime/utils/attachment-add-operations.d.ts +18 -0
- package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -0
- package/dist/runtime/utils/attachment-add-operations.js +43 -0
- package/dist/runtime/utils/attachment-add-operations.js.map +1 -0
- package/dist/runtime/utils/auto-status.d.ts +6 -1
- package/dist/runtime/utils/auto-status.d.ts.map +1 -1
- package/dist/runtime/utils/auto-status.js +7 -1
- package/dist/runtime/utils/auto-status.js.map +1 -1
- package/dist/runtime/utils/external-message-conversion.d.ts +47 -0
- package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -0
- package/dist/runtime/utils/external-message-conversion.js +221 -0
- package/dist/runtime/utils/external-message-conversion.js.map +1 -0
- package/dist/runtime/utils/external-store-message.d.ts +2 -1
- package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
- package/dist/runtime/utils/external-store-message.js +2 -1
- package/dist/runtime/utils/external-store-message.js.map +1 -1
- package/dist/runtime/utils/message-repository-session.d.ts +30 -0
- package/dist/runtime/utils/message-repository-session.d.ts.map +1 -0
- package/dist/runtime/utils/message-repository-session.js +130 -0
- package/dist/runtime/utils/message-repository-session.js.map +1 -0
- package/dist/runtime/utils/message-repository.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.js +30 -22
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtime/utils/pending-tool-calls.d.ts +20 -0
- package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -0
- package/dist/runtime/utils/pending-tool-calls.js +22 -0
- package/dist/runtime/utils/pending-tool-calls.js.map +1 -0
- package/dist/runtime/utils/thread-message-like.d.ts +2 -1
- package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +2 -5
- 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 +3 -10
- 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 +19 -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 +124 -20
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +102 -0
- package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -0
- package/dist/runtimes/inert/InertThreadRuntimeCore.js +156 -0
- package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -0
- package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +45 -8
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +12 -60
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +10 -145
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
- package/dist/runtimes/remote-thread-list/adapter-changed.d.ts +9 -0
- package/dist/runtimes/remote-thread-list/adapter-changed.d.ts.map +1 -0
- package/dist/runtimes/remote-thread-list/adapter-changed.js +13 -0
- package/dist/runtimes/remote-thread-list/adapter-changed.js.map +1 -0
- package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/empty-thread-core.js +16 -155
- package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
- package/dist/runtimes/remote-thread-list/optimistic-state.d.ts +2 -0
- package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/optimistic-state.js +11 -0
- package/dist/runtimes/remote-thread-list/optimistic-state.js.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.js +77 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +3 -0
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -0
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
- package/dist/store/clients/external-thread.d.ts.map +1 -1
- package/dist/store/clients/external-thread.js +75 -100
- package/dist/store/clients/external-thread.js.map +1 -1
- package/dist/store/clients/single-thread-list.d.ts.map +1 -1
- package/dist/store/clients/single-thread-list.js +1 -1
- package/dist/store/clients/single-thread-list.js.map +1 -1
- package/dist/store/clients/suggestions.d.ts.map +1 -1
- package/dist/store/clients/suggestions.js +16 -7
- package/dist/store/clients/suggestions.js.map +1 -1
- package/dist/store/index.d.ts +4 -2
- package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -1
- package/dist/store/runtime-clients/handle-runtime-action.js +2 -0
- package/dist/store/runtime-clients/handle-runtime-action.js.map +1 -1
- package/dist/store/scope-registration.d.ts +1 -1
- package/dist/store/scopes/composer.d.ts +2 -1
- package/dist/store/scopes/composer.d.ts.map +1 -1
- package/dist/store/scopes/message.d.ts +1 -1
- package/dist/store/scopes/queue-item.d.ts +2 -9
- package/dist/store/scopes/queue-item.d.ts.map +1 -1
- package/dist/store/scopes/queue-item.js +0 -6
- package/dist/types/message.d.ts +8 -4
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js.map +1 -1
- package/dist/utils/data-url.d.ts +14 -1
- package/dist/utils/data-url.d.ts.map +1 -1
- package/dist/utils/data-url.js +13 -1
- package/dist/utils/data-url.js.map +1 -1
- package/dist/utils/invoke-user-callback.d.ts +5 -0
- package/dist/utils/invoke-user-callback.d.ts.map +1 -0
- package/dist/utils/invoke-user-callback.js +19 -0
- package/dist/utils/invoke-user-callback.js.map +1 -0
- package/dist/utils/normalizePartStatus.js.map +1 -1
- package/dist/utils/silent-runtime-action.d.ts +6 -0
- package/dist/utils/silent-runtime-action.d.ts.map +1 -0
- package/dist/utils/silent-runtime-action.js +7 -0
- package/dist/utils/silent-runtime-action.js.map +1 -0
- package/package.json +10 -10
- package/src/adapters/voice.test.ts +66 -1
- package/src/adapters/voice.ts +8 -0
- package/src/internal.ts +13 -0
- package/src/model-context/frame/host.test.ts +152 -9
- package/src/model-context/frame/host.ts +23 -5
- package/src/model-context/frame/provider.test.ts +601 -19
- package/src/model-context/frame/provider.ts +158 -20
- package/src/model-context/frame/types.ts +5 -0
- package/src/model-context/interactable-composer-metadata.ts +1 -1
- package/src/model-context/registry.test.ts +44 -0
- package/src/model-context/registry.ts +13 -35
- package/src/react/client/InMemoryThreadList.test.tsx +49 -0
- package/src/react/client/InMemoryThreadList.ts +7 -1
- package/src/react/client/Interactables.ts +19 -178
- package/src/react/client/RemoteThreadList.test.ts +292 -0
- package/src/react/client/RemoteThreadList.ts +119 -82
- package/src/react/interactables-legacy/Interactables.ts +20 -177
- package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +161 -0
- package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +206 -0
- package/src/react/primitive-hooks/useActionBarCopy.test.ts +15 -0
- package/src/react/primitive-hooks/useActionBarCopy.ts +13 -3
- package/src/react/primitive-hooks/useThreadIsEmpty.ts +3 -0
- package/src/react/primitive-hooks/useThreadIsRunning.ts +3 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +12 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.republish.test.ts +87 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +251 -76
- package/src/react/runtimes/RemoteThreadResource.ts +2 -0
- package/src/react/runtimes/assistant-transport/runManager.ts +4 -17
- package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +58 -0
- package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +3 -1
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +92 -0
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +40 -141
- package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.test.ts +37 -0
- package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.ts +18 -0
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +98 -3
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +68 -6
- package/src/react/runtimes/external-message-converter.test.ts +0 -14
- package/src/react/runtimes/external-message-converter.test.tsx +77 -0
- package/src/react/runtimes/external-message-converter.ts +58 -481
- package/src/react/runtimes/useLocalRuntime.test.tsx +5 -5
- package/src/runtime/api/composer-runtime.ts +1 -1
- package/src/runtime/api/message-runtime.test.ts +28 -1
- package/src/runtime/base/base-composer-runtime-core.ts +20 -40
- package/src/runtime/base/base-thread-runtime-core.test.ts +16 -0
- package/src/runtime/base/base-thread-runtime-core.ts +6 -11
- package/src/runtime/base/default-thread-composer-runtime-core.ts +1 -4
- package/src/runtime/interfaces/composer-runtime-core.ts +1 -1
- package/src/runtime/interfaces/thread-runtime-core.ts +1 -1
- package/src/runtime/internal.ts +5 -0
- package/src/runtime/queue/external-thread-queue-adapter.ts +1 -1
- package/src/runtime/queue/message-queue.ts +1 -4
- package/src/runtime/queue/queue-item.ts +10 -0
- package/src/runtime/utils/abortable-thread-load.test.ts +87 -0
- package/src/runtime/utils/abortable-thread-load.ts +53 -0
- package/src/runtime/utils/attachment-add-operations.ts +59 -0
- package/src/runtime/utils/auto-status.ts +33 -0
- package/src/runtime/utils/external-message-conversion.test.ts +113 -0
- package/src/runtime/utils/external-message-conversion.ts +494 -0
- package/src/runtime/utils/external-store-message.ts +2 -0
- package/src/runtime/utils/message-repository-session.test.ts +195 -0
- package/src/runtime/utils/message-repository-session.ts +176 -0
- package/src/runtime/utils/message-repository.ts +33 -22
- package/src/runtime/utils/pending-tool-calls.test.ts +57 -0
- package/src/runtime/utils/pending-tool-calls.ts +35 -0
- package/src/runtime/utils/thread-message-like.ts +2 -0
- package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +6 -14
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +179 -28
- package/src/runtimes/inert/InertThreadRuntimeCore.test.ts +253 -0
- package/src/runtimes/inert/InertThreadRuntimeCore.ts +232 -0
- package/src/runtimes/local/local-thread-runtime-core.test.ts +491 -1
- package/src/runtimes/local/local-thread-runtime-core.ts +86 -11
- package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +17 -209
- package/src/runtimes/remote-thread-list/adapter-changed.ts +10 -0
- package/src/runtimes/remote-thread-list/empty-thread-core.ts +28 -221
- package/src/runtimes/remote-thread-list/optimistic-state.test.ts +18 -0
- package/src/runtimes/remote-thread-list/optimistic-state.ts +13 -0
- package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +37 -0
- package/src/runtimes/remote-thread-list/remote-thread-state.ts +123 -0
- package/src/runtimes/remote-thread-list/types.ts +4 -0
- package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -0
- package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +140 -0
- package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +3 -0
- package/src/store/clients/external-thread.ts +17 -62
- package/src/store/clients/single-thread-list.ts +4 -1
- package/src/store/clients/suggestions.test.ts +31 -0
- package/src/store/clients/suggestions.ts +18 -19
- package/src/store/runtime-clients/handle-runtime-action.test.ts +40 -0
- package/src/store/runtime-clients/handle-runtime-action.ts +3 -0
- package/src/store/scopes/message.ts +1 -1
- package/src/store/scopes/queue-item.ts +2 -9
- package/src/tests/MessageRepository.test.ts +125 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-errors.test.ts +2 -2
- package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +194 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +8 -6
- package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +158 -0
- package/src/tests/empty-thread-core.test.ts +6 -0
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +184 -0
- package/src/tests/external-store-thread-runtime-core.test.ts +642 -0
- package/src/tests/external-thread-attachments.test.tsx +173 -0
- package/src/tests/external-thread-isEmpty.test.tsx +100 -0
- package/src/tests/remote-thread-list-adapter-switch.test.ts +383 -0
- package/src/tests/single-thread-list-item.test.tsx +47 -0
- package/src/tests/thread-message-like.test.ts +39 -1
- package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +67 -0
- package/src/types/message.ts +9 -4
- package/src/utils/data-url.test.ts +52 -1
- package/src/utils/data-url.ts +21 -0
- package/src/utils/invoke-user-callback.test.ts +82 -0
- package/src/utils/invoke-user-callback.ts +31 -0
- package/src/utils/normalizePartStatus.ts +1 -1
- package/src/utils/silent-runtime-action.ts +6 -0
- package/dist/store/scopes/queue-item.js.map +0 -1
|
@@ -7,7 +7,11 @@ import type {
|
|
|
7
7
|
} from "../../runtime/utils/chat-model-adapter";
|
|
8
8
|
import type { AppendMessage } from "../../types/message";
|
|
9
9
|
import type { LocalRuntimeOptionsBase } from "./local-runtime-options";
|
|
10
|
-
import
|
|
10
|
+
import {
|
|
11
|
+
ExportedMessageRepository,
|
|
12
|
+
type ExportedMessageRepositoryItem,
|
|
13
|
+
} from "../../runtime/utils/message-repository";
|
|
14
|
+
import type { ThreadMessageLike } from "../../runtime/utils/thread-message-like";
|
|
11
15
|
import type { ThreadSuggestion } from "../../runtime/interfaces/thread-runtime-core";
|
|
12
16
|
import { isMessageNotSentError } from "../../types/error";
|
|
13
17
|
|
|
@@ -673,6 +677,325 @@ describe("LocalThreadRuntimeCore tool approvals", () => {
|
|
|
673
677
|
});
|
|
674
678
|
|
|
675
679
|
describe("LocalThreadRuntimeCore cancellation", () => {
|
|
680
|
+
it("settles a superseded tool-call result without aborting its replacement", async () => {
|
|
681
|
+
let resolveFirst!: (result: ChatModelRunResult) => void;
|
|
682
|
+
let resolveSecond!: (result: ChatModelRunResult) => void;
|
|
683
|
+
const firstResult = new Promise<ChatModelRunResult>((resolve) => {
|
|
684
|
+
resolveFirst = resolve;
|
|
685
|
+
});
|
|
686
|
+
const secondResult = new Promise<ChatModelRunResult>((resolve) => {
|
|
687
|
+
resolveSecond = resolve;
|
|
688
|
+
});
|
|
689
|
+
const runs: ChatModelRunOptions[] = [];
|
|
690
|
+
const thread = createThread({
|
|
691
|
+
run(options) {
|
|
692
|
+
runs.push(options);
|
|
693
|
+
return runs.length === 1 ? firstResult : secondResult;
|
|
694
|
+
},
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
const firstAppend = thread.append(userMessage("first"));
|
|
698
|
+
await flush();
|
|
699
|
+
const supersededMessageId = thread.messages.at(-1)!.id;
|
|
700
|
+
const secondAppend = thread.append({
|
|
701
|
+
...userMessage("second"),
|
|
702
|
+
parentId: supersededMessageId,
|
|
703
|
+
});
|
|
704
|
+
await flush();
|
|
705
|
+
|
|
706
|
+
expect(runs).toHaveLength(2);
|
|
707
|
+
expect(runs[0]?.abortSignal.aborted).toBe(true);
|
|
708
|
+
expect(runs[1]?.abortSignal.aborted).toBe(false);
|
|
709
|
+
|
|
710
|
+
resolveFirst(toolCallResult("lookup_weather"));
|
|
711
|
+
await firstAppend;
|
|
712
|
+
|
|
713
|
+
expect(runs).toHaveLength(2);
|
|
714
|
+
expect(runs[1]?.abortSignal.aborted).toBe(false);
|
|
715
|
+
expect(
|
|
716
|
+
thread.messages.find((item) => item.id === supersededMessageId)?.status,
|
|
717
|
+
).toEqual({
|
|
718
|
+
type: "incomplete",
|
|
719
|
+
reason: "cancelled",
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
resolveSecond({ content: [{ type: "text", text: "replacement" }] });
|
|
723
|
+
await secondAppend;
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
it("cancels a superseded approval pause", async () => {
|
|
727
|
+
let resolveFirst!: (result: ChatModelRunResult) => void;
|
|
728
|
+
let resolveSecond!: (result: ChatModelRunResult) => void;
|
|
729
|
+
const firstResult = new Promise<ChatModelRunResult>((resolve) => {
|
|
730
|
+
resolveFirst = resolve;
|
|
731
|
+
});
|
|
732
|
+
const secondResult = new Promise<ChatModelRunResult>((resolve) => {
|
|
733
|
+
resolveSecond = resolve;
|
|
734
|
+
});
|
|
735
|
+
const runs: ChatModelRunOptions[] = [];
|
|
736
|
+
const thread = createThread({
|
|
737
|
+
run(options) {
|
|
738
|
+
runs.push(options);
|
|
739
|
+
return runs.length === 1 ? firstResult : secondResult;
|
|
740
|
+
},
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
const firstAppend = thread.append(userMessage("first"));
|
|
744
|
+
await flush();
|
|
745
|
+
const supersededMessageId = thread.messages.at(-1)!.id;
|
|
746
|
+
const secondAppend = thread.append({
|
|
747
|
+
...userMessage("second"),
|
|
748
|
+
parentId: supersededMessageId,
|
|
749
|
+
});
|
|
750
|
+
await flush();
|
|
751
|
+
|
|
752
|
+
resolveFirst(toolCallResult("deploy", { id: "approval-1" }));
|
|
753
|
+
await firstAppend;
|
|
754
|
+
|
|
755
|
+
expect(
|
|
756
|
+
thread.messages.find((item) => item.id === supersededMessageId)?.status,
|
|
757
|
+
).toEqual({
|
|
758
|
+
type: "incomplete",
|
|
759
|
+
reason: "cancelled",
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
resolveSecond({ content: [{ type: "text", text: "replacement" }] });
|
|
763
|
+
await secondAppend;
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
it("ignores a stale result after addToolResult replaces the same message", async () => {
|
|
767
|
+
let releaseFirst!: () => void;
|
|
768
|
+
let resolveSecond!: (result: ChatModelRunResult) => void;
|
|
769
|
+
const firstTeardown = new Promise<void>((resolve) => {
|
|
770
|
+
releaseFirst = resolve;
|
|
771
|
+
});
|
|
772
|
+
const secondResult = new Promise<ChatModelRunResult>((resolve) => {
|
|
773
|
+
resolveSecond = resolve;
|
|
774
|
+
});
|
|
775
|
+
const runs: ChatModelRunOptions[] = [];
|
|
776
|
+
const thread = createThread({
|
|
777
|
+
run(options) {
|
|
778
|
+
runs.push(options);
|
|
779
|
+
if (runs.length === 1) {
|
|
780
|
+
return (async function* () {
|
|
781
|
+
yield toolCallResult("send_email");
|
|
782
|
+
await firstTeardown;
|
|
783
|
+
yield toolCallResult("stale_tool");
|
|
784
|
+
})();
|
|
785
|
+
}
|
|
786
|
+
return secondResult;
|
|
787
|
+
},
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
const firstAppend = thread.append(userMessage("send an email"));
|
|
791
|
+
await flush();
|
|
792
|
+
const messageId = thread.messages.at(-1)!.id;
|
|
793
|
+
|
|
794
|
+
thread.addToolResult({
|
|
795
|
+
messageId,
|
|
796
|
+
toolCallId: "call-send_email",
|
|
797
|
+
toolName: "send_email",
|
|
798
|
+
result: { approved: true },
|
|
799
|
+
isError: false,
|
|
800
|
+
});
|
|
801
|
+
await flush();
|
|
802
|
+
|
|
803
|
+
expect(runs).toHaveLength(2);
|
|
804
|
+
expect(runs[1]?.abortSignal.aborted).toBe(false);
|
|
805
|
+
|
|
806
|
+
releaseFirst();
|
|
807
|
+
await firstAppend;
|
|
808
|
+
|
|
809
|
+
const toolCall = thread.messages
|
|
810
|
+
.find((item) => item.id === messageId)
|
|
811
|
+
?.content.find(
|
|
812
|
+
(part) =>
|
|
813
|
+
part.type === "tool-call" && part.toolCallId === "call-send_email",
|
|
814
|
+
);
|
|
815
|
+
expect(toolCall?.result).toEqual({ approved: true });
|
|
816
|
+
expect(runs[1]?.abortSignal.aborted).toBe(false);
|
|
817
|
+
|
|
818
|
+
resolveSecond({ content: [{ type: "text", text: "replacement" }] });
|
|
819
|
+
await vi.waitFor(() => {
|
|
820
|
+
expect(
|
|
821
|
+
thread.messages.find((item) => item.id === messageId)?.status.type,
|
|
822
|
+
).toBe("complete");
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
it("ignores stale chunks after a partial tool result updates the active message", async () => {
|
|
827
|
+
let releaseTeardown!: () => void;
|
|
828
|
+
const teardown = new Promise<void>((resolve) => {
|
|
829
|
+
releaseTeardown = resolve;
|
|
830
|
+
});
|
|
831
|
+
const runs: ChatModelRunOptions[] = [];
|
|
832
|
+
const thread = createThread({
|
|
833
|
+
run(options) {
|
|
834
|
+
runs.push(options);
|
|
835
|
+
return (async function* () {
|
|
836
|
+
yield {
|
|
837
|
+
content: [
|
|
838
|
+
toolCallPart("send_email"),
|
|
839
|
+
toolCallPart("deploy", { id: "approval-1" }),
|
|
840
|
+
],
|
|
841
|
+
status: {
|
|
842
|
+
type: "requires-action",
|
|
843
|
+
reason: "tool-calls",
|
|
844
|
+
},
|
|
845
|
+
} satisfies ChatModelRunResult;
|
|
846
|
+
await teardown;
|
|
847
|
+
yield { content: [{ type: "text", text: "stale" }] };
|
|
848
|
+
})();
|
|
849
|
+
},
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
const appendPromise = thread.append(userMessage("send and deploy"));
|
|
853
|
+
await flush();
|
|
854
|
+
const messageId = thread.messages.at(-1)!.id;
|
|
855
|
+
|
|
856
|
+
thread.addToolResult({
|
|
857
|
+
messageId,
|
|
858
|
+
toolCallId: "call-send_email",
|
|
859
|
+
toolName: "send_email",
|
|
860
|
+
result: { approved: true },
|
|
861
|
+
isError: false,
|
|
862
|
+
});
|
|
863
|
+
await flush();
|
|
864
|
+
|
|
865
|
+
expect(runs).toHaveLength(1);
|
|
866
|
+
|
|
867
|
+
releaseTeardown();
|
|
868
|
+
await appendPromise;
|
|
869
|
+
|
|
870
|
+
const message = thread.messages.find((item) => item.id === messageId);
|
|
871
|
+
const sendEmail = message?.content.find(
|
|
872
|
+
(part) =>
|
|
873
|
+
part.type === "tool-call" && part.toolCallId === "call-send_email",
|
|
874
|
+
);
|
|
875
|
+
const deploy = message?.content.find(
|
|
876
|
+
(part) => part.type === "tool-call" && part.toolCallId === "call-deploy",
|
|
877
|
+
);
|
|
878
|
+
expect(sendEmail?.result).toEqual({ approved: true });
|
|
879
|
+
expect(deploy?.approval).toEqual({ id: "approval-1" });
|
|
880
|
+
expect(message?.status.type).toBe("requires-action");
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
it("ignores a superseded result after its message is removed", async () => {
|
|
884
|
+
let resolveFirst!: (result: ChatModelRunResult) => void;
|
|
885
|
+
const firstResult = new Promise<ChatModelRunResult>((resolve) => {
|
|
886
|
+
resolveFirst = resolve;
|
|
887
|
+
});
|
|
888
|
+
let runCount = 0;
|
|
889
|
+
const thread = createThread({
|
|
890
|
+
run() {
|
|
891
|
+
runCount++;
|
|
892
|
+
if (runCount === 1) return firstResult;
|
|
893
|
+
return Promise.resolve({
|
|
894
|
+
content: [{ type: "text", text: "replacement" }],
|
|
895
|
+
});
|
|
896
|
+
},
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
const firstAppend = thread.append(userMessage("first"));
|
|
900
|
+
await flush();
|
|
901
|
+
await thread.append(userMessage("second"));
|
|
902
|
+
thread.reset();
|
|
903
|
+
|
|
904
|
+
resolveFirst(toolCallResult("lookup_weather"));
|
|
905
|
+
|
|
906
|
+
await expect(firstAppend).resolves.toBeUndefined();
|
|
907
|
+
expect(thread.messages).toHaveLength(0);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
it("does not continue after cancelRun when a delayed tool call resolves", async () => {
|
|
911
|
+
let resolveFirst!: (result: ChatModelRunResult) => void;
|
|
912
|
+
const firstResult = new Promise<ChatModelRunResult>((resolve) => {
|
|
913
|
+
resolveFirst = resolve;
|
|
914
|
+
});
|
|
915
|
+
const runs: ChatModelRunOptions[] = [];
|
|
916
|
+
const thread = createThread({
|
|
917
|
+
run(options) {
|
|
918
|
+
runs.push(options);
|
|
919
|
+
if (runs.length === 1) return firstResult;
|
|
920
|
+
return Promise.resolve({
|
|
921
|
+
content: [{ type: "text", text: "unexpected continuation" }],
|
|
922
|
+
status: { type: "complete", reason: "stop" },
|
|
923
|
+
});
|
|
924
|
+
},
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
const appendPromise = thread.append(userMessage("first"));
|
|
928
|
+
await flush();
|
|
929
|
+
|
|
930
|
+
thread.cancelRun();
|
|
931
|
+
resolveFirst(toolCallResult("lookup_weather"));
|
|
932
|
+
await appendPromise;
|
|
933
|
+
|
|
934
|
+
expect(runs).toHaveLength(1);
|
|
935
|
+
expect(thread.messages.at(-1)?.status).toEqual({
|
|
936
|
+
type: "incomplete",
|
|
937
|
+
reason: "cancelled",
|
|
938
|
+
});
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
it("preserves a terminal adapter status when cancelled during teardown", async () => {
|
|
942
|
+
let releaseTeardown!: () => void;
|
|
943
|
+
const teardown = new Promise<void>((resolve) => {
|
|
944
|
+
releaseTeardown = resolve;
|
|
945
|
+
});
|
|
946
|
+
const thread = createThread({
|
|
947
|
+
async *run() {
|
|
948
|
+
yield {
|
|
949
|
+
content: [{ type: "text", text: "done" }],
|
|
950
|
+
status: { type: "complete", reason: "stop" },
|
|
951
|
+
};
|
|
952
|
+
await teardown;
|
|
953
|
+
},
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
const appendPromise = thread.append(userMessage("first"));
|
|
957
|
+
await flush();
|
|
958
|
+
|
|
959
|
+
expect(thread.messages.at(-1)?.status).toEqual({
|
|
960
|
+
type: "complete",
|
|
961
|
+
reason: "stop",
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
thread.cancelRun();
|
|
965
|
+
releaseTeardown();
|
|
966
|
+
await appendPromise;
|
|
967
|
+
|
|
968
|
+
expect(thread.messages.at(-1)?.status).toEqual({
|
|
969
|
+
type: "complete",
|
|
970
|
+
reason: "stop",
|
|
971
|
+
});
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
it("preserves an approval pause when the adapter yields after cancellation", async () => {
|
|
975
|
+
let releaseTeardown!: () => void;
|
|
976
|
+
const teardown = new Promise<void>((resolve) => {
|
|
977
|
+
releaseTeardown = resolve;
|
|
978
|
+
});
|
|
979
|
+
const thread = createThread({
|
|
980
|
+
async *run() {
|
|
981
|
+
yield toolCallResult("deploy", { id: "approval-1" });
|
|
982
|
+
await teardown;
|
|
983
|
+
yield { content: [{ type: "text", text: "late" }] };
|
|
984
|
+
},
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
const appendPromise = thread.append(userMessage("deploy"));
|
|
988
|
+
await flush();
|
|
989
|
+
|
|
990
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("requires-action");
|
|
991
|
+
|
|
992
|
+
thread.cancelRun();
|
|
993
|
+
releaseTeardown();
|
|
994
|
+
await appendPromise;
|
|
995
|
+
|
|
996
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("requires-action");
|
|
997
|
+
});
|
|
998
|
+
|
|
676
999
|
it("keeps a replacement run cancellable after the previous run settles", async () => {
|
|
677
1000
|
let releaseFirst!: () => void;
|
|
678
1001
|
let releaseSecond!: () => void;
|
|
@@ -1683,3 +2006,170 @@ describe("LocalRuntimeCore composer.canCancel", () => {
|
|
|
1683
2006
|
thread.cancelRun();
|
|
1684
2007
|
});
|
|
1685
2008
|
});
|
|
2009
|
+
|
|
2010
|
+
describe("LocalThreadRuntimeCore imported approvals", () => {
|
|
2011
|
+
const pausedOnApproval = (
|
|
2012
|
+
approval: { id: string } | undefined,
|
|
2013
|
+
extra: Record<string, unknown> = {},
|
|
2014
|
+
): ThreadMessageLike[] => [
|
|
2015
|
+
{ role: "user", content: [{ type: "text", text: "send an email" }] },
|
|
2016
|
+
{
|
|
2017
|
+
role: "assistant",
|
|
2018
|
+
content: [
|
|
2019
|
+
{
|
|
2020
|
+
type: "tool-call",
|
|
2021
|
+
toolCallId: "call-send_email",
|
|
2022
|
+
toolName: "send_email",
|
|
2023
|
+
args: {},
|
|
2024
|
+
argsText: "{}",
|
|
2025
|
+
...(approval !== undefined ? { approval } : {}),
|
|
2026
|
+
...extra,
|
|
2027
|
+
},
|
|
2028
|
+
],
|
|
2029
|
+
},
|
|
2030
|
+
];
|
|
2031
|
+
|
|
2032
|
+
const createImportedThread = (
|
|
2033
|
+
initialMessages: ThreadMessageLike[],
|
|
2034
|
+
history?: LocalRuntimeOptionsBase["adapters"]["history"],
|
|
2035
|
+
) => {
|
|
2036
|
+
const runs: ChatModelRunOptions[] = [];
|
|
2037
|
+
const core = new LocalRuntimeCore(
|
|
2038
|
+
{
|
|
2039
|
+
adapters: {
|
|
2040
|
+
chatModel: {
|
|
2041
|
+
async run(options) {
|
|
2042
|
+
runs.push(options);
|
|
2043
|
+
return { content: [{ type: "text", text: "done" }] };
|
|
2044
|
+
},
|
|
2045
|
+
},
|
|
2046
|
+
...(history !== undefined && { history }),
|
|
2047
|
+
},
|
|
2048
|
+
unstable_humanToolNames: ["send_email"],
|
|
2049
|
+
},
|
|
2050
|
+
initialMessages,
|
|
2051
|
+
);
|
|
2052
|
+
return { thread: core.threads.getMainThreadRuntimeCore(), runs };
|
|
2053
|
+
};
|
|
2054
|
+
|
|
2055
|
+
it("resumes the run after approving an imported pending approval", async () => {
|
|
2056
|
+
const { thread, runs } = createImportedThread(
|
|
2057
|
+
pausedOnApproval({ id: "a1" }),
|
|
2058
|
+
);
|
|
2059
|
+
|
|
2060
|
+
expect(thread.messages.at(-1)?.status).toMatchObject({
|
|
2061
|
+
type: "requires-action",
|
|
2062
|
+
reason: "tool-calls",
|
|
2063
|
+
});
|
|
2064
|
+
|
|
2065
|
+
thread.respondToToolApproval({ approvalId: "a1", approved: true });
|
|
2066
|
+
await flush();
|
|
2067
|
+
|
|
2068
|
+
expect(runs).toHaveLength(1);
|
|
2069
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
2070
|
+
});
|
|
2071
|
+
|
|
2072
|
+
it("keeps the interrupt reason for an imported interrupted tool call", () => {
|
|
2073
|
+
const { thread } = createImportedThread(
|
|
2074
|
+
pausedOnApproval(undefined, {
|
|
2075
|
+
interrupt: { type: "human", payload: { question: "which one?" } },
|
|
2076
|
+
}),
|
|
2077
|
+
);
|
|
2078
|
+
|
|
2079
|
+
expect(thread.messages.at(-1)?.status).toMatchObject({
|
|
2080
|
+
type: "requires-action",
|
|
2081
|
+
reason: "interrupt",
|
|
2082
|
+
});
|
|
2083
|
+
});
|
|
2084
|
+
|
|
2085
|
+
it("resumes the run after approving an approval restored by the history adapter", async () => {
|
|
2086
|
+
const { thread, runs } = createImportedThread([], {
|
|
2087
|
+
async load() {
|
|
2088
|
+
return ExportedMessageRepository.fromArray(
|
|
2089
|
+
pausedOnApproval({ id: "a1" }),
|
|
2090
|
+
);
|
|
2091
|
+
},
|
|
2092
|
+
async append() {},
|
|
2093
|
+
async update() {},
|
|
2094
|
+
});
|
|
2095
|
+
|
|
2096
|
+
thread.__internal_load();
|
|
2097
|
+
await flush();
|
|
2098
|
+
|
|
2099
|
+
expect(thread.messages.at(-1)?.status).toMatchObject({
|
|
2100
|
+
type: "requires-action",
|
|
2101
|
+
reason: "tool-calls",
|
|
2102
|
+
});
|
|
2103
|
+
|
|
2104
|
+
thread.respondToToolApproval({ approvalId: "a1", approved: true });
|
|
2105
|
+
await flush();
|
|
2106
|
+
|
|
2107
|
+
expect(runs).toHaveLength(1);
|
|
2108
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
2109
|
+
});
|
|
2110
|
+
|
|
2111
|
+
it("normalizes a serialized approval pause that lost its auto-status marker", async () => {
|
|
2112
|
+
const { thread, runs } = createImportedThread([]);
|
|
2113
|
+
thread.import(
|
|
2114
|
+
JSON.parse(
|
|
2115
|
+
JSON.stringify(
|
|
2116
|
+
ExportedMessageRepository.fromArray(pausedOnApproval({ id: "a1" })),
|
|
2117
|
+
),
|
|
2118
|
+
),
|
|
2119
|
+
);
|
|
2120
|
+
|
|
2121
|
+
expect(thread.messages.at(-1)?.status).toMatchObject({
|
|
2122
|
+
type: "requires-action",
|
|
2123
|
+
reason: "tool-calls",
|
|
2124
|
+
});
|
|
2125
|
+
|
|
2126
|
+
thread.respondToToolApproval({ approvalId: "a1", approved: true });
|
|
2127
|
+
await flush();
|
|
2128
|
+
|
|
2129
|
+
expect(runs).toHaveLength(1);
|
|
2130
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
2131
|
+
});
|
|
2132
|
+
|
|
2133
|
+
it("completes an imported interrupt that has nothing left to act on", () => {
|
|
2134
|
+
const { thread } = createImportedThread([
|
|
2135
|
+
{ role: "user", content: [{ type: "text", text: "send an email" }] },
|
|
2136
|
+
{
|
|
2137
|
+
role: "assistant",
|
|
2138
|
+
status: { type: "requires-action", reason: "interrupt" },
|
|
2139
|
+
content: [
|
|
2140
|
+
{
|
|
2141
|
+
type: "tool-call",
|
|
2142
|
+
toolCallId: "call-send_email",
|
|
2143
|
+
toolName: "send_email",
|
|
2144
|
+
args: {},
|
|
2145
|
+
argsText: "{}",
|
|
2146
|
+
result: { sent: true },
|
|
2147
|
+
},
|
|
2148
|
+
],
|
|
2149
|
+
} as ThreadMessageLike,
|
|
2150
|
+
]);
|
|
2151
|
+
|
|
2152
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
2153
|
+
});
|
|
2154
|
+
|
|
2155
|
+
it("resumes the run after adding a result to an imported resultless tool call", async () => {
|
|
2156
|
+
const { thread, runs } = createImportedThread(pausedOnApproval(undefined));
|
|
2157
|
+
|
|
2158
|
+
expect(thread.messages.at(-1)?.status).toMatchObject({
|
|
2159
|
+
type: "requires-action",
|
|
2160
|
+
reason: "tool-calls",
|
|
2161
|
+
});
|
|
2162
|
+
|
|
2163
|
+
thread.addToolResult({
|
|
2164
|
+
messageId: thread.messages.at(-1)!.id,
|
|
2165
|
+
toolCallId: "call-send_email",
|
|
2166
|
+
toolName: "send_email",
|
|
2167
|
+
result: { sent: true },
|
|
2168
|
+
isError: false,
|
|
2169
|
+
});
|
|
2170
|
+
await flush();
|
|
2171
|
+
|
|
2172
|
+
expect(runs).toHaveLength(1);
|
|
2173
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
2174
|
+
});
|
|
2175
|
+
});
|
|
@@ -5,6 +5,8 @@ import type {
|
|
|
5
5
|
ChatModelRunResult,
|
|
6
6
|
} from "../../runtime/utils/chat-model-adapter";
|
|
7
7
|
import { shouldContinue } from "./should-continue";
|
|
8
|
+
import { getAutoStatus } from "../../runtime/utils/auto-status";
|
|
9
|
+
import type { ExportedMessageRepository } from "../../runtime/utils/message-repository";
|
|
8
10
|
import type { LocalRuntimeOptionsBase } from "./local-runtime-options";
|
|
9
11
|
import { consumeSuggestionResult } from "../../adapters/suggestion";
|
|
10
12
|
import type {
|
|
@@ -21,7 +23,7 @@ import type {
|
|
|
21
23
|
AppendMessage,
|
|
22
24
|
ThreadAssistantMessage,
|
|
23
25
|
} from "../../types/message";
|
|
24
|
-
import type { RunConfig } from "../../types/message";
|
|
26
|
+
import type { RunConfig, ThreadMessage } from "../../types/message";
|
|
25
27
|
import { MessageNotSentError, toAssistantError } from "../../types/error";
|
|
26
28
|
import type { ModelContextProvider } from "../../model-context/types";
|
|
27
29
|
import {
|
|
@@ -32,7 +34,7 @@ import type { QueuePlacement } from "../../runtime/queue/external-thread-queue-a
|
|
|
32
34
|
import {
|
|
33
35
|
EMPTY_QUEUE_ITEMS,
|
|
34
36
|
type QueueItemState,
|
|
35
|
-
} from "../../
|
|
37
|
+
} from "../../runtime/queue/queue-item";
|
|
36
38
|
import {
|
|
37
39
|
captureThreadRuntimeGeneration,
|
|
38
40
|
invalidateThreadRuntime,
|
|
@@ -49,6 +51,45 @@ class AbortError extends Error {
|
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
// `shouldContinue` resumes only on `tool-calls` and `resumeToolCall` throws, so
|
|
55
|
+
// an imported `interrupt` with no interrupt payload would be stranded here.
|
|
56
|
+
// Provenance cannot gate it: a repository that went through JSON has no marker.
|
|
57
|
+
// The replacement stays content-derived, so a message with nothing resultless
|
|
58
|
+
// left to act on lands on `complete` rather than an unactionable pause.
|
|
59
|
+
const withLocalPauseReason = (message: ThreadMessage): ThreadMessage => {
|
|
60
|
+
if (
|
|
61
|
+
message.role !== "assistant" ||
|
|
62
|
+
message.status.type !== "requires-action" ||
|
|
63
|
+
message.status.reason !== "interrupt" ||
|
|
64
|
+
message.content.some(
|
|
65
|
+
(c) =>
|
|
66
|
+
c.type === "tool-call" && c.result === undefined && c.interrupt != null,
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
return message;
|
|
70
|
+
return {
|
|
71
|
+
...message,
|
|
72
|
+
status: getAutoStatus(
|
|
73
|
+
false,
|
|
74
|
+
false,
|
|
75
|
+
false,
|
|
76
|
+
message.content.some(
|
|
77
|
+
(c) => c.type === "tool-call" && c.result === undefined,
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const withLocalPauseReasons = (
|
|
84
|
+
data: ExportedMessageRepository,
|
|
85
|
+
): ExportedMessageRepository => ({
|
|
86
|
+
...data,
|
|
87
|
+
messages: data.messages.map((item) => ({
|
|
88
|
+
...item,
|
|
89
|
+
message: withLocalPauseReason(item.message),
|
|
90
|
+
})),
|
|
91
|
+
});
|
|
92
|
+
|
|
52
93
|
export class LocalThreadRuntimeCore
|
|
53
94
|
extends BaseThreadRuntimeCore
|
|
54
95
|
implements ThreadRuntimeCore
|
|
@@ -272,7 +313,7 @@ export class LocalThreadRuntimeCore
|
|
|
272
313
|
this._loadPromise = promise
|
|
273
314
|
.then((repo) => {
|
|
274
315
|
if (!repo) return;
|
|
275
|
-
this.repository.import(repo);
|
|
316
|
+
this.repository.import(withLocalPauseReasons(repo));
|
|
276
317
|
if (repo.messages.length > 0) {
|
|
277
318
|
this.ensureInitialized();
|
|
278
319
|
}
|
|
@@ -434,6 +475,10 @@ export class LocalThreadRuntimeCore
|
|
|
434
475
|
throw new Error("Runtime does not support exporting external states.");
|
|
435
476
|
}
|
|
436
477
|
|
|
478
|
+
public override import(data: ExportedMessageRepository) {
|
|
479
|
+
super.import(withLocalPauseReasons(data));
|
|
480
|
+
}
|
|
481
|
+
|
|
437
482
|
public importExternalState(): void {
|
|
438
483
|
throw new Error("Runtime does not support importing external states.");
|
|
439
484
|
}
|
|
@@ -501,9 +546,11 @@ export class LocalThreadRuntimeCore
|
|
|
501
546
|
message,
|
|
502
547
|
runConfig,
|
|
503
548
|
alreadyPersisted,
|
|
549
|
+
run,
|
|
504
550
|
runCallback,
|
|
505
551
|
);
|
|
506
552
|
runCallback = undefined;
|
|
553
|
+
if (this._activeRun !== run) break;
|
|
507
554
|
} while (shouldContinue(message, this._options.unstable_humanToolNames));
|
|
508
555
|
} finally {
|
|
509
556
|
this._notifyEventSubscribers("runEnd", {});
|
|
@@ -548,6 +595,7 @@ export class LocalThreadRuntimeCore
|
|
|
548
595
|
message: ThreadAssistantMessage,
|
|
549
596
|
runConfig: RunConfig | undefined,
|
|
550
597
|
alreadyPersisted: boolean,
|
|
598
|
+
run: { cancelled: boolean },
|
|
551
599
|
runCallback?: ChatModelAdapter["run"],
|
|
552
600
|
) {
|
|
553
601
|
const messages = parentId ? this.repository.getMessages(parentId) : [];
|
|
@@ -562,7 +610,23 @@ export class LocalThreadRuntimeCore
|
|
|
562
610
|
const initialData = message.metadata?.unstable_data;
|
|
563
611
|
const initialSteps = message.metadata?.steps;
|
|
564
612
|
const initialCustom = message.metadata?.custom;
|
|
613
|
+
let hasStoredMessage = true;
|
|
614
|
+
try {
|
|
615
|
+
this.repository.getMessage(message.id);
|
|
616
|
+
} catch {
|
|
617
|
+
hasStoredMessage = false;
|
|
618
|
+
}
|
|
619
|
+
// Other writers replace the stored message object, so identity distinguishes this run from a newer owner.
|
|
620
|
+
const ownsMessage = () => {
|
|
621
|
+
if (!hasStoredMessage) return this._activeRun === run;
|
|
622
|
+
try {
|
|
623
|
+
return this.repository.getMessage(message.id).message === message;
|
|
624
|
+
} catch {
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
};
|
|
565
628
|
const updateMessage = (m: Partial<ChatModelRunResult>) => {
|
|
629
|
+
if (!ownsMessage()) return;
|
|
566
630
|
const newSteps = m.metadata?.steps;
|
|
567
631
|
const steps = newSteps
|
|
568
632
|
? [...(initialSteps ?? []), ...newSteps]
|
|
@@ -609,6 +673,7 @@ export class LocalThreadRuntimeCore
|
|
|
609
673
|
: undefined),
|
|
610
674
|
};
|
|
611
675
|
this.repository.addOrUpdateMessage(parentId, message);
|
|
676
|
+
hasStoredMessage = true;
|
|
612
677
|
this._notifySubscribers();
|
|
613
678
|
};
|
|
614
679
|
|
|
@@ -647,6 +712,12 @@ export class LocalThreadRuntimeCore
|
|
|
647
712
|
this.adapters.chatModel.run.bind(this.adapters.chatModel);
|
|
648
713
|
|
|
649
714
|
const abortSignal = abortController.signal;
|
|
715
|
+
const shouldCancelMessage = () =>
|
|
716
|
+
abortSignal.aborted &&
|
|
717
|
+
(message.status.type === "running" ||
|
|
718
|
+
(message.status.type === "requires-action" &&
|
|
719
|
+
(this._activeRun !== run ||
|
|
720
|
+
shouldContinue(message, this._options.unstable_humanToolNames))));
|
|
650
721
|
const threadId = this._getThreadId?.();
|
|
651
722
|
const promiseOrGenerator = runCallback({
|
|
652
723
|
messages,
|
|
@@ -665,9 +736,11 @@ export class LocalThreadRuntimeCore
|
|
|
665
736
|
if (Symbol.asyncIterator in promiseOrGenerator) {
|
|
666
737
|
for await (const r of promiseOrGenerator) {
|
|
667
738
|
if (abortSignal.aborted) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
739
|
+
if (shouldCancelMessage()) {
|
|
740
|
+
updateMessage({
|
|
741
|
+
status: { type: "incomplete", reason: "cancelled" },
|
|
742
|
+
});
|
|
743
|
+
}
|
|
671
744
|
break;
|
|
672
745
|
}
|
|
673
746
|
|
|
@@ -677,11 +750,13 @@ export class LocalThreadRuntimeCore
|
|
|
677
750
|
updateMessage(await promiseOrGenerator);
|
|
678
751
|
}
|
|
679
752
|
|
|
680
|
-
if (
|
|
753
|
+
if (shouldCancelMessage()) {
|
|
754
|
+
updateMessage({
|
|
755
|
+
status: { type: "incomplete", reason: "cancelled" },
|
|
756
|
+
});
|
|
757
|
+
} else if (message.status.type === "running") {
|
|
681
758
|
updateMessage({
|
|
682
|
-
status:
|
|
683
|
-
? { type: "incomplete", reason: "cancelled" }
|
|
684
|
-
: { type: "complete", reason: "unknown" },
|
|
759
|
+
status: { type: "complete", reason: "unknown" },
|
|
685
760
|
});
|
|
686
761
|
}
|
|
687
762
|
} catch (e) {
|
|
@@ -724,7 +799,7 @@ export class LocalThreadRuntimeCore
|
|
|
724
799
|
|
|
725
800
|
// Pauses are written only for adapters that can rewrite the entry later;
|
|
726
801
|
// an append-only adapter would strand a half-finished run in history.
|
|
727
|
-
if (isTerminal || (isPausing && history?.update)) {
|
|
802
|
+
if (ownsMessage() && (isTerminal || (isPausing && history?.update))) {
|
|
728
803
|
const write =
|
|
729
804
|
alreadyPersisted && history?.update
|
|
730
805
|
? history.update.bind(history)
|