@assistant-ui/core 0.3.17 → 0.3.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/attachment.d.ts.map +1 -1
- package/dist/adapters/attachment.js +5 -4
- package/dist/adapters/attachment.js.map +1 -1
- package/dist/adapters/speech.d.ts.map +1 -1
- package/dist/adapters/speech.js +13 -3
- package/dist/adapters/speech.js.map +1 -1
- package/dist/adapters/thread-history.d.ts +4 -0
- package/dist/adapters/thread-history.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +4 -3
- package/dist/internal.js +3 -2
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +35 -14
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/frame/provider.d.ts +2 -0
- package/dist/model-context/frame/provider.d.ts.map +1 -1
- package/dist/model-context/frame/provider.js +30 -7
- package/dist/model-context/frame/provider.js.map +1 -1
- package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
- package/dist/model-context/interactable-composer-metadata.js +27 -18
- package/dist/model-context/interactable-composer-metadata.js.map +1 -1
- package/dist/model-context/registry.d.ts.map +1 -1
- package/dist/model-context/registry.js +3 -5
- package/dist/model-context/registry.js.map +1 -1
- package/dist/model-context/types.d.ts.map +1 -1
- package/dist/model-context/types.js +5 -4
- package/dist/model-context/types.js.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.js +45 -7
- package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
- package/dist/react/client/DataRenderers.d.ts.map +1 -1
- package/dist/react/client/DataRenderers.js +20 -15
- package/dist/react/client/DataRenderers.js.map +1 -1
- package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
- package/dist/react/client/InMemoryThreadList.js +62 -56
- 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 +35 -32
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/RemoteThreadList.d.ts +1 -0
- package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
- package/dist/react/client/RemoteThreadList.js +154 -143
- package/dist/react/client/RemoteThreadList.js.map +1 -1
- package/dist/react/client/Tools.d.ts.map +1 -1
- package/dist/react/client/Tools.js +18 -16
- package/dist/react/client/Tools.js.map +1 -1
- package/dist/react/client/interactable-model-context.d.ts.map +1 -1
- package/dist/react/client/interactable-model-context.js +3 -2
- package/dist/react/client/interactable-model-context.js.map +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +2 -2
- package/dist/react/interactables-legacy/Interactables.js +242 -121
- package/dist/react/interactables-legacy/Interactables.js.map +1 -1
- package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -1
- package/dist/react/interactables-legacy/useAssistantInteractable.js +3 -0
- package/dist/react/interactables-legacy/useAssistantInteractable.js.map +1 -1
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
- package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
- package/dist/react/model-context/makeAssistantTool.js +1 -1
- package/dist/react/model-context/makeAssistantTool.js.map +1 -1
- package/dist/react/model-context/useAssistantTool.d.ts +2 -2
- package/dist/react/model-context/useAssistantTool.js +1 -1
- package/dist/react/model-context/useAssistantTool.js.map +1 -1
- package/dist/react/model-context/useInteractable.d.ts.map +1 -1
- package/dist/react/model-context/useInteractable.js +4 -1
- package/dist/react/model-context/useInteractable.js.map +1 -1
- package/dist/react/model-context/useInteractableVersions.js +1 -1
- package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
- package/dist/react/model-context/useToolArgsStatus.js +2 -1
- package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.d.ts +11 -2
- package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.js +3 -2
- package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
- package/dist/react/primitives/part/PartMessages.d.ts +1 -0
- package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
- package/dist/react/primitives/part/PartMessages.js +1 -0
- package/dist/react/primitives/part/PartMessages.js.map +1 -1
- package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
- package/dist/react/primitives/thread/ThreadMessages.js +6 -5
- package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +16 -8
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +5 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +105 -104
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -0
- package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.js +13 -11
- package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +5 -24
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +354 -226
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +1 -0
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +22 -2
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts +40 -15
- package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.js +62 -15
- package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +2 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +6 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/sdkIdentity.d.ts +6 -0
- package/dist/react/runtimes/cloud/sdkIdentity.d.ts.map +1 -0
- package/dist/react/runtimes/cloud/sdkIdentity.js +9 -0
- package/dist/react/runtimes/cloud/sdkIdentity.js.map +1 -0
- package/dist/react/runtimes/external-message-converter.d.ts +12 -2
- package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.js +26 -61
- package/dist/react/runtimes/external-message-converter.js.map +1 -1
- package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
- package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.js +64 -24
- package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
- package/dist/react/utils/groupParts.d.ts +6 -1
- package/dist/react/utils/groupParts.d.ts.map +1 -1
- package/dist/react/utils/groupParts.js +6 -0
- package/dist/react/utils/groupParts.js.map +1 -1
- package/dist/react/utils/useJSONEqual.d.ts +3 -1
- package/dist/react/utils/useJSONEqual.d.ts.map +1 -1
- package/dist/react/utils/useJSONEqual.js +6 -1
- package/dist/react/utils/useJSONEqual.js.map +1 -1
- package/dist/react/utils/useJSONSchemaDependency.d.ts +7 -0
- package/dist/react/utils/useJSONSchemaDependency.d.ts.map +1 -0
- package/dist/react/utils/useJSONSchemaDependency.js +20 -0
- package/dist/react/utils/useJSONSchemaDependency.js.map +1 -0
- package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
- package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.js +2 -2
- package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
- package/dist/runtime/api/thread-list-runtime.d.ts +3 -0
- package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-runtime.js +18 -14
- package/dist/runtime/api/thread-list-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +4 -0
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-runtime.js +3 -6
- package/dist/runtime/api/thread-runtime.js.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -0
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +62 -22
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -0
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +161 -69
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
- package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +4 -1
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.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/queue/message-queue.d.ts +5 -0
- package/dist/runtime/queue/message-queue.d.ts.map +1 -1
- package/dist/runtime/queue/message-queue.js +61 -6
- package/dist/runtime/queue/message-queue.js.map +1 -1
- package/dist/runtime/utils/auto-status.d.ts +6 -2
- package/dist/runtime/utils/auto-status.d.ts.map +1 -1
- package/dist/runtime/utils/auto-status.js +12 -4
- package/dist/runtime/utils/auto-status.js.map +1 -1
- package/dist/runtime/utils/external-message-conversion.d.ts +16 -4
- package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
- package/dist/runtime/utils/external-message-conversion.js +49 -13
- package/dist/runtime/utils/external-message-conversion.js.map +1 -1
- package/dist/runtime/utils/message-repository.d.ts +1 -0
- package/dist/runtime/utils/message-repository.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.js +18 -9
- package/dist/runtime/utils/message-repository.js.map +1 -1
- 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 +5 -3
- package/dist/runtime/utils/thread-message-like.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 +21 -27
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js +8 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/internal.d.ts +3 -3
- package/dist/runtimes/internal.js +2 -2
- package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +4 -1
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -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 +130 -42
- package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
- package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
- package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
- package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
- package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
- package/dist/runtimes/remote-thread-list/title.d.ts +3 -1
- package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/title.js +9 -1
- package/dist/runtimes/remote-thread-list/title.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +10 -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 -1
- 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 +260 -203
- package/dist/store/clients/external-thread.js.map +1 -1
- package/dist/store/clients/model-context-client.d.ts.map +1 -1
- package/dist/store/clients/model-context-client.js +2 -1
- package/dist/store/clients/model-context-client.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 -0
- 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 +5 -4
- package/dist/store/clients/suggestions.js.map +1 -1
- package/dist/store/clients/thread-tasks.d.ts +13 -0
- package/dist/store/clients/thread-tasks.d.ts.map +1 -0
- package/dist/store/clients/thread-tasks.js +69 -0
- package/dist/store/clients/thread-tasks.js.map +1 -0
- package/dist/store/index.d.ts +3 -2
- package/dist/store/index.js +3 -3
- package/dist/store/primitive-predicates.d.ts.map +1 -1
- package/dist/store/primitive-predicates.js +4 -4
- package/dist/store/primitive-predicates.js.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
- package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.js +97 -61
- package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
- package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/message-runtime-client.js +221 -144
- package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
- 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 +54 -52
- package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.js +246 -126
- package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
- package/dist/store/scope-registration.d.ts +2 -0
- package/dist/store/scope-registration.d.ts.map +1 -1
- package/dist/store/scopes/composer.d.ts +10 -2
- package/dist/store/scopes/composer.d.ts.map +1 -1
- package/dist/store/scopes/message.d.ts +30 -1
- package/dist/store/scopes/message.d.ts.map +1 -1
- package/dist/store/scopes/task.d.ts +36 -0
- package/dist/store/scopes/task.d.ts.map +1 -0
- package/dist/store/scopes/task.js +0 -0
- package/dist/store/scopes/thread-list-item.d.ts +2 -2
- package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
- package/dist/store/scopes/thread.d.ts +28 -0
- package/dist/store/scopes/thread.d.ts.map +1 -1
- package/dist/store/scopes/threads.d.ts +1 -0
- 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 -15
- package/dist/subscribable/subscribable.js.map +1 -1
- package/dist/types/attachment.d.ts +2 -1
- package/dist/types/attachment.d.ts.map +1 -1
- package/dist/types/attachment.js +2 -1
- package/dist/types/attachment.js.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/message.d.ts +8 -1
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js.map +1 -1
- package/dist/utils/abortable-iterable.d.ts +19 -0
- package/dist/utils/abortable-iterable.d.ts.map +1 -0
- package/dist/utils/abortable-iterable.js +88 -0
- package/dist/utils/abortable-iterable.js.map +1 -0
- package/dist/utils/composite-context-provider.d.ts.map +1 -1
- package/dist/utils/composite-context-provider.js +20 -2
- package/dist/utils/composite-context-provider.js.map +1 -1
- package/dist/utils/getGroupStatus.d.ts +18 -2
- package/dist/utils/getGroupStatus.d.ts.map +1 -1
- package/dist/utils/getGroupStatus.js +32 -7
- package/dist/utils/getGroupStatus.js.map +1 -1
- package/dist/utils/record.d.ts +11 -0
- package/dist/utils/record.d.ts.map +1 -0
- package/dist/utils/record.js +12 -0
- package/dist/utils/record.js.map +1 -0
- package/dist/utils/wire-media.d.ts.map +1 -1
- package/dist/utils/wire-media.js +2 -2
- package/dist/utils/wire-media.js.map +1 -1
- package/package.json +11 -11
- package/src/adapters/attachment.test.ts +50 -1
- package/src/adapters/attachment.ts +7 -4
- package/src/adapters/speech.test.ts +236 -0
- package/src/adapters/speech.ts +17 -10
- package/src/adapters/thread-history.ts +3 -0
- package/src/index.ts +2 -0
- package/src/internal.ts +4 -0
- package/src/model-context/frame/host.test.ts +137 -1
- package/src/model-context/frame/host.ts +49 -16
- package/src/model-context/frame/provider.test.ts +71 -0
- package/src/model-context/frame/provider.ts +37 -7
- package/src/model-context/interactable-composer-metadata.test.ts +58 -0
- package/src/model-context/interactable-composer-metadata.ts +19 -13
- package/src/model-context/registry.test.ts +12 -0
- package/src/model-context/registry.ts +3 -2
- package/src/model-context/types.test.ts +25 -0
- package/src/model-context/types.ts +9 -4
- package/src/react/RuntimeAdapter.test.ts +34 -0
- package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +244 -0
- package/src/react/adapters/LocalStorageThreadListAdapter.tsx +104 -7
- package/src/react/client/DataRenderers.test.tsx +45 -0
- package/src/react/client/DataRenderers.ts +13 -9
- package/src/react/client/InMemoryThreadList.ts +1 -0
- package/src/react/client/Interactables.test.ts +72 -1
- package/src/react/client/Interactables.ts +22 -14
- package/src/react/client/RemoteThreadList.background.test.ts +148 -6
- package/src/react/client/RemoteThreadList.concurrent.test.tsx +42 -1
- package/src/react/client/RemoteThreadList.test.ts +386 -4
- package/src/react/client/RemoteThreadList.ts +181 -112
- package/src/react/client/Tools.ts +22 -11
- package/src/react/client/interactable-model-context.test.ts +44 -0
- package/src/react/client/interactable-model-context.ts +3 -2
- package/src/react/index.ts +5 -1
- package/src/react/interactables-legacy/Interactables.ts +16 -16
- package/src/react/interactables-legacy/useAssistantInteractable.test.tsx +132 -0
- package/src/react/interactables-legacy/useAssistantInteractable.ts +10 -1
- package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
- package/src/react/model-context/makeAssistantTool.ts +2 -2
- package/src/react/model-context/useAssistantTool.ts +2 -2
- package/src/react/model-context/useInteractable.test.tsx +177 -0
- package/src/react/model-context/useInteractable.ts +3 -0
- package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
- package/src/react/model-context/useToolArgsStatus.ts +2 -1
- package/src/react/primitive-hooks/useActionBarCopy.test.ts +0 -1
- package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +0 -1
- package/src/react/primitives/message/MessageGroupedParts.test.tsx +96 -0
- package/src/react/primitives/message/MessageGroupedParts.tsx +15 -4
- package/src/react/primitives/part/PartMessages.test.tsx +104 -1
- package/src/react/primitives/part/PartMessages.tsx +1 -0
- package/src/react/primitives/thread/ThreadMessages.test.tsx +142 -0
- package/src/react/primitives/thread/ThreadMessages.tsx +8 -5
- package/src/react/providers/ReadonlyThreadProvider.test.tsx +56 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +33 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +28 -5
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +105 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +143 -112
- package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
- package/src/react/runtimes/RemoteThreadResource.ts +19 -12
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +20 -14
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +703 -6
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +492 -402
- package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +77 -0
- package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +27 -1
- package/src/react/runtimes/cloud/auiV0.ts +160 -25
- package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +36 -0
- package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +8 -1
- package/src/react/runtimes/cloud/sdkIdentity.ts +9 -0
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +18 -4
- package/src/react/runtimes/external-message-converter.test.ts +136 -1
- package/src/react/runtimes/external-message-converter.ts +39 -93
- package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
- package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
- package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
- package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
- package/src/react/runtimes/useRemoteThreadListRuntime.ts +34 -1
- package/src/react/utils/groupParts.ts +12 -1
- package/src/react/utils/useJSONEqual.ts +9 -0
- package/src/react/utils/useJSONSchemaDependency.ts +22 -0
- package/src/runtime/api/message-runtime.test.ts +75 -0
- package/src/runtime/api/thread-list-item-runtime.ts +10 -3
- package/src/runtime/api/thread-list-runtime.ts +21 -11
- package/src/runtime/api/thread-runtime.test.ts +136 -0
- package/src/runtime/api/thread-runtime.ts +10 -5
- package/src/runtime/base/base-composer-runtime-core.ts +105 -24
- package/src/runtime/base/base-thread-runtime-core.test.ts +1063 -2
- package/src/runtime/base/base-thread-runtime-core.ts +228 -87
- package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
- package/src/runtime/interfaces/thread-list-runtime-core.ts +5 -1
- package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
- package/src/runtime/queue/message-queue.ts +68 -6
- package/src/runtime/utils/auto-status.test.ts +112 -1
- package/src/runtime/utils/auto-status.ts +31 -5
- package/src/runtime/utils/external-message-conversion.test.ts +202 -0
- package/src/runtime/utils/external-message-conversion.ts +123 -32
- package/src/runtime/utils/message-repository.test.ts +49 -0
- package/src/runtime/utils/message-repository.ts +32 -20
- package/src/runtime/utils/thread-message-like.test.ts +52 -0
- package/src/runtime/utils/thread-message-like.ts +5 -1
- package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +18 -13
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +31 -1
- package/src/runtimes/internal.ts +1 -0
- package/src/runtimes/local/local-thread-runtime-core.test.ts +21 -0
- package/src/runtimes/local/local-thread-runtime-core.ts +11 -1
- package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +128 -2
- package/src/runtimes/remote-thread-list/remote-thread-state.ts +182 -42
- package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
- package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
- package/src/runtimes/remote-thread-list/title.test.ts +62 -0
- package/src/runtimes/remote-thread-list/title.ts +13 -0
- package/src/runtimes/remote-thread-list/types.ts +10 -0
- package/src/runtimes/tool-invocations/EDGE_CASES.md +2 -6
- package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +133 -0
- package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +10 -6
- package/src/store/clients/external-thread.ts +67 -4
- package/src/store/clients/model-context-client.ts +5 -1
- package/src/store/clients/single-thread-list.ts +1 -0
- package/src/store/clients/suggestions.test.ts +40 -0
- package/src/store/clients/suggestions.ts +13 -10
- package/src/store/clients/thread-tasks.test.ts +274 -0
- package/src/store/clients/thread-tasks.ts +138 -0
- package/src/store/index.ts +8 -0
- package/src/store/primitive-predicates.test.ts +48 -5
- package/src/store/primitive-predicates.ts +7 -2
- package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
- package/src/store/runtime-clients/message-runtime-client.ts +54 -6
- package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
- package/src/store/runtime-clients/thread-list-runtime-client.ts +1 -0
- package/src/store/runtime-clients/thread-runtime-client.ts +89 -11
- package/src/store/scope-registration.ts +2 -0
- package/src/store/scopes/composer.ts +16 -4
- package/src/store/scopes/message.ts +14 -0
- package/src/store/scopes/task.ts +34 -0
- package/src/store/scopes/thread-list-item.ts +5 -2
- package/src/store/scopes/thread.ts +20 -0
- package/src/store/scopes/threads.ts +1 -0
- package/src/store/thread-tasks.test.tsx +209 -0
- package/src/subscribable/subscribable.test.ts +156 -1
- package/src/subscribable/subscribable.ts +25 -15
- package/src/tests/MessageRepository.test.ts +117 -5
- package/src/tests/OptimisticState-list-race.test.ts +1 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +47 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
- package/src/tests/attachment-adapters.test.ts +14 -0
- package/src/tests/auiV0Encode.test.ts +300 -0
- package/src/tests/base-composer-runtime-core-send.test.ts +51 -0
- package/src/tests/base-composer-runtime-core.test.ts +353 -2
- package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +178 -18
- package/src/tests/external-thread-attachments.test.tsx +208 -0
- package/src/tests/external-thread-parity.test.tsx +57 -0
- package/src/tests/groupParts.test.ts +71 -0
- package/src/tests/message-queue.test.ts +253 -0
- package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
- package/src/tests/remote-thread-list-reactive-threadId.test.ts +51 -0
- package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
- package/src/tests/thread-switch-events.test.tsx +78 -0
- package/src/tests/tools-scope-migration.test.tsx +28 -0
- package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +43 -0
- package/src/types/attachment.ts +4 -0
- package/src/types/index.ts +1 -0
- package/src/types/message.ts +8 -0
- package/src/utils/abortable-iterable.test.ts +328 -0
- package/src/utils/abortable-iterable.ts +104 -0
- package/src/utils/composite-context-provider.test.ts +25 -0
- package/src/utils/composite-context-provider.ts +23 -2
- package/src/utils/getGroupStatus.test.ts +70 -4
- package/src/utils/getGroupStatus.ts +35 -12
- package/src/utils/record.test.ts +48 -0
- package/src/utils/record.ts +11 -0
- package/src/utils/wire-media.test.ts +10 -0
- package/src/utils/wire-media.ts +6 -2
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { abortableIterable, openAbortableIterable } from "./abortable-iterable";
|
|
3
|
+
|
|
4
|
+
const deferred = <T>() => {
|
|
5
|
+
let resolve!: (value: T) => void;
|
|
6
|
+
const promise = new Promise<T>((r) => {
|
|
7
|
+
resolve = r;
|
|
8
|
+
});
|
|
9
|
+
return { promise, resolve };
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const collect = async <T>(iterable: AsyncIterable<T>) => {
|
|
13
|
+
const seen: T[] = [];
|
|
14
|
+
for await (const value of iterable) seen.push(value);
|
|
15
|
+
return seen;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
describe("abortableIterable", () => {
|
|
19
|
+
it.each([false, true])(
|
|
20
|
+
"releases the opening abort listener on rejection=%s",
|
|
21
|
+
async (fails) => {
|
|
22
|
+
const controller = new AbortController();
|
|
23
|
+
const added = vi.spyOn(controller.signal, "addEventListener");
|
|
24
|
+
const removed = vi.spyOn(controller.signal, "removeEventListener");
|
|
25
|
+
const source: AsyncIterable<number> = {
|
|
26
|
+
async *[Symbol.asyncIterator]() {
|
|
27
|
+
yield 1;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const opening = openAbortableIterable(
|
|
31
|
+
fails ? Promise.reject(new Error("opening failed")) : source,
|
|
32
|
+
controller.signal,
|
|
33
|
+
);
|
|
34
|
+
if (fails) await expect(opening).rejects.toThrow("opening failed");
|
|
35
|
+
else await expect(opening).resolves.toBe(source);
|
|
36
|
+
expect(added).toHaveBeenCalledTimes(1);
|
|
37
|
+
expect(removed).toHaveBeenCalledWith("abort", added.mock.calls[0]![1]);
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
it("finalizes once when an aborted read later rejects", async () => {
|
|
42
|
+
let rejectRead!: (error: unknown) => void;
|
|
43
|
+
const read = new Promise<IteratorResult<number>>((_, reject) => {
|
|
44
|
+
rejectRead = reject;
|
|
45
|
+
});
|
|
46
|
+
const finalize = vi.fn(async () => ({
|
|
47
|
+
done: true as const,
|
|
48
|
+
value: undefined,
|
|
49
|
+
}));
|
|
50
|
+
const source: AsyncIterable<number> = {
|
|
51
|
+
[Symbol.asyncIterator]: () => ({ next: () => read, return: finalize }),
|
|
52
|
+
};
|
|
53
|
+
const controller = new AbortController();
|
|
54
|
+
const iterator = abortableIterable(source, controller.signal)[
|
|
55
|
+
Symbol.asyncIterator
|
|
56
|
+
]();
|
|
57
|
+
const pending = iterator.next();
|
|
58
|
+
controller.abort();
|
|
59
|
+
await expect(pending).resolves.toEqual({ done: true, value: undefined });
|
|
60
|
+
expect(finalize).toHaveBeenCalledTimes(1);
|
|
61
|
+
rejectRead(new Error("late failure"));
|
|
62
|
+
await read.catch(() => {});
|
|
63
|
+
await iterator.return?.();
|
|
64
|
+
expect(finalize).toHaveBeenCalledTimes(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("releases the listener and finalizes after a synchronous read failure", async () => {
|
|
68
|
+
const controller = new AbortController();
|
|
69
|
+
const added = vi.spyOn(controller.signal, "addEventListener");
|
|
70
|
+
const removed = vi.spyOn(controller.signal, "removeEventListener");
|
|
71
|
+
const error = new Error("read failed");
|
|
72
|
+
const finalize = vi.fn(async () => ({
|
|
73
|
+
done: true as const,
|
|
74
|
+
value: undefined,
|
|
75
|
+
}));
|
|
76
|
+
const source: AsyncIterable<number> = {
|
|
77
|
+
[Symbol.asyncIterator]: () => ({
|
|
78
|
+
next: () => {
|
|
79
|
+
throw error;
|
|
80
|
+
},
|
|
81
|
+
return: finalize,
|
|
82
|
+
}),
|
|
83
|
+
};
|
|
84
|
+
await expect(
|
|
85
|
+
collect(abortableIterable(source, controller.signal)),
|
|
86
|
+
).rejects.toBe(error);
|
|
87
|
+
expect(removed).toHaveBeenCalledWith("abort", added.mock.calls[0]![1]);
|
|
88
|
+
expect(finalize).toHaveBeenCalledTimes(1);
|
|
89
|
+
controller.abort();
|
|
90
|
+
expect(finalize).toHaveBeenCalledTimes(1);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("does not read or finalize again after the consumer returns", async () => {
|
|
94
|
+
const next = vi.fn(async () => ({ done: false as const, value: 1 }));
|
|
95
|
+
const finalize = vi.fn(async () => ({
|
|
96
|
+
done: true as const,
|
|
97
|
+
value: undefined,
|
|
98
|
+
}));
|
|
99
|
+
const source: AsyncIterable<number> = {
|
|
100
|
+
[Symbol.asyncIterator]: () => ({ next, return: finalize }),
|
|
101
|
+
};
|
|
102
|
+
const iterator = abortableIterable(source, new AbortController().signal)[
|
|
103
|
+
Symbol.asyncIterator
|
|
104
|
+
]();
|
|
105
|
+
await iterator.return?.();
|
|
106
|
+
await iterator.return?.();
|
|
107
|
+
await expect(iterator.next()).resolves.toEqual({
|
|
108
|
+
done: true,
|
|
109
|
+
value: undefined,
|
|
110
|
+
});
|
|
111
|
+
expect(next).not.toHaveBeenCalled();
|
|
112
|
+
expect(finalize).toHaveBeenCalledTimes(1);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("does not finalize a naturally exhausted source", async () => {
|
|
116
|
+
const next = vi.fn(async () => ({ done: true as const, value: undefined }));
|
|
117
|
+
const finalize = vi.fn(async () => ({
|
|
118
|
+
done: true as const,
|
|
119
|
+
value: undefined,
|
|
120
|
+
}));
|
|
121
|
+
const source: AsyncIterable<number> = {
|
|
122
|
+
[Symbol.asyncIterator]: () => ({ next, return: finalize }),
|
|
123
|
+
};
|
|
124
|
+
const iterator = abortableIterable(source, new AbortController().signal)[
|
|
125
|
+
Symbol.asyncIterator
|
|
126
|
+
]();
|
|
127
|
+
await iterator.next();
|
|
128
|
+
await iterator.next();
|
|
129
|
+
await iterator.return?.();
|
|
130
|
+
expect(next).toHaveBeenCalledTimes(1);
|
|
131
|
+
expect(finalize).not.toHaveBeenCalled();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("settles an opening stream on abort and finalizes a late iterable", async () => {
|
|
135
|
+
const opened = deferred<AsyncIterable<number>>();
|
|
136
|
+
const finalize = vi.fn(async () => ({
|
|
137
|
+
done: true as const,
|
|
138
|
+
value: undefined,
|
|
139
|
+
}));
|
|
140
|
+
const controller = new AbortController();
|
|
141
|
+
|
|
142
|
+
const opening = openAbortableIterable(opened.promise, controller.signal);
|
|
143
|
+
controller.abort();
|
|
144
|
+
|
|
145
|
+
await expect(opening).resolves.toBeUndefined();
|
|
146
|
+
opened.resolve({
|
|
147
|
+
[Symbol.asyncIterator]: () => ({
|
|
148
|
+
next: () => Promise.resolve({ done: true, value: undefined }),
|
|
149
|
+
return: finalize,
|
|
150
|
+
}),
|
|
151
|
+
});
|
|
152
|
+
await vi.waitFor(() => expect(finalize).toHaveBeenCalledTimes(1));
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("passes a stream that completes on its own through untouched", async () => {
|
|
156
|
+
async function* source() {
|
|
157
|
+
yield 1;
|
|
158
|
+
yield 2;
|
|
159
|
+
}
|
|
160
|
+
const controller = new AbortController();
|
|
161
|
+
|
|
162
|
+
expect(
|
|
163
|
+
await collect(abortableIterable(source(), controller.signal)),
|
|
164
|
+
).toEqual([1, 2]);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// the reason this wrapper exists: the source ignores the signal and parks on
|
|
168
|
+
// its own work, so nothing but the abort can end the loop
|
|
169
|
+
it("ends the loop while the source is parked mid-chunk", async () => {
|
|
170
|
+
const hang = deferred<void>();
|
|
171
|
+
const finallyRan = vi.fn();
|
|
172
|
+
async function* source() {
|
|
173
|
+
try {
|
|
174
|
+
yield 1;
|
|
175
|
+
await hang.promise;
|
|
176
|
+
yield 2;
|
|
177
|
+
} finally {
|
|
178
|
+
finallyRan();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const controller = new AbortController();
|
|
182
|
+
|
|
183
|
+
const seen: number[] = [];
|
|
184
|
+
const consuming = (async () => {
|
|
185
|
+
for await (const value of abortableIterable(
|
|
186
|
+
source(),
|
|
187
|
+
controller.signal,
|
|
188
|
+
)) {
|
|
189
|
+
seen.push(value);
|
|
190
|
+
}
|
|
191
|
+
})();
|
|
192
|
+
// let the loop take the first chunk and park awaiting the second
|
|
193
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
194
|
+
|
|
195
|
+
controller.abort();
|
|
196
|
+
|
|
197
|
+
// settles even though the source never does
|
|
198
|
+
await consuming;
|
|
199
|
+
expect(seen).toEqual([1]);
|
|
200
|
+
expect(finallyRan).not.toHaveBeenCalled();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("finalizes a source still suspended at a yield when the signal aborts", async () => {
|
|
204
|
+
const finallyRan = vi.fn();
|
|
205
|
+
async function* source() {
|
|
206
|
+
try {
|
|
207
|
+
yield 1;
|
|
208
|
+
yield 2;
|
|
209
|
+
} finally {
|
|
210
|
+
finallyRan();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const controller = new AbortController();
|
|
214
|
+
|
|
215
|
+
const seen: number[] = [];
|
|
216
|
+
for await (const value of abortableIterable(source(), controller.signal)) {
|
|
217
|
+
seen.push(value);
|
|
218
|
+
controller.abort();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
expect(seen).toEqual([1]);
|
|
222
|
+
expect(finallyRan).toHaveBeenCalledTimes(1);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("reports a stream aborted before the first read as exhausted", async () => {
|
|
226
|
+
const next = vi.fn();
|
|
227
|
+
const controller = new AbortController();
|
|
228
|
+
controller.abort();
|
|
229
|
+
|
|
230
|
+
const seen = await collect(
|
|
231
|
+
abortableIterable(
|
|
232
|
+
{
|
|
233
|
+
[Symbol.asyncIterator]: () => ({ next }),
|
|
234
|
+
} as AsyncIterable<number>,
|
|
235
|
+
controller.signal,
|
|
236
|
+
),
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
expect(seen).toEqual([]);
|
|
240
|
+
expect(next).not.toHaveBeenCalled();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("propagates a source failure", async () => {
|
|
244
|
+
async function* source() {
|
|
245
|
+
yield 1;
|
|
246
|
+
throw new Error("stream failed");
|
|
247
|
+
}
|
|
248
|
+
const controller = new AbortController();
|
|
249
|
+
|
|
250
|
+
await expect(
|
|
251
|
+
collect(abortableIterable(source(), controller.signal)),
|
|
252
|
+
).rejects.toThrow("stream failed");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("finalizes a source after its next call rejects", async () => {
|
|
256
|
+
const finalize = vi.fn(async () => ({
|
|
257
|
+
done: true as const,
|
|
258
|
+
value: undefined,
|
|
259
|
+
}));
|
|
260
|
+
const source: AsyncIterable<number> = {
|
|
261
|
+
[Symbol.asyncIterator]: () => ({
|
|
262
|
+
next: () => Promise.reject(new Error("stream failed")),
|
|
263
|
+
return: finalize,
|
|
264
|
+
}),
|
|
265
|
+
};
|
|
266
|
+
const controller = new AbortController();
|
|
267
|
+
|
|
268
|
+
await expect(
|
|
269
|
+
collect(abortableIterable(source, controller.signal)),
|
|
270
|
+
).rejects.toThrow("stream failed");
|
|
271
|
+
expect(finalize).toHaveBeenCalledTimes(1);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("finalizes the source when the consumer breaks out", async () => {
|
|
275
|
+
const finallyRan = vi.fn();
|
|
276
|
+
async function* source() {
|
|
277
|
+
try {
|
|
278
|
+
yield 1;
|
|
279
|
+
yield 2;
|
|
280
|
+
} finally {
|
|
281
|
+
finallyRan();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const controller = new AbortController();
|
|
285
|
+
|
|
286
|
+
for await (const _ of abortableIterable(source(), controller.signal)) {
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// finalized without the break waiting on it
|
|
291
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
292
|
+
expect(finallyRan).toHaveBeenCalledTimes(1);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("does not let a source that parks in its own cleanup stall the break", async () => {
|
|
296
|
+
const hang = deferred<void>();
|
|
297
|
+
async function* source() {
|
|
298
|
+
try {
|
|
299
|
+
yield 1;
|
|
300
|
+
yield 2;
|
|
301
|
+
} finally {
|
|
302
|
+
await hang.promise;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const controller = new AbortController();
|
|
306
|
+
|
|
307
|
+
// resolves even though the source's finally never does
|
|
308
|
+
for await (const _ of abortableIterable(source(), controller.signal)) {
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("releases its abort listener after each chunk", async () => {
|
|
314
|
+
const controller = new AbortController();
|
|
315
|
+
const added = vi.spyOn(controller.signal, "addEventListener");
|
|
316
|
+
const removed = vi.spyOn(controller.signal, "removeEventListener");
|
|
317
|
+
async function* source() {
|
|
318
|
+
yield 1;
|
|
319
|
+
yield 2;
|
|
320
|
+
yield 3;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
await collect(abortableIterable(source(), controller.signal));
|
|
324
|
+
|
|
325
|
+
expect(added).toHaveBeenCalledTimes(removed.mock.calls.length);
|
|
326
|
+
expect(added.mock.calls.length).toBeGreaterThan(0);
|
|
327
|
+
});
|
|
328
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const done = <T>(): IteratorResult<T> => ({ done: true, value: undefined });
|
|
2
|
+
|
|
3
|
+
export const openAbortableIterable = async <T>(
|
|
4
|
+
source: AsyncIterable<T> | Promise<AsyncIterable<T>>,
|
|
5
|
+
signal: AbortSignal,
|
|
6
|
+
): Promise<AsyncIterable<T> | undefined> => {
|
|
7
|
+
const opened = Promise.resolve(source);
|
|
8
|
+
let release = () => {};
|
|
9
|
+
const aborted = new Promise<undefined>((resolve) => {
|
|
10
|
+
const onAbort = () => resolve(undefined);
|
|
11
|
+
if (signal.aborted) return onAbort();
|
|
12
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
13
|
+
release = () => signal.removeEventListener("abort", onAbort);
|
|
14
|
+
});
|
|
15
|
+
try {
|
|
16
|
+
const iterable = await Promise.race([opened, aborted]);
|
|
17
|
+
if (!iterable) {
|
|
18
|
+
void opened
|
|
19
|
+
.then((late) => late[Symbol.asyncIterator]().return?.(undefined))
|
|
20
|
+
.catch(() => {});
|
|
21
|
+
}
|
|
22
|
+
return iterable;
|
|
23
|
+
} finally {
|
|
24
|
+
release();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Presents a stream as exhausted once the signal aborts, so a consumer stops
|
|
30
|
+
* waiting on it.
|
|
31
|
+
*
|
|
32
|
+
* A caller's stream is free to ignore the abort signal it is handed. One that
|
|
33
|
+
* also stops yielding, because it is awaiting its own work, leaves `for await`
|
|
34
|
+
* parked on `next()` with nothing left to wake it: the run never settles and
|
|
35
|
+
* whatever is serialized behind it never starts. Reporting the stream as
|
|
36
|
+
* exhausted ends the loop at cancellation instead.
|
|
37
|
+
*
|
|
38
|
+
* The source is finalized on the way out, but never awaited, since a stream
|
|
39
|
+
* that is already parked would not settle that either.
|
|
40
|
+
*/
|
|
41
|
+
export const abortableIterable = <T>(
|
|
42
|
+
source: AsyncIterable<T>,
|
|
43
|
+
signal: AbortSignal,
|
|
44
|
+
): AsyncIterable<T> => ({
|
|
45
|
+
[Symbol.asyncIterator]: () => {
|
|
46
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
47
|
+
let finished = false;
|
|
48
|
+
const finalize = () => {
|
|
49
|
+
if (finished) return;
|
|
50
|
+
finished = true;
|
|
51
|
+
try {
|
|
52
|
+
iterator.return?.(undefined)?.catch(() => {});
|
|
53
|
+
} catch {
|
|
54
|
+
// a source that throws on finalize has nothing left to clean up
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
[Symbol.asyncIterator]() {
|
|
60
|
+
return this;
|
|
61
|
+
},
|
|
62
|
+
next: () => {
|
|
63
|
+
if (finished || signal.aborted) {
|
|
64
|
+
finalize();
|
|
65
|
+
return Promise.resolve(done<T>());
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return new Promise<IteratorResult<T>>((resolve, reject) => {
|
|
69
|
+
// Registered per chunk and released as soon as the chunk settles, so
|
|
70
|
+
// a long stream does not accumulate one reaction per chunk.
|
|
71
|
+
const onAbort = () => {
|
|
72
|
+
finalize();
|
|
73
|
+
resolve(done<T>());
|
|
74
|
+
};
|
|
75
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
76
|
+
const release = () => signal.removeEventListener("abort", onAbort);
|
|
77
|
+
|
|
78
|
+
const onError = (error: unknown) => {
|
|
79
|
+
release();
|
|
80
|
+
finalize();
|
|
81
|
+
reject(error);
|
|
82
|
+
};
|
|
83
|
+
try {
|
|
84
|
+
void iterator.next().then((result) => {
|
|
85
|
+
release();
|
|
86
|
+
if (finished) return resolve(done<T>());
|
|
87
|
+
if (result.done) finished = true;
|
|
88
|
+
resolve(result);
|
|
89
|
+
}, onError);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
onError(error);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
// The consumer breaks out of the loop to stop a cancelled run, so this is
|
|
96
|
+
// reached on the same path as an abort and finalizes the same way: a
|
|
97
|
+
// source whose own cleanup parks must not stall the break either.
|
|
98
|
+
return: async () => {
|
|
99
|
+
finalize();
|
|
100
|
+
return done<T>();
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
});
|
|
@@ -114,6 +114,31 @@ describe("CompositeContextProvider", () => {
|
|
|
114
114
|
expect(composite.getModelContext().system).toBeUndefined();
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
+
it("finishes unregistering when the provider cleanup throws", () => {
|
|
118
|
+
const composite = new CompositeContextProvider();
|
|
119
|
+
const cleanupError = new Error("cleanup failed");
|
|
120
|
+
const subscriber = vi.fn();
|
|
121
|
+
const unsubscribe = vi.fn(() => {
|
|
122
|
+
throw cleanupError;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
composite.subscribe(subscriber);
|
|
126
|
+
const unregister = composite.registerModelContextProvider({
|
|
127
|
+
getModelContext: () => ({ system: "provider instructions" }),
|
|
128
|
+
subscribe: () => unsubscribe,
|
|
129
|
+
});
|
|
130
|
+
subscriber.mockClear();
|
|
131
|
+
|
|
132
|
+
expect(() => unregister()).toThrow(cleanupError);
|
|
133
|
+
expect(composite.getModelContext().system).toBeUndefined();
|
|
134
|
+
expect(subscriber).toHaveBeenCalledOnce();
|
|
135
|
+
expect(unsubscribe).toHaveBeenCalledOnce();
|
|
136
|
+
|
|
137
|
+
expect(() => unregister()).not.toThrow();
|
|
138
|
+
expect(subscriber).toHaveBeenCalledOnce();
|
|
139
|
+
expect(unsubscribe).toHaveBeenCalledOnce();
|
|
140
|
+
});
|
|
141
|
+
|
|
117
142
|
it("notifies every subscriber and rethrows on provider updates", () => {
|
|
118
143
|
const composite = new CompositeContextProvider();
|
|
119
144
|
const error = new Error("subscriber failed");
|
|
@@ -32,12 +32,33 @@ export class CompositeContextProvider implements ModelContextProvider {
|
|
|
32
32
|
}
|
|
33
33
|
this._providerUnsubscribes.set(id, unsubscribe);
|
|
34
34
|
this.notifySubscribers();
|
|
35
|
+
let released = false;
|
|
35
36
|
return () => {
|
|
37
|
+
if (released) return;
|
|
38
|
+
released = true;
|
|
36
39
|
this._providers.delete(id);
|
|
37
40
|
const unsubscribe = this._providerUnsubscribes.get(id);
|
|
38
|
-
unsubscribe?.();
|
|
39
41
|
this._providerUnsubscribes.delete(id);
|
|
40
|
-
|
|
42
|
+
|
|
43
|
+
let cleanupFailed = false;
|
|
44
|
+
let cleanupError: unknown;
|
|
45
|
+
const runCleanup = (cleanup: () => void) => {
|
|
46
|
+
try {
|
|
47
|
+
cleanup();
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (cleanupFailed) {
|
|
50
|
+
console.error(error);
|
|
51
|
+
} else {
|
|
52
|
+
cleanupFailed = true;
|
|
53
|
+
cleanupError = error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
if (unsubscribe) runCleanup(unsubscribe);
|
|
59
|
+
runCleanup(() => this.notifySubscribers());
|
|
60
|
+
|
|
61
|
+
if (cleanupFailed) throw cleanupError;
|
|
41
62
|
};
|
|
42
63
|
}
|
|
43
64
|
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import type {
|
|
3
|
+
MessagePartStatus,
|
|
4
|
+
ToolCallMessagePartStatus,
|
|
5
|
+
} from "../types/message";
|
|
6
|
+
import { getGroupStatus, getGroupSummary } from "./getGroupStatus";
|
|
7
|
+
|
|
8
|
+
type Status = MessagePartStatus | ToolCallMessagePartStatus;
|
|
9
|
+
|
|
10
|
+
const part = (status: Status) => ({ status });
|
|
3
11
|
|
|
4
12
|
describe("getGroupStatus", () => {
|
|
5
13
|
it("reports running when the first member is running and the last is complete", () => {
|
|
@@ -10,7 +18,6 @@ describe("getGroupStatus", () => {
|
|
|
10
18
|
const status = getGroupStatus(parts);
|
|
11
19
|
|
|
12
20
|
expect(status).toEqual({ type: "running" });
|
|
13
|
-
expect(getGroupStatus(parts, [0, 1])).toBe(status);
|
|
14
21
|
expect(status).toBe(getGroupStatus([{ status: { type: "running" } }]));
|
|
15
22
|
expect(Object.isFrozen(status)).toBe(true);
|
|
16
23
|
});
|
|
@@ -27,10 +34,69 @@ describe("getGroupStatus", () => {
|
|
|
27
34
|
|
|
28
35
|
it("matches the positional outcome for statusless adapters", () => {
|
|
29
36
|
expect(
|
|
30
|
-
|
|
37
|
+
getGroupSummary(
|
|
31
38
|
[{ status: { type: "complete" } }, { status: { type: "running" } }],
|
|
32
39
|
[0, 1],
|
|
33
|
-
),
|
|
40
|
+
).status,
|
|
34
41
|
).toEqual({ type: "running" });
|
|
35
42
|
});
|
|
36
43
|
});
|
|
44
|
+
|
|
45
|
+
describe("getGroupSummary", () => {
|
|
46
|
+
it("tallies every status over the requested indices and reports running when any part runs", () => {
|
|
47
|
+
const summary = getGroupSummary(
|
|
48
|
+
[
|
|
49
|
+
part({ type: "complete" }),
|
|
50
|
+
part({ type: "running" }),
|
|
51
|
+
part({ type: "incomplete", reason: "error" }),
|
|
52
|
+
part({ type: "requires-action", reason: "tool-calls" }),
|
|
53
|
+
],
|
|
54
|
+
[0, 1, 2, 3],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(summary.counts).toEqual({
|
|
58
|
+
running: 1,
|
|
59
|
+
complete: 1,
|
|
60
|
+
incomplete: 1,
|
|
61
|
+
requiresAction: 1,
|
|
62
|
+
});
|
|
63
|
+
expect(
|
|
64
|
+
Object.values(summary.counts).reduce((sum, count) => sum + count),
|
|
65
|
+
).toBe(4);
|
|
66
|
+
expect(summary.status).toEqual({ type: "running" });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("uses the last status when no part runs and treats missing parts as complete", () => {
|
|
70
|
+
const summary = getGroupSummary(
|
|
71
|
+
[
|
|
72
|
+
undefined,
|
|
73
|
+
part({ type: "incomplete", reason: "cancelled" }),
|
|
74
|
+
part({ type: "requires-action", reason: "interrupt" }),
|
|
75
|
+
],
|
|
76
|
+
[0, 1, 2],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(summary.counts).toEqual({
|
|
80
|
+
running: 0,
|
|
81
|
+
complete: 1,
|
|
82
|
+
incomplete: 1,
|
|
83
|
+
requiresAction: 1,
|
|
84
|
+
});
|
|
85
|
+
expect(summary.status).toEqual({
|
|
86
|
+
type: "requires-action",
|
|
87
|
+
reason: "interrupt",
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("returns complete with zero counts for empty indices", () => {
|
|
92
|
+
expect(getGroupSummary([], [])).toEqual({
|
|
93
|
+
status: { type: "complete" },
|
|
94
|
+
counts: {
|
|
95
|
+
running: 0,
|
|
96
|
+
complete: 0,
|
|
97
|
+
incomplete: 0,
|
|
98
|
+
requiresAction: 0,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -10,22 +10,45 @@ type PartWithStatus = {
|
|
|
10
10
|
|
|
11
11
|
export const getGroupStatus = (
|
|
12
12
|
parts: readonly (PartWithStatus | undefined)[],
|
|
13
|
-
indices?: readonly number[],
|
|
14
13
|
): MessagePartStatus | ToolCallMessagePartStatus => {
|
|
15
|
-
if (indices) {
|
|
16
|
-
for (const index of indices) {
|
|
17
|
-
if (parts[index]?.status.type === "running") return RUNNING_STATUS;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const lastIndex = indices.at(-1);
|
|
21
|
-
return lastIndex === undefined
|
|
22
|
-
? COMPLETE_STATUS
|
|
23
|
-
: (parts[lastIndex]?.status ?? COMPLETE_STATUS);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
14
|
for (const part of parts) {
|
|
27
15
|
if (part?.status.type === "running") return RUNNING_STATUS;
|
|
28
16
|
}
|
|
29
17
|
|
|
30
18
|
return parts.at(-1)?.status ?? COMPLETE_STATUS;
|
|
31
19
|
};
|
|
20
|
+
|
|
21
|
+
export const getGroupSummary = (
|
|
22
|
+
parts: readonly (PartWithStatus | undefined)[],
|
|
23
|
+
indices: readonly number[],
|
|
24
|
+
) => {
|
|
25
|
+
const counts = {
|
|
26
|
+
running: 0,
|
|
27
|
+
complete: 0,
|
|
28
|
+
incomplete: 0,
|
|
29
|
+
requiresAction: 0,
|
|
30
|
+
};
|
|
31
|
+
let status: MessagePartStatus | ToolCallMessagePartStatus = COMPLETE_STATUS;
|
|
32
|
+
let isRunning = false;
|
|
33
|
+
|
|
34
|
+
for (const index of indices) {
|
|
35
|
+
status = parts[index]?.status ?? COMPLETE_STATUS;
|
|
36
|
+
switch (status.type) {
|
|
37
|
+
case "running":
|
|
38
|
+
counts.running++;
|
|
39
|
+
isRunning = true;
|
|
40
|
+
break;
|
|
41
|
+
case "complete":
|
|
42
|
+
counts.complete++;
|
|
43
|
+
break;
|
|
44
|
+
case "incomplete":
|
|
45
|
+
counts.incomplete++;
|
|
46
|
+
break;
|
|
47
|
+
case "requires-action":
|
|
48
|
+
counts.requiresAction++;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { status: isRunning ? RUNNING_STATUS : status, counts };
|
|
54
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { nullProtoRecord } from "./record";
|
|
3
|
+
|
|
4
|
+
describe("nullProtoRecord", () => {
|
|
5
|
+
it("has no prototype", () => {
|
|
6
|
+
expect(Object.getPrototypeOf(nullProtoRecord())).toBeNull();
|
|
7
|
+
expect(Object.getPrototypeOf(nullProtoRecord({ a: 1 }))).toBeNull();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("copies own entries from every source, later sources winning", () => {
|
|
11
|
+
expect({ ...nullProtoRecord({ a: 1, b: 2 }, undefined, { b: 3 }) }).toEqual(
|
|
12
|
+
{
|
|
13
|
+
a: 1,
|
|
14
|
+
b: 3,
|
|
15
|
+
},
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it.each(["__proto__", "constructor", "toString"])(
|
|
20
|
+
"assigns %s as an own property",
|
|
21
|
+
(key) => {
|
|
22
|
+
const record = nullProtoRecord<number>();
|
|
23
|
+
record[key] = 1;
|
|
24
|
+
|
|
25
|
+
expect(Object.hasOwn(record, key)).toBe(true);
|
|
26
|
+
expect(record[key]).toBe(1);
|
|
27
|
+
expect(Object.keys(record)).toEqual([key]);
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
it.each(["__proto__", "constructor", "toString"])(
|
|
32
|
+
"carries %s through a source",
|
|
33
|
+
(key) => {
|
|
34
|
+
const record = nullProtoRecord<number>({ [key]: 1, ok: 2 });
|
|
35
|
+
|
|
36
|
+
expect(Object.keys(record)).toEqual([key, "ok"]);
|
|
37
|
+
expect(record[key]).toBe(1);
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
it("reads an absent prototype-named key as undefined", () => {
|
|
42
|
+
const record = nullProtoRecord<number>({ ok: 1 });
|
|
43
|
+
|
|
44
|
+
expect(record["__proto__"]).toBeUndefined();
|
|
45
|
+
expect(record["constructor"]).toBeUndefined();
|
|
46
|
+
expect(record["toString"]).toBeUndefined();
|
|
47
|
+
});
|
|
48
|
+
});
|