@assistant-ui/core 0.3.16 → 0.3.18
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/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +4 -2
- package/dist/internal.js +4 -2
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +10 -4
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/frame/provider.d.ts +5 -0
- package/dist/model-context/frame/provider.d.ts.map +1 -1
- package/dist/model-context/frame/provider.js +101 -28
- 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 +16 -9
- 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/tool-call-text.d.ts +26 -0
- package/dist/model-context/tool-call-text.d.ts.map +1 -0
- package/dist/model-context/tool-call-text.js +18 -0
- package/dist/model-context/tool-call-text.js.map +1 -0
- 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/RuntimeAdapter.js +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
- package/dist/react/client/AdaptedRemoteThread.js +1 -1
- package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
- package/dist/react/client/InMemoryThreadList.js +63 -57
- 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 +25 -28
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/RemoteThreadList.d.ts +16 -0
- package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
- package/dist/react/client/RemoteThreadList.js +354 -242
- 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 +24 -18
- package/dist/react/client/Tools.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +2 -1
- package/dist/react/interactables-legacy/Interactables.js +246 -125
- package/dist/react/interactables-legacy/Interactables.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/toolbox.d.ts +2 -14
- package/dist/react/model-context/toolbox.d.ts.map +1 -1
- package/dist/react/model-context/toolbox.js +1 -13
- package/dist/react/model-context/toolbox.js.map +1 -1
- package/dist/react/model-context/useAssistantContext.js +37 -7
- package/dist/react/model-context/useAssistantContext.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/useAuiToolOverrides.js +31 -5
- package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
- package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
- package/dist/react/model-context/useInlineRender.js +10 -36
- package/dist/react/model-context/useInlineRender.js.map +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/primitive-hooks/useMessageError.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useMessageError.js +2 -8
- package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
- package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
- package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
- package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
- package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
- package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
- package/dist/react/primitives/composer/ComposerQueue.js +4 -3
- package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
- package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageAttachments.js +7 -6
- package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
- package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
- package/dist/react/primitives/message/MessageParts.js +3 -3
- package/dist/react/primitives/message/MessageParts.js.map +1 -1
- package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
- package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
- package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +6 -4
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +59 -29
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +11 -6
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +160 -143
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.d.ts +2 -6
- package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.js +15 -13
- package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
- package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
- package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +4 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +424 -52
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +160 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
- package/dist/react/runtimes/external-message-converter.js +1 -1
- 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 +77 -29
- package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
- package/dist/react/types/MessagePartComponentTypes.d.ts +7 -3
- package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
- package/dist/react/types/scopes/tools.d.ts +2 -0
- package/dist/react/types/scopes/tools.d.ts.map +1 -1
- package/dist/runtime/api/message-part-runtime.d.ts +2 -2
- package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
- package/dist/runtime/api/message-part-runtime.js +1 -1
- package/dist/runtime/api/message-part-runtime.js.map +1 -1
- 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 +12 -1
- package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-runtime.js +23 -14
- package/dist/runtime/api/thread-list-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +5 -1
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-runtime.js +1 -1
- package/dist/runtime/api/thread-runtime.js.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +16 -12
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -2
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +8 -7
- 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 +18 -3
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts +15 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/utils/auto-status.d.ts +1 -1
- package/dist/runtime/utils/auto-status.d.ts.map +1 -1
- package/dist/runtime/utils/auto-status.js +6 -2
- package/dist/runtime/utils/auto-status.js.map +1 -1
- package/dist/runtime/utils/external-message-conversion.d.ts +3 -2
- package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
- package/dist/runtime/utils/external-message-conversion.js +5 -4
- 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 +15 -6
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
- package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
- package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
- package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
- package/dist/runtime/utils/thread-message-like.d.ts +2 -10
- package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
- package/dist/runtime/utils/thread-message-like.js +1 -1
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
- package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
- package/dist/runtime/utils/tool-call-tree.js +85 -0
- package/dist/runtime/utils/tool-call-tree.js.map +1 -0
- package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
- package/dist/runtimes/external-store/external-store-adapter.d.ts.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 +2 -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 +34 -28
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
- package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
- package/dist/runtimes/internal.d.ts +2 -2
- 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 +3 -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 +148 -43
- 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 +11 -0
- package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
- package/dist/runtimes/remote-thread-list/title.js +14 -0
- package/dist/runtimes/remote-thread-list/title.js.map +1 -0
- package/dist/runtimes/remote-thread-list/types.d.ts +16 -5
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +97 -60
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
- package/dist/store/clients/external-thread.d.ts +1 -1
- package/dist/store/clients/external-thread.d.ts.map +1 -1
- package/dist/store/clients/external-thread.js +30 -7
- 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 +2 -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 +19 -6
- package/dist/store/clients/suggestions.js.map +1 -1
- package/dist/store/clients/thread-message-client.js +1 -1
- package/dist/store/clients/thread-selection-events.js +1 -1
- package/dist/store/index.d.ts +2 -1
- package/dist/store/index.js +2 -1
- package/dist/store/internal.d.ts +2 -2
- package/dist/store/internal.js +2 -2
- package/dist/store/primitive-predicates.d.ts +4 -1
- package/dist/store/primitive-predicates.d.ts.map +1 -1
- package/dist/store/primitive-predicates.js +9 -1
- 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 +98 -62
- 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 +2 -2
- 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 +128 -107
- 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 +194 -122
- package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
- package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
- package/dist/store/runtime-clients/useSubscribable.js +1 -1
- package/dist/store/runtime-clients/useSubscribable.js.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/part.d.ts +6 -3
- package/dist/store/scopes/part.d.ts.map +1 -1
- 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 +8 -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 +8 -1
- package/dist/subscribable/subscribable.d.ts.map +1 -1
- package/dist/subscribable/subscribable.js +24 -16
- 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/message.d.ts +40 -1
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js +7 -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 +72 -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/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/package.json +12 -17
- package/src/adapters/attachment.test.ts +50 -1
- package/src/adapters/attachment.ts +7 -4
- package/src/index.ts +7 -1
- package/src/internal.ts +12 -0
- package/src/model-context/frame/host.test.ts +24 -0
- package/src/model-context/frame/host.ts +10 -4
- package/src/model-context/frame/provider.test.ts +288 -1
- package/src/model-context/frame/provider.ts +148 -30
- package/src/model-context/interactable-composer-metadata.test.ts +28 -0
- package/src/model-context/interactable-composer-metadata.ts +9 -3
- package/src/model-context/registry.test.ts +12 -0
- package/src/model-context/registry.ts +3 -2
- package/src/model-context/tool-call-text.test.ts +67 -0
- package/src/model-context/tool-call-text.ts +67 -0
- package/src/model-context/types.test.ts +25 -0
- package/src/model-context/types.ts +9 -4
- package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
- package/src/react/RuntimeAdapter.test.ts +34 -0
- package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
- package/src/react/client/InMemoryThreadList.ts +1 -0
- package/src/react/client/Interactables.test.ts +28 -0
- package/src/react/client/Interactables.ts +15 -13
- package/src/react/client/RemoteThreadList.background.test.ts +526 -0
- package/src/react/client/RemoteThreadList.concurrent.test.tsx +315 -0
- package/src/react/client/RemoteThreadList.test.ts +617 -3
- package/src/react/client/RemoteThreadList.ts +433 -167
- package/src/react/client/Tools.ts +29 -12
- package/src/react/index.ts +4 -0
- package/src/react/interactables-legacy/Interactables.ts +16 -16
- package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
- package/src/react/model-context/makeAssistantTool.ts +2 -2
- package/src/react/model-context/modelContextHooks.test.tsx +131 -0
- package/src/react/model-context/toolbox.test.ts +9 -9
- package/src/react/model-context/toolbox.ts +9 -36
- package/src/react/model-context/useAssistantContext.ts +4 -2
- package/src/react/model-context/useAssistantTool.ts +2 -2
- package/src/react/model-context/useAuiToolOverrides.ts +4 -2
- package/src/react/model-context/useInlineRender.test.tsx +48 -0
- package/src/react/model-context/useInlineRender.ts +14 -10
- package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
- package/src/react/model-context/useToolArgsStatus.ts +2 -1
- package/src/react/primitive-hooks/useMessageError.ts +2 -19
- package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
- package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
- package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
- package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
- package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
- package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
- package/src/react/primitives/message/MessageAttachments.tsx +10 -7
- package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
- package/src/react/primitives/message/MessageParts.tsx +3 -3
- package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
- package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +127 -1
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +99 -35
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +274 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +223 -152
- package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
- package/src/react/runtimes/RemoteThreadResource.ts +20 -16
- package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
- package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +546 -5
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +546 -103
- package/src/react/runtimes/cloud/auiV0.ts +5 -0
- package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +103 -0
- package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +237 -0
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
- package/src/react/runtimes/external-message-converter.ts +1 -0
- 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.concurrent.test.tsx +216 -0
- package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
- package/src/react/runtimes/useRemoteThreadListRuntime.ts +57 -6
- package/src/react/types/MessagePartComponentTypes.ts +7 -3
- package/src/react/types/scopes/tools.ts +2 -0
- package/src/runtime/api/message-part-runtime.ts +3 -3
- 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 +46 -12
- package/src/runtime/api/thread-runtime.test.ts +78 -0
- package/src/runtime/api/thread-runtime.ts +8 -1
- package/src/runtime/base/base-composer-runtime-core.ts +36 -8
- package/src/runtime/base/base-thread-runtime-core.test.ts +149 -4
- package/src/runtime/base/base-thread-runtime-core.ts +16 -8
- package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
- package/src/runtime/interfaces/thread-list-runtime-core.ts +25 -2
- package/src/runtime/interfaces/thread-runtime-core.ts +17 -1
- package/src/runtime/utils/auto-status.test.ts +57 -0
- package/src/runtime/utils/auto-status.ts +13 -1
- package/src/runtime/utils/external-message-conversion.test.ts +165 -0
- package/src/runtime/utils/external-message-conversion.ts +18 -2
- package/src/runtime/utils/message-repository.ts +29 -17
- package/src/runtime/utils/resolveToolApprovalResponse.test.ts +73 -0
- package/src/runtime/utils/resolveToolApprovalResponse.ts +27 -6
- package/src/runtime/utils/thread-message-like.ts +3 -11
- package/src/runtime/utils/tool-call-tree.test.ts +191 -0
- package/src/runtime/utils/tool-call-tree.ts +97 -0
- package/src/runtimes/external-store/external-store-adapter.ts +38 -2
- 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 +56 -33
- package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
- package/src/runtimes/internal.ts +1 -0
- package/src/runtimes/local/local-thread-runtime-core.test.ts +29 -3
- package/src/runtimes/local/local-thread-runtime-core.ts +5 -1
- package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +238 -0
- package/src/runtimes/remote-thread-list/remote-thread-state.ts +223 -44
- 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 +16 -0
- package/src/runtimes/remote-thread-list/types.ts +16 -5
- package/src/runtimes/tool-invocations/EDGE_CASES.md +110 -5
- package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +750 -136
- package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +144 -91
- package/src/store/clients/external-thread.ts +62 -11
- 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 +195 -1
- package/src/store/clients/suggestions.ts +45 -34
- package/src/store/index.ts +4 -0
- package/src/store/internal.ts +2 -0
- package/src/store/primitive-predicates.ts +35 -0
- 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 +17 -2
- package/src/store/runtime-clients/thread-runtime-client.ts +58 -10
- package/src/store/runtime-clients/useSubscribable.ts +8 -2
- package/src/store/scopes/composer.ts +16 -4
- package/src/store/scopes/message.ts +14 -0
- package/src/store/scopes/part.ts +6 -3
- package/src/store/scopes/thread-list-item.ts +5 -2
- package/src/store/scopes/thread.ts +4 -0
- package/src/store/scopes/threads.ts +1 -0
- package/src/subscribable/subscribable.test.ts +50 -0
- package/src/subscribable/subscribable.ts +33 -21
- package/src/tests/MessageRepository.test.ts +116 -0
- 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 +158 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
- package/src/tests/attachment-adapters.test.ts +14 -0
- package/src/tests/base-composer-runtime-core.test.ts +39 -0
- package/src/tests/external-store-optimistic-id.test.ts +79 -0
- package/src/tests/external-store-repository-instance.test.ts +166 -0
- package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +49 -34
- package/src/tests/external-thread-attachments.test.tsx +224 -1
- package/src/tests/external-thread-parity.test.tsx +23 -0
- package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
- package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
- package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
- package/src/tests/thread-switch-events.test.tsx +475 -2
- package/src/tests/tools-scope-migration.test.tsx +28 -0
- package/src/types/attachment.ts +4 -0
- package/src/types/message.ts +50 -2
- package/src/utils/abortable-iterable.test.ts +213 -0
- package/src/utils/abortable-iterable.ts +93 -0
- package/src/utils/composite-context-provider.test.ts +25 -0
- package/src/utils/composite-context-provider.ts +23 -2
- package/src/utils/record.test.ts +48 -0
- package/src/utils/record.ts +11 -0
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { Unsubscribe } from "../../types/unsubscribe";
|
|
2
2
|
import type { ThreadRuntimeEventType } from "../../runtime/interfaces/thread-runtime-core";
|
|
3
|
-
import type {
|
|
4
|
-
|
|
3
|
+
import type {
|
|
4
|
+
CreateAppendMessage,
|
|
5
|
+
ThreadRuntime,
|
|
6
|
+
} from "../../runtime/api/thread-runtime";
|
|
7
|
+
import { useMemo, useEffect, useCallback, type RefObject } from "react";
|
|
5
8
|
import { useResource, resource, withKey } from "@assistant-ui/tap";
|
|
6
9
|
import { liveRef } from "./liveRef";
|
|
7
10
|
import type { ClientOutput } from "@assistant-ui/store";
|
|
@@ -20,17 +23,21 @@ const useMessageClientById = ({
|
|
|
20
23
|
runtime,
|
|
21
24
|
id,
|
|
22
25
|
threadIdRef,
|
|
26
|
+
threadId,
|
|
23
27
|
}: {
|
|
24
28
|
runtime: ThreadRuntime;
|
|
25
29
|
id: string;
|
|
26
30
|
threadIdRef: RefObject<string>;
|
|
31
|
+
threadId: string;
|
|
27
32
|
}) => {
|
|
28
33
|
const messageRuntime = useMemo(
|
|
29
34
|
() => runtime.getMessageById(id),
|
|
30
35
|
[runtime, id],
|
|
31
36
|
);
|
|
32
37
|
|
|
33
|
-
return useResource(
|
|
38
|
+
return useResource(
|
|
39
|
+
MessageClient({ runtime: messageRuntime, threadIdRef, threadId }),
|
|
40
|
+
);
|
|
34
41
|
};
|
|
35
42
|
|
|
36
43
|
const MessageClientById = resource(useMessageClientById);
|
|
@@ -72,11 +79,24 @@ const useThreadClient = ({
|
|
|
72
79
|
() => liveRef(() => runtime.getState()!.threadId),
|
|
73
80
|
[runtime],
|
|
74
81
|
);
|
|
82
|
+
const emitThreadEvent = (
|
|
83
|
+
event: "thread.cancelRun" | "thread.voiceStarted",
|
|
84
|
+
) => {
|
|
85
|
+
emit(event, { threadId: runtime.getState()!.threadId });
|
|
86
|
+
};
|
|
87
|
+
const isSuggestion = useCallback(
|
|
88
|
+
(text: string) =>
|
|
89
|
+
runtime
|
|
90
|
+
.getState()!
|
|
91
|
+
.suggestions.some((suggestion) => suggestion.prompt === text),
|
|
92
|
+
[runtime],
|
|
93
|
+
);
|
|
75
94
|
|
|
76
95
|
const composer = useClientResource(
|
|
77
96
|
ComposerClient({
|
|
78
97
|
runtime: runtime.composer,
|
|
79
98
|
threadIdRef,
|
|
99
|
+
isSuggestion,
|
|
80
100
|
}),
|
|
81
101
|
);
|
|
82
102
|
const suggestions = useClientResource(
|
|
@@ -84,11 +104,16 @@ const useThreadClient = ({
|
|
|
84
104
|
);
|
|
85
105
|
const messages = useClientLookup(
|
|
86
106
|
runtimeState.messages.map((m) =>
|
|
87
|
-
withKey(
|
|
88
|
-
runtime,
|
|
107
|
+
withKey(
|
|
89
108
|
m.id,
|
|
90
|
-
|
|
91
|
-
|
|
109
|
+
MessageClientById({
|
|
110
|
+
runtime,
|
|
111
|
+
id: m.id,
|
|
112
|
+
threadIdRef,
|
|
113
|
+
threadId: runtimeState.threadId,
|
|
114
|
+
}),
|
|
115
|
+
[runtime, m.id, threadIdRef, runtimeState.threadId],
|
|
116
|
+
),
|
|
92
117
|
),
|
|
93
118
|
);
|
|
94
119
|
|
|
@@ -114,18 +139,41 @@ const useThreadClient = ({
|
|
|
114
139
|
getState: () => state,
|
|
115
140
|
composer: () => composer.methods,
|
|
116
141
|
suggestions: () => suggestions.methods,
|
|
117
|
-
append:
|
|
142
|
+
append: (message) => {
|
|
143
|
+
const appended: Exclude<CreateAppendMessage, string> =
|
|
144
|
+
typeof message === "string"
|
|
145
|
+
? { content: [{ type: "text", text: message }] }
|
|
146
|
+
: message;
|
|
147
|
+
if ((appended.role ?? "user") === "user") {
|
|
148
|
+
const text = appended.content
|
|
149
|
+
.map((part) => (part.type === "text" ? part.text : ""))
|
|
150
|
+
.join("");
|
|
151
|
+
emit("composer.send", {
|
|
152
|
+
threadId: runtime.getState()!.threadId,
|
|
153
|
+
chars: text.length,
|
|
154
|
+
attachments: appended.attachments?.length ?? 0,
|
|
155
|
+
...(isSuggestion(text) ? { suggestion: true } : undefined),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
runtime.append(message);
|
|
159
|
+
},
|
|
118
160
|
deleteMessage: runtime.deleteMessage,
|
|
119
161
|
startRun: runtime.startRun,
|
|
120
162
|
resumeRun: runtime.resumeRun,
|
|
121
163
|
importExternalState: runtime.importExternalState,
|
|
122
|
-
cancelRun:
|
|
164
|
+
cancelRun: () => {
|
|
165
|
+
if (runtimeState.isRunning) emitThreadEvent("thread.cancelRun");
|
|
166
|
+
runtime.cancelRun();
|
|
167
|
+
},
|
|
123
168
|
getModelContext: runtime.getModelContext,
|
|
124
169
|
export: runtime.export,
|
|
125
170
|
import: runtime.import,
|
|
126
171
|
reset: runtime.reset,
|
|
127
172
|
stopSpeaking: runtime.stopSpeaking,
|
|
128
|
-
connectVoice:
|
|
173
|
+
connectVoice: () => {
|
|
174
|
+
runtime.connectVoice();
|
|
175
|
+
emitThreadEvent("thread.voiceStarted");
|
|
176
|
+
},
|
|
129
177
|
disconnectVoice: runtime.disconnectVoice,
|
|
130
178
|
getVoiceVolume: runtime.getVoiceVolume,
|
|
131
179
|
subscribeVoiceVolume: runtime.subscribeVoiceVolume,
|
|
@@ -2,7 +2,13 @@ import { useSyncExternalStore } from "react";
|
|
|
2
2
|
import type { SubscribableWithState } from "../../subscribable/subscribable";
|
|
3
3
|
|
|
4
4
|
export const useSubscribable = <T>(
|
|
5
|
-
subscribable: Omit<SubscribableWithState<T, any>, "path"
|
|
5
|
+
subscribable: Omit<SubscribableWithState<T, any>, "path"> & {
|
|
6
|
+
getServerSnapshot?: () => T;
|
|
7
|
+
},
|
|
6
8
|
) => {
|
|
7
|
-
return useSyncExternalStore(
|
|
9
|
+
return useSyncExternalStore(
|
|
10
|
+
subscribable.subscribe,
|
|
11
|
+
subscribable.getState,
|
|
12
|
+
subscribable.getServerSnapshot,
|
|
13
|
+
);
|
|
8
14
|
};
|
|
@@ -109,22 +109,34 @@ export type ComposerMeta = {
|
|
|
109
109
|
|
|
110
110
|
export type ComposerEvents = {
|
|
111
111
|
/**
|
|
112
|
-
* The user sent
|
|
113
|
-
* came from an edit composer.
|
|
112
|
+
* The user sent a message, from the composer or through `thread.append`.
|
|
113
|
+
* `messageId` is set when the send came from an edit composer.
|
|
114
114
|
*/
|
|
115
|
-
"composer.send": {
|
|
115
|
+
"composer.send": {
|
|
116
|
+
threadId: string;
|
|
117
|
+
messageId?: string;
|
|
118
|
+
chars: number;
|
|
119
|
+
attachments: number;
|
|
120
|
+
suggestion?: boolean;
|
|
121
|
+
};
|
|
116
122
|
/**
|
|
117
123
|
* An attachment was added to the composer. `messageId` is set when the
|
|
118
124
|
* attachment was added to an edit composer.
|
|
119
125
|
*/
|
|
120
|
-
"composer.attachmentAdd": {
|
|
126
|
+
"composer.attachmentAdd": {
|
|
127
|
+
threadId: string;
|
|
128
|
+
messageId?: string;
|
|
129
|
+
contentType?: string;
|
|
130
|
+
};
|
|
121
131
|
"composer.attachmentAddError": {
|
|
122
132
|
threadId: string;
|
|
123
133
|
messageId?: string;
|
|
124
134
|
attachmentId?: string;
|
|
125
135
|
reason: AttachmentAddErrorReason;
|
|
126
136
|
message: string;
|
|
137
|
+
contentType?: string;
|
|
127
138
|
};
|
|
139
|
+
"composer.cancel": { threadId: string };
|
|
128
140
|
};
|
|
129
141
|
|
|
130
142
|
export type ComposerClientSchema = {
|
|
@@ -66,7 +66,21 @@ export type MessageMeta = {
|
|
|
66
66
|
query: { type: "id"; id: string } | { type: "index"; index: number };
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
+
export type MessageEvents = {
|
|
70
|
+
/** The user asked for this assistant message to be generated again. */
|
|
71
|
+
"message.reload": { threadId: string; messageId: string };
|
|
72
|
+
/** The user moved to another branch at this message. */
|
|
73
|
+
"message.branchSwitched": { threadId: string; messageId: string };
|
|
74
|
+
/** The user copied this message. */
|
|
75
|
+
"message.copied": { threadId: string; messageId: string };
|
|
76
|
+
/** Speech synthesis started for this message. */
|
|
77
|
+
"message.speak": { threadId: string; messageId: string };
|
|
78
|
+
/** This message entered an error status. */
|
|
79
|
+
"message.error": { threadId: string; messageId: string; reason: "error" };
|
|
80
|
+
};
|
|
81
|
+
|
|
69
82
|
export type MessageClientSchema = {
|
|
70
83
|
methods: MessageMethods;
|
|
71
84
|
meta: MessageMeta;
|
|
85
|
+
events: MessageEvents;
|
|
72
86
|
};
|
package/src/store/scopes/part.ts
CHANGED
|
@@ -29,10 +29,13 @@ export type PartMethods = {
|
|
|
29
29
|
resumeToolCall(payload: unknown): void;
|
|
30
30
|
/**
|
|
31
31
|
* Respond to a server-side tool approval gate. The approval id is read from
|
|
32
|
-
* the part. Accepts a boolean decision
|
|
33
|
-
* options.
|
|
32
|
+
* the part. Accepts a boolean decision, the id of one of the approval's
|
|
33
|
+
* options, or a free-form answer when the request asks for one.
|
|
34
|
+
*
|
|
35
|
+
* Resolves once the runtime has accepted the response, and rejects when it
|
|
36
|
+
* could not be recorded, so a renderer can leave its controls retryable.
|
|
34
37
|
*/
|
|
35
|
-
respondToToolApproval(response: ToolApprovalResponse): void
|
|
38
|
+
respondToToolApproval(response: ToolApprovalResponse): Promise<void>;
|
|
36
39
|
__internal_getRuntime?(): MessagePartRuntime;
|
|
37
40
|
};
|
|
38
41
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ThreadListItemGenerateTitleOptions,
|
|
3
|
+
ThreadListItemRuntime,
|
|
4
|
+
} from "../../runtime/api/thread-list-item-runtime";
|
|
2
5
|
import type { ThreadListItemStatus } from "../../runtime/interfaces/thread-list-runtime-core";
|
|
3
6
|
|
|
4
7
|
export type ThreadListItemState = {
|
|
@@ -26,7 +29,7 @@ export type ThreadListItemMethods = {
|
|
|
26
29
|
archive(): void;
|
|
27
30
|
unarchive(): void;
|
|
28
31
|
delete(): void;
|
|
29
|
-
generateTitle(): void;
|
|
32
|
+
generateTitle(options?: ThreadListItemGenerateTitleOptions): void;
|
|
30
33
|
initialize(): Promise<{ remoteId: string; externalId: string | undefined }>;
|
|
31
34
|
detach(): void;
|
|
32
35
|
__internal_getRuntime?(): ThreadListItemRuntime;
|
|
@@ -155,6 +155,10 @@ export type ThreadEvents = {
|
|
|
155
155
|
* state.
|
|
156
156
|
*/
|
|
157
157
|
"thread.runEnd": { threadId: string };
|
|
158
|
+
/** The user stopped the run in progress on this thread. */
|
|
159
|
+
"thread.cancelRun": { threadId: string };
|
|
160
|
+
/** The user started a voice session on this thread. */
|
|
161
|
+
"thread.voiceStarted": { threadId: string };
|
|
158
162
|
/**
|
|
159
163
|
* The thread transitioned from new to initialized. Fires before the first
|
|
160
164
|
* message is added, so read thread state via `useAuiState` rather than
|
|
@@ -9,6 +9,7 @@ export type ThreadsState = {
|
|
|
9
9
|
readonly mainThreadId: string;
|
|
10
10
|
readonly newThreadId: string | null;
|
|
11
11
|
readonly isLoading: boolean;
|
|
12
|
+
readonly loadError: unknown;
|
|
12
13
|
readonly isLoadingMore: boolean;
|
|
13
14
|
readonly hasMore: boolean;
|
|
14
15
|
readonly threadIds: readonly string[];
|
|
@@ -32,6 +32,30 @@ const createBinding = (initialState: TestState) => {
|
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
+
// Mirrors runtime bindings such as `getThreadListState`, which assemble a new
|
|
36
|
+
// object on every read while the underlying values keep their identity.
|
|
37
|
+
const createRebuildingBinding = (initialState: TestState) => {
|
|
38
|
+
let state = initialState;
|
|
39
|
+
const subscribers = new Set<() => void>();
|
|
40
|
+
|
|
41
|
+
const binding: SubscribableWithState<TestState, null> = {
|
|
42
|
+
path: null,
|
|
43
|
+
getState: () => ({ ...state }),
|
|
44
|
+
subscribe: (callback) => {
|
|
45
|
+
subscribers.add(callback);
|
|
46
|
+
return () => subscribers.delete(callback);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
binding,
|
|
52
|
+
update(nextState: TestState) {
|
|
53
|
+
state = nextState;
|
|
54
|
+
for (const callback of subscribers) callback();
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
35
59
|
describe("ShallowMemoizeSubject", () => {
|
|
36
60
|
it("notifies subscribers when a state key is removed", () => {
|
|
37
61
|
const source = createBinding({
|
|
@@ -131,4 +155,30 @@ describe("LazyMemoizeSubject", () => {
|
|
|
131
155
|
expect(subject.getState()).toEqual({ status: "ready" });
|
|
132
156
|
reconnect();
|
|
133
157
|
});
|
|
158
|
+
|
|
159
|
+
it("keeps one reference while unconnected and the state is unchanged", () => {
|
|
160
|
+
// A consumer that reads without subscribing (React's useSyncExternalStore
|
|
161
|
+
// calls getSnapshot outside the subscription) must not see a new object on
|
|
162
|
+
// every call: React compares snapshots with Object.is, so a fresh reference
|
|
163
|
+
// reads as a change and re-renders forever.
|
|
164
|
+
const source = createRebuildingBinding({ status: "ready" });
|
|
165
|
+
const subject = new LazyMemoizeSubject(source.binding);
|
|
166
|
+
|
|
167
|
+
const first = subject.getState();
|
|
168
|
+
|
|
169
|
+
expect(subject.getState()).toBe(first);
|
|
170
|
+
expect(subject.getState()).toBe(first);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("returns a new reference once the unchanged state actually changes", () => {
|
|
174
|
+
const source = createRebuildingBinding({ status: "ready" });
|
|
175
|
+
const subject = new LazyMemoizeSubject(source.binding);
|
|
176
|
+
|
|
177
|
+
const first = subject.getState();
|
|
178
|
+
source.update({ status: "done" });
|
|
179
|
+
|
|
180
|
+
const second = subject.getState();
|
|
181
|
+
expect(second).not.toBe(first);
|
|
182
|
+
expect(second).toEqual({ status: "done" });
|
|
183
|
+
});
|
|
134
184
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { shallowEqual } from "@assistant-ui/store/client";
|
|
1
2
|
import type { Unsubscribe } from "../types/unsubscribe";
|
|
2
3
|
import { notifyEventListeners } from "../utils/notify-event-listeners";
|
|
3
4
|
|
|
@@ -57,25 +58,10 @@ export const notifySubscribers = <TArgs extends unknown[]>(
|
|
|
57
58
|
}
|
|
58
59
|
};
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
)
|
|
64
|
-
if (objA === undefined && objB === undefined) return true;
|
|
65
|
-
if (objA === undefined) return false;
|
|
66
|
-
if (objB === undefined) return false;
|
|
67
|
-
|
|
68
|
-
const keysA = Object.keys(objA);
|
|
69
|
-
if (keysA.length !== Object.keys(objB).length) return false;
|
|
70
|
-
|
|
71
|
-
for (const key of keysA) {
|
|
72
|
-
const valueA = objA[key as keyof T];
|
|
73
|
-
const valueB = objB[key as keyof T];
|
|
74
|
-
if (!Object.is(valueA, valueB)) return false;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
61
|
+
const shallowEqualOrUndefined = <T extends object>(
|
|
62
|
+
a: T | undefined,
|
|
63
|
+
b: T | undefined,
|
|
64
|
+
) => (a === undefined || b === undefined ? a === b : shallowEqual(a, b));
|
|
79
65
|
|
|
80
66
|
export class BaseSubscribable {
|
|
81
67
|
private _subscribers = new Set<() => void>();
|
|
@@ -99,6 +85,32 @@ export class BaseSubscribable {
|
|
|
99
85
|
}
|
|
100
86
|
}
|
|
101
87
|
|
|
88
|
+
export class WritableSubscribable<TState> extends BaseSubscribable {
|
|
89
|
+
private _state: TState;
|
|
90
|
+
|
|
91
|
+
constructor(state: TState) {
|
|
92
|
+
super();
|
|
93
|
+
this._state = state;
|
|
94
|
+
this.subscribe = this.subscribe.bind(this);
|
|
95
|
+
this.getState = this.getState.bind(this);
|
|
96
|
+
// Hydration has to agree with what the server rendered, so the server
|
|
97
|
+
// snapshot stays at the creation-time state rather than following writes.
|
|
98
|
+
this.getServerSnapshot = () => state;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public getState(): TState {
|
|
102
|
+
return this._state;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public getServerSnapshot: () => TState;
|
|
106
|
+
|
|
107
|
+
public setState(state: TState): void {
|
|
108
|
+
if (Object.is(state, this._state)) return;
|
|
109
|
+
this._state = state;
|
|
110
|
+
this._notifySubscribers();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
102
114
|
// lazy connect/disconnect: only opens upstream subscription while it has subscribers
|
|
103
115
|
export abstract class BaseSubject {
|
|
104
116
|
private _subscriptions = new Set<(payload?: unknown) => void>();
|
|
@@ -168,7 +180,7 @@ export class ShallowMemoizeSubject<TState extends object, TPath>
|
|
|
168
180
|
private _syncState() {
|
|
169
181
|
const state = this.binding.getState();
|
|
170
182
|
if (state === SKIP_UPDATE) return false;
|
|
171
|
-
if (
|
|
183
|
+
if (shallowEqualOrUndefined(state, this._previousState)) return false;
|
|
172
184
|
this._previousState = state;
|
|
173
185
|
return true;
|
|
174
186
|
}
|
|
@@ -209,7 +221,7 @@ export class LazyMemoizeSubject<TState extends object, TPath>
|
|
|
209
221
|
if (
|
|
210
222
|
newState !== SKIP_UPDATE &&
|
|
211
223
|
(this._previousState === undefined ||
|
|
212
|
-
!
|
|
224
|
+
!shallowEqualOrUndefined(newState, this._previousState))
|
|
213
225
|
) {
|
|
214
226
|
this._previousState = newState;
|
|
215
227
|
}
|
|
@@ -240,6 +240,80 @@ describe("MessageRepository", () => {
|
|
|
240
240
|
expect(messages2.map((m) => m.id)).toEqual(["parent-id", "branch2-id"]);
|
|
241
241
|
});
|
|
242
242
|
|
|
243
|
+
it("keeps a nested branch selected when revisiting its ancestor", () => {
|
|
244
|
+
repository.addOrUpdateMessage(null, createTestMessage({ id: "r" }));
|
|
245
|
+
repository.addOrUpdateMessage("r", createTestMessage({ id: "a" }));
|
|
246
|
+
repository.addOrUpdateMessage("a", createTestMessage({ id: "x" }));
|
|
247
|
+
repository.addOrUpdateMessage("r", createTestMessage({ id: "b" }));
|
|
248
|
+
repository.addOrUpdateMessage("b", createTestMessage({ id: "y" }));
|
|
249
|
+
|
|
250
|
+
repository.switchToBranch("y");
|
|
251
|
+
expect(repository.getMessages().map((m) => m.id)).toEqual([
|
|
252
|
+
"r",
|
|
253
|
+
"b",
|
|
254
|
+
"y",
|
|
255
|
+
]);
|
|
256
|
+
|
|
257
|
+
repository.switchToBranch("r");
|
|
258
|
+
expect(repository.getMessages().map((m) => m.id)).toEqual([
|
|
259
|
+
"r",
|
|
260
|
+
"b",
|
|
261
|
+
"y",
|
|
262
|
+
]);
|
|
263
|
+
|
|
264
|
+
repository.switchToBranch("x");
|
|
265
|
+
repository.switchToBranch("r");
|
|
266
|
+
expect(repository.getMessages().map((m) => m.id)).toEqual([
|
|
267
|
+
"r",
|
|
268
|
+
"a",
|
|
269
|
+
"x",
|
|
270
|
+
]);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("keeps the selected root branch when deletion selects from the root", () => {
|
|
274
|
+
repository.addOrUpdateMessage(null, createTestMessage({ id: "a" }));
|
|
275
|
+
repository.addOrUpdateMessage("a", createTestMessage({ id: "x" }));
|
|
276
|
+
repository.addOrUpdateMessage(null, createTestMessage({ id: "b" }));
|
|
277
|
+
repository.addOrUpdateMessage("b", createTestMessage({ id: "y" }));
|
|
278
|
+
|
|
279
|
+
repository.switchToBranch("y");
|
|
280
|
+
repository.deleteMessage("y", null);
|
|
281
|
+
|
|
282
|
+
expect(repository.getMessages().map((m) => m.id)).toEqual(["b"]);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("keeps the selected root branch after relinking a message", () => {
|
|
286
|
+
repository.addOrUpdateMessage(null, createTestMessage({ id: "a" }));
|
|
287
|
+
repository.addOrUpdateMessage("a", createTestMessage({ id: "m" }));
|
|
288
|
+
repository.addOrUpdateMessage("m", createTestMessage({ id: "x" }));
|
|
289
|
+
repository.addOrUpdateMessage(null, createTestMessage({ id: "b" }));
|
|
290
|
+
repository.addOrUpdateMessage("b", createTestMessage({ id: "m" }));
|
|
291
|
+
|
|
292
|
+
expect(repository.getMessages().map((m) => m.id)).toEqual([
|
|
293
|
+
"b",
|
|
294
|
+
"m",
|
|
295
|
+
"x",
|
|
296
|
+
]);
|
|
297
|
+
|
|
298
|
+
repository.deleteMessage("x", null);
|
|
299
|
+
|
|
300
|
+
expect(repository.getMessages().map((m) => m.id)).toEqual(["b", "m"]);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("keeps the selected branch when relinking an off-branch message", () => {
|
|
304
|
+
repository.addOrUpdateMessage(null, createTestMessage({ id: "a" }));
|
|
305
|
+
repository.addOrUpdateMessage("a", createTestMessage({ id: "m" }));
|
|
306
|
+
repository.addOrUpdateMessage("a", createTestMessage({ id: "n" }));
|
|
307
|
+
repository.addOrUpdateMessage(null, createTestMessage({ id: "b" }));
|
|
308
|
+
repository.addOrUpdateMessage("b", createTestMessage({ id: "n" }));
|
|
309
|
+
|
|
310
|
+
expect(repository.getMessages().map((m) => m.id)).toEqual(["a", "m"]);
|
|
311
|
+
|
|
312
|
+
repository.deleteMessage("m", null);
|
|
313
|
+
|
|
314
|
+
expect(repository.getMessages().map((m) => m.id)).toEqual(["a"]);
|
|
315
|
+
});
|
|
316
|
+
|
|
243
317
|
it("should operate on a long message history without overflowing the stack", () => {
|
|
244
318
|
const messageCount = 30_000;
|
|
245
319
|
const messages = createLongBranchMessages(messageCount);
|
|
@@ -486,6 +560,31 @@ describe("MessageRepository", () => {
|
|
|
486
560
|
).toBe("parent-id");
|
|
487
561
|
});
|
|
488
562
|
|
|
563
|
+
it("round-trips after reparenting under a later-added ancestor", () => {
|
|
564
|
+
const messageA = createTestMessage({ id: "A" });
|
|
565
|
+
repository.addOrUpdateMessage(null, messageA);
|
|
566
|
+
repository.addOrUpdateMessage("A", createTestMessage({ id: "B" }));
|
|
567
|
+
repository.addOrUpdateMessage("A", createTestMessage({ id: "C" }));
|
|
568
|
+
repository.addOrUpdateMessage(null, createTestMessage({ id: "X" }));
|
|
569
|
+
repository.addOrUpdateMessage("X", messageA);
|
|
570
|
+
|
|
571
|
+
const exported = repository.export();
|
|
572
|
+
expect(exported.messages.map((m) => m.message.id)).toEqual([
|
|
573
|
+
"X",
|
|
574
|
+
"A",
|
|
575
|
+
"B",
|
|
576
|
+
"C",
|
|
577
|
+
]);
|
|
578
|
+
|
|
579
|
+
const restored = new MessageRepository();
|
|
580
|
+
restored.import(exported);
|
|
581
|
+
|
|
582
|
+
expect(restored.headId).toBe("B");
|
|
583
|
+
expect(restored.getMessages().map((m) => m.id)).toEqual(["X", "A", "B"]);
|
|
584
|
+
restored.switchToBranch("C");
|
|
585
|
+
expect(restored.getMessages().map((m) => m.id)).toEqual(["X", "A", "C"]);
|
|
586
|
+
});
|
|
587
|
+
|
|
489
588
|
it("should import repository state", () => {
|
|
490
589
|
const parent = createTestMessage({ id: "parent-id" });
|
|
491
590
|
const child = createTestMessage({ id: "child-id" });
|
|
@@ -941,5 +1040,22 @@ describe("MessageRepository", () => {
|
|
|
941
1040
|
|
|
942
1041
|
expect(repository.headId).toBe("C");
|
|
943
1042
|
});
|
|
1043
|
+
|
|
1044
|
+
it("should advance the head when re-parenting onto the current head", () => {
|
|
1045
|
+
repository.addOrUpdateMessage(null, createTestMessage({ id: "a" }));
|
|
1046
|
+
repository.addOrUpdateMessage("a", createTestMessage({ id: "h" }));
|
|
1047
|
+
repository.addOrUpdateMessage(null, createTestMessage({ id: "b" }));
|
|
1048
|
+
repository.addOrUpdateMessage("b", createTestMessage({ id: "c" }));
|
|
1049
|
+
|
|
1050
|
+
repository.addOrUpdateMessage("h", createTestMessage({ id: "c" }));
|
|
1051
|
+
|
|
1052
|
+
expect(repository.getMessages().map((m) => m.id)).toEqual([
|
|
1053
|
+
"a",
|
|
1054
|
+
"h",
|
|
1055
|
+
"c",
|
|
1056
|
+
]);
|
|
1057
|
+
expect(repository.headId).toBe("c");
|
|
1058
|
+
expect(repository.getMessage("c").parentId).toBe("h");
|
|
1059
|
+
});
|
|
944
1060
|
});
|
|
945
1061
|
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
createCore,
|
|
4
|
+
deferred,
|
|
5
|
+
makeAdapter,
|
|
6
|
+
} from "./remote-thread-list-test-helpers";
|
|
7
|
+
|
|
8
|
+
type InitializeResult = { remoteId: string; externalId: string };
|
|
9
|
+
|
|
10
|
+
describe("RemoteThreadListThreadListRuntimeCore initialize", () => {
|
|
11
|
+
it("keeps the initialization task on the promoted slot when the adapter changes mid-flight", async () => {
|
|
12
|
+
const initializing = deferred<InitializeResult>();
|
|
13
|
+
const core = createCore(
|
|
14
|
+
makeAdapter({ initialize: vi.fn(() => initializing.promise) }),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
await core.switchToNewThread();
|
|
18
|
+
const localId = core.newThreadId!;
|
|
19
|
+
const pending = core.initialize(localId);
|
|
20
|
+
|
|
21
|
+
// An adapter swap advances the generation without resetting the store, so
|
|
22
|
+
// the completion still applies its optimistic transform while `then`
|
|
23
|
+
// declines to reconcile against the retired adapter.
|
|
24
|
+
core.__internal_setOptions({
|
|
25
|
+
adapter: makeAdapter(),
|
|
26
|
+
runtimeHook: () => ({}) as never,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
initializing.resolve({ remoteId: "remote-1", externalId: "external-1" });
|
|
30
|
+
await expect(pending).rejects.toThrow();
|
|
31
|
+
|
|
32
|
+
const item = core.getItemById(localId);
|
|
33
|
+
expect(item?.status).toBe("regular");
|
|
34
|
+
await expect(
|
|
35
|
+
item?.status === "new" ? undefined : item?.initializeTask,
|
|
36
|
+
).resolves.toEqual({ remoteId: "remote-1", externalId: "external-1" });
|
|
37
|
+
});
|
|
38
|
+
});
|