@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
|
@@ -2,15 +2,20 @@ import {
|
|
|
2
2
|
useCallback,
|
|
3
3
|
useEffect,
|
|
4
4
|
useMemo,
|
|
5
|
+
useRef,
|
|
5
6
|
useState,
|
|
6
7
|
useSyncExternalStore,
|
|
7
8
|
} from "react";
|
|
8
|
-
import { resource, withKey } from "@assistant-ui/tap";
|
|
9
|
+
import { resource, withKey, type ResourceElement } from "@assistant-ui/tap";
|
|
9
10
|
import type { ClientOutput } from "@assistant-ui/store";
|
|
10
11
|
import {
|
|
11
12
|
attachTransformScopes,
|
|
13
|
+
Derived,
|
|
14
|
+
useAssistantContextProvider,
|
|
15
|
+
useAssistantContextValue,
|
|
12
16
|
useClientLookup,
|
|
13
17
|
useClientResource,
|
|
18
|
+
useConfiguredAui,
|
|
14
19
|
} from "@assistant-ui/store/client";
|
|
15
20
|
import { isDevelopment, useThreadSelectionEvents } from "../../store/internal";
|
|
16
21
|
import { OptimisticState } from "../../runtimes/remote-thread-list/optimistic-state";
|
|
@@ -20,6 +25,8 @@ import {
|
|
|
20
25
|
createThreadMappingId,
|
|
21
26
|
getThreadData,
|
|
22
27
|
normalizeCursor,
|
|
28
|
+
reconcileInitializedThread,
|
|
29
|
+
promoteNewThreadReducer,
|
|
23
30
|
updateStatusReducer,
|
|
24
31
|
type RemoteThreadData,
|
|
25
32
|
type RemoteThreadState,
|
|
@@ -33,13 +40,23 @@ import type {
|
|
|
33
40
|
} from "../../runtimes/remote-thread-list/types";
|
|
34
41
|
import { ThreadListAdapterChangedError } from "../../runtimes/remote-thread-list/adapter-changed";
|
|
35
42
|
import type { ThreadMessage } from "../../types/message";
|
|
36
|
-
import { AssistantMessageStream } from "assistant-stream";
|
|
37
43
|
import { handleThreadListAction } from "../../store/runtime-clients/handle-thread-list-action";
|
|
38
44
|
import {
|
|
39
45
|
inMemoryThreadListTransformScopes,
|
|
40
46
|
type InMemoryThreadListProps,
|
|
41
47
|
} from "./InMemoryThreadList";
|
|
42
48
|
import { AdaptedRemoteThread } from "./AdaptedRemoteThread";
|
|
49
|
+
import {
|
|
50
|
+
applyTitleStream,
|
|
51
|
+
isTitleSourceMessage,
|
|
52
|
+
} from "../../runtimes/remote-thread-list/title";
|
|
53
|
+
import {
|
|
54
|
+
clearThreadTitleState,
|
|
55
|
+
finishThreadTitleRename,
|
|
56
|
+
runThreadTitleGeneration,
|
|
57
|
+
startThreadTitleRename,
|
|
58
|
+
type ThreadTitleState,
|
|
59
|
+
} from "../../runtimes/remote-thread-list/title-generation";
|
|
43
60
|
|
|
44
61
|
const RESOLVED_PROMISE = Promise.resolve();
|
|
45
62
|
|
|
@@ -61,7 +78,20 @@ export type RemoteThreadListProps = {
|
|
|
61
78
|
onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;
|
|
62
79
|
onSwitchToThread?: ((threadId: string) => void) | undefined;
|
|
63
80
|
onSwitchToNewThread?: (() => void) | undefined;
|
|
81
|
+
/** Called after the backing adapter successfully deletes the thread. */
|
|
64
82
|
onDelete?: ((threadId: string) => void) | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Keeps every thread the session switched to mounted, so a run continues
|
|
85
|
+
* after the user switches away, per-item `isRunning` reflects background
|
|
86
|
+
* runs, and a freshly initialized thread generates its title automatically.
|
|
87
|
+
* A body unmounts on delete, detach, or an adapter replacement (`reload()`
|
|
88
|
+
* after swapping the adapter); visited threads otherwise stay mounted for
|
|
89
|
+
* the client's lifetime. Each body reads its own thread through the ambient
|
|
90
|
+
* `threadListItem` scope, and `unstable_useAdapters` runs once per mounted
|
|
91
|
+
* body. Off (the default), only the visible thread is mounted and a switch
|
|
92
|
+
* unmounts it. The mode is fixed at mount.
|
|
93
|
+
*/
|
|
94
|
+
backgroundThreads?: boolean | undefined;
|
|
65
95
|
};
|
|
66
96
|
|
|
67
97
|
const threadNotFoundError = (threadIdOrRemoteId: string, action: string) =>
|
|
@@ -83,16 +113,6 @@ const toInitializeResult = (
|
|
|
83
113
|
externalId: result.externalId,
|
|
84
114
|
});
|
|
85
115
|
|
|
86
|
-
const applyTitleStream = async (
|
|
87
|
-
stream: Parameters<typeof AssistantMessageStream.fromAssistantStream>[0],
|
|
88
|
-
onTitle: (title: string | undefined) => Promise<void>,
|
|
89
|
-
) => {
|
|
90
|
-
const messageStream = AssistantMessageStream.fromAssistantStream(stream);
|
|
91
|
-
for await (const result of messageStream) {
|
|
92
|
-
await onTitle(result.parts.filter((part) => part.type === "text")[0]?.text);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
116
|
const useThreadListItemClient = (props: {
|
|
97
117
|
data: RemoteThreadData;
|
|
98
118
|
isRunning: boolean;
|
|
@@ -102,7 +122,7 @@ const useThreadListItemClient = (props: {
|
|
|
102
122
|
onArchive: () => void;
|
|
103
123
|
onUnarchive: () => void;
|
|
104
124
|
onDelete: () => void;
|
|
105
|
-
onGenerateTitle: () => void;
|
|
125
|
+
onGenerateTitle: (options?: { automatic?: boolean }) => void;
|
|
106
126
|
onInitialize: () => Promise<{
|
|
107
127
|
remoteId: string;
|
|
108
128
|
externalId: string | undefined;
|
|
@@ -202,9 +222,96 @@ const isSameThread = (
|
|
|
202
222
|
return data !== undefined && itemMatchesId(data, listState, right);
|
|
203
223
|
};
|
|
204
224
|
|
|
225
|
+
const useRemoteThreadBody = ({
|
|
226
|
+
id,
|
|
227
|
+
status,
|
|
228
|
+
remoteId,
|
|
229
|
+
item,
|
|
230
|
+
thread,
|
|
231
|
+
}: {
|
|
232
|
+
id: string;
|
|
233
|
+
status: RemoteThreadData["status"];
|
|
234
|
+
remoteId: string | undefined;
|
|
235
|
+
item: (isRunning: boolean) => ResourceElement<ClientOutput<"threadListItem">>;
|
|
236
|
+
thread: ResourceElement<ClientOutput<"thread">>;
|
|
237
|
+
}): ClientOutput<"thread"> => {
|
|
238
|
+
const parent = useAssistantContextValue();
|
|
239
|
+
const [isRunning, setIsRunning] = useState(false);
|
|
240
|
+
// The item client is mounted inside the body and served to the subtree
|
|
241
|
+
// through a Derived scope, which keeps the graft on the derived-only (no
|
|
242
|
+
// nested client host) path; resolving `parent.threads` here instead would
|
|
243
|
+
// read the threads scope during its own construction.
|
|
244
|
+
const itemHandle = useClientResource(item(isRunning));
|
|
245
|
+
const { client } = useConfiguredAui(parent, {
|
|
246
|
+
threadListItem: Derived({
|
|
247
|
+
source: "threads",
|
|
248
|
+
query: { type: "id", id },
|
|
249
|
+
get: () => itemHandle.methods,
|
|
250
|
+
}),
|
|
251
|
+
});
|
|
252
|
+
// Auto-title arms only for a thread born "new" in this body's lifetime;
|
|
253
|
+
// threads loaded or fetched from the adapter already carry their title. The
|
|
254
|
+
// settled (non-optimistic) initialize is what writes remoteId, so its
|
|
255
|
+
// arrival is the initialization signal.
|
|
256
|
+
const bornNewRef = useRef(status === "new");
|
|
257
|
+
const titleFiredRef = useRef(false);
|
|
258
|
+
return useAssistantContextProvider(client, function useBoundRemoteBody() {
|
|
259
|
+
const body = useClientResource(thread);
|
|
260
|
+
const bodyRunning = body.state.isRunning === true;
|
|
261
|
+
useEffect(() => {
|
|
262
|
+
setIsRunning(bodyRunning);
|
|
263
|
+
}, [bodyRunning]);
|
|
264
|
+
const armed =
|
|
265
|
+
bornNewRef.current && !titleFiredRef.current && remoteId !== undefined;
|
|
266
|
+
const hasTitleSource =
|
|
267
|
+
armed &&
|
|
268
|
+
(
|
|
269
|
+
body.state.messages as readonly {
|
|
270
|
+
status?: { type: string } | undefined;
|
|
271
|
+
}[]
|
|
272
|
+
).some(isTitleSourceMessage);
|
|
273
|
+
useEffect(() => {
|
|
274
|
+
if (!hasTitleSource || titleFiredRef.current) return;
|
|
275
|
+
titleFiredRef.current = true;
|
|
276
|
+
client.threadListItem.generateTitle({ automatic: true });
|
|
277
|
+
}, [hasTitleSource]);
|
|
278
|
+
return body.methods;
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const RemoteThreadBody = resource(useRemoteThreadBody);
|
|
283
|
+
|
|
284
|
+
// `thread("main")` keeps one identity across switches, like the single
|
|
285
|
+
// useClientResource slot it replaced: consumers (the ambient `thread` scope,
|
|
286
|
+
// captured clients) hold the facade while it delegates to whichever body is
|
|
287
|
+
// currently main. Publishing the target during commit keeps abandoned
|
|
288
|
+
// concurrent renders from changing the facade observed by committed consumers.
|
|
289
|
+
const useMainThreadFacade = (
|
|
290
|
+
current: ClientOutput<"thread">,
|
|
291
|
+
): ClientOutput<"thread"> => {
|
|
292
|
+
const currentRef = useRef(current);
|
|
293
|
+
useEffect(() => {
|
|
294
|
+
currentRef.current = current;
|
|
295
|
+
}, [current]);
|
|
296
|
+
const [facade] = useState(
|
|
297
|
+
() =>
|
|
298
|
+
new Proxy({} as ClientOutput<"thread">, {
|
|
299
|
+
get: (_, prop) =>
|
|
300
|
+
(currentRef.current as unknown as Record<PropertyKey, unknown>)[prop],
|
|
301
|
+
has: (_, prop) => prop in (currentRef.current as object),
|
|
302
|
+
ownKeys: () => Reflect.ownKeys(currentRef.current as object),
|
|
303
|
+
getOwnPropertyDescriptor: (_, prop) =>
|
|
304
|
+
Reflect.getOwnPropertyDescriptor(currentRef.current as object, prop),
|
|
305
|
+
}),
|
|
306
|
+
);
|
|
307
|
+
return facade;
|
|
308
|
+
};
|
|
309
|
+
|
|
205
310
|
const useRemoteThreadListView = ({
|
|
206
311
|
listState,
|
|
207
312
|
mainThreadId,
|
|
313
|
+
startedIds,
|
|
314
|
+
backgroundThreads,
|
|
208
315
|
threadFactory,
|
|
209
316
|
useAdapters,
|
|
210
317
|
onSwitchTo,
|
|
@@ -219,6 +326,8 @@ const useRemoteThreadListView = ({
|
|
|
219
326
|
}: {
|
|
220
327
|
listState: RemoteThreadState;
|
|
221
328
|
mainThreadId: string;
|
|
329
|
+
startedIds: readonly string[];
|
|
330
|
+
backgroundThreads: boolean;
|
|
222
331
|
threadFactory: RemoteThreadListProps["thread"];
|
|
223
332
|
useAdapters: RemoteThreadListAdapter["unstable_useAdapters"];
|
|
224
333
|
onSwitchTo: (
|
|
@@ -236,6 +345,7 @@ const useRemoteThreadListView = ({
|
|
|
236
345
|
onGenerateTitle: (
|
|
237
346
|
threadId: string,
|
|
238
347
|
messages: readonly ThreadMessage[] | undefined,
|
|
348
|
+
options?: { automatic?: boolean },
|
|
239
349
|
) => Promise<void>;
|
|
240
350
|
onInitialize: (threadId: string) => Promise<{
|
|
241
351
|
remoteId: string;
|
|
@@ -243,17 +353,103 @@ const useRemoteThreadListView = ({
|
|
|
243
353
|
}>;
|
|
244
354
|
onDetach: (threadId: string) => Promise<void>;
|
|
245
355
|
}) => {
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
356
|
+
const bodyIds = useMemo(() => {
|
|
357
|
+
if (!backgroundThreads) return [mainThreadId];
|
|
358
|
+
const seen = new Set<string>();
|
|
359
|
+
const seenRemoteIds = new Set<string>();
|
|
360
|
+
const ids: string[] = [];
|
|
361
|
+
for (const id of startedIds) {
|
|
362
|
+
const data = getThreadData(listState, id);
|
|
363
|
+
if (!data || seen.has(data.id)) continue;
|
|
364
|
+
if (data.remoteId !== undefined && seenRemoteIds.has(data.remoteId)) {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
seen.add(data.id);
|
|
368
|
+
if (data.remoteId !== undefined) seenRemoteIds.add(data.remoteId);
|
|
369
|
+
ids.push(data.id);
|
|
370
|
+
}
|
|
371
|
+
return ids;
|
|
372
|
+
}, [backgroundThreads, listState, mainThreadId, startedIds]);
|
|
373
|
+
|
|
374
|
+
const itemElementFor = (data: RemoteThreadData, isRunning: boolean) =>
|
|
375
|
+
ThreadListItemClient({
|
|
376
|
+
data,
|
|
377
|
+
isRunning,
|
|
378
|
+
onSwitchTo: (options) =>
|
|
379
|
+
handleThreadListAction("switch", () => onSwitchTo(data.id, options)),
|
|
380
|
+
onRename: (title) =>
|
|
381
|
+
handleThreadListAction("rename", () => onRename(data.id, title)),
|
|
382
|
+
onUpdateCustom: (custom) =>
|
|
383
|
+
handleThreadListAction("update custom metadata", () =>
|
|
384
|
+
onUpdateCustom(data.id, custom),
|
|
385
|
+
),
|
|
386
|
+
onArchive: () =>
|
|
387
|
+
handleThreadListAction("archive", () => onArchive(data.id)),
|
|
388
|
+
onUnarchive: () =>
|
|
389
|
+
handleThreadListAction("unarchive", () => onUnarchive(data.id)),
|
|
390
|
+
onDelete: () => handleThreadListAction("delete", () => onDelete(data.id)),
|
|
391
|
+
onGenerateTitle: (options) =>
|
|
392
|
+
handleThreadListAction("generate title", () =>
|
|
393
|
+
onGenerateTitle(
|
|
394
|
+
data.id,
|
|
395
|
+
(backgroundThreads
|
|
396
|
+
? bodyStateOf(data.id)?.messages
|
|
397
|
+
: mainThreadClient.state.messages) as
|
|
398
|
+
| readonly ThreadMessage[]
|
|
399
|
+
| undefined,
|
|
400
|
+
options,
|
|
401
|
+
),
|
|
402
|
+
),
|
|
403
|
+
onInitialize: () => onInitialize(data.id),
|
|
404
|
+
onDetach: () => handleThreadListAction("detach", () => onDetach(data.id)),
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const bodies = useClientLookup(
|
|
408
|
+
bodyIds.map((id) => {
|
|
409
|
+
const made = threadFactory(id);
|
|
410
|
+
// Background bodies never change occupant, so an unkeyed factory
|
|
411
|
+
// element is keyed by the mapping id here; per-body history loads
|
|
412
|
+
// without requiring the factory to key itself.
|
|
413
|
+
const thread =
|
|
414
|
+
backgroundThreads && made.key === undefined ? withKey(id, made) : made;
|
|
415
|
+
const wrapped =
|
|
416
|
+
useAdapters === undefined
|
|
417
|
+
? thread
|
|
418
|
+
: AdaptedRemoteThread({
|
|
419
|
+
useAdapters,
|
|
420
|
+
thread,
|
|
421
|
+
});
|
|
422
|
+
const data = getThreadData(listState, id);
|
|
423
|
+
const element =
|
|
424
|
+
backgroundThreads && data !== undefined
|
|
425
|
+
? RemoteThreadBody({
|
|
426
|
+
id,
|
|
427
|
+
status: data.status,
|
|
428
|
+
remoteId: data.remoteId,
|
|
429
|
+
item: (isRunning) => itemElementFor(data, isRunning),
|
|
430
|
+
thread: wrapped,
|
|
431
|
+
})
|
|
432
|
+
: wrapped;
|
|
433
|
+
return withKey(backgroundThreads ? id : (made.key ?? "main"), element);
|
|
434
|
+
}),
|
|
256
435
|
);
|
|
436
|
+
// A reload can remap a local thread id to its remote identity while the
|
|
437
|
+
// body stays keyed by the original mapping id, so lookups fall back to
|
|
438
|
+
// remote-identity matching.
|
|
439
|
+
const bodyIndexOf = (id: string) => {
|
|
440
|
+
const direct = bodyIds.indexOf(id);
|
|
441
|
+
if (direct !== -1) return direct;
|
|
442
|
+
return bodyIds.findIndex((bodyId) => isSameThread(listState, bodyId, id));
|
|
443
|
+
};
|
|
444
|
+
const bodyStateOf = (id: string) => {
|
|
445
|
+
const index = bodyIndexOf(id);
|
|
446
|
+
return index === -1 ? undefined : bodies.state[index];
|
|
447
|
+
};
|
|
448
|
+
const mainIndex = bodyIndexOf(mainThreadId);
|
|
449
|
+
const mainThreadClient = {
|
|
450
|
+
state: bodies.state[mainIndex]!,
|
|
451
|
+
methods: useMainThreadFacade(bodies.get({ index: mainIndex })),
|
|
452
|
+
};
|
|
257
453
|
const itemOrder = useMemo(
|
|
258
454
|
() => collectItemOrder(listState, mainThreadId),
|
|
259
455
|
[listState, mainThreadId],
|
|
@@ -262,40 +458,13 @@ const useRemoteThreadListView = ({
|
|
|
262
458
|
itemOrder.map((data) =>
|
|
263
459
|
withKey(
|
|
264
460
|
data.id,
|
|
265
|
-
|
|
461
|
+
itemElementFor(
|
|
266
462
|
data,
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
onSwitchTo(data.id, options),
|
|
273
|
-
),
|
|
274
|
-
onRename: (title) =>
|
|
275
|
-
handleThreadListAction("rename", () => onRename(data.id, title)),
|
|
276
|
-
onUpdateCustom: (custom) =>
|
|
277
|
-
handleThreadListAction("update custom metadata", () =>
|
|
278
|
-
onUpdateCustom(data.id, custom),
|
|
279
|
-
),
|
|
280
|
-
onArchive: () =>
|
|
281
|
-
handleThreadListAction("archive", () => onArchive(data.id)),
|
|
282
|
-
onUnarchive: () =>
|
|
283
|
-
handleThreadListAction("unarchive", () => onUnarchive(data.id)),
|
|
284
|
-
onDelete: () =>
|
|
285
|
-
handleThreadListAction("delete", () => onDelete(data.id)),
|
|
286
|
-
onGenerateTitle: () =>
|
|
287
|
-
handleThreadListAction("generate title", () =>
|
|
288
|
-
onGenerateTitle(
|
|
289
|
-
data.id,
|
|
290
|
-
mainThreadClient.state.messages as
|
|
291
|
-
| readonly ThreadMessage[]
|
|
292
|
-
| undefined,
|
|
293
|
-
),
|
|
294
|
-
),
|
|
295
|
-
onInitialize: () => onInitialize(data.id),
|
|
296
|
-
onDetach: () =>
|
|
297
|
-
handleThreadListAction("detach", () => onDetach(data.id)),
|
|
298
|
-
}),
|
|
463
|
+
backgroundThreads
|
|
464
|
+
? (bodyStateOf(data.id)?.isRunning ?? false)
|
|
465
|
+
: itemMatchesId(data, listState, mainThreadId) &&
|
|
466
|
+
mainThreadClient.state.isRunning,
|
|
467
|
+
),
|
|
299
468
|
),
|
|
300
469
|
),
|
|
301
470
|
);
|
|
@@ -324,6 +493,7 @@ const useRemoteThreadList = (
|
|
|
324
493
|
adapter,
|
|
325
494
|
adapterAtLoad: adapter,
|
|
326
495
|
adapterGeneration: 0,
|
|
496
|
+
titleStates: new Map<string, ThreadTitleState>(),
|
|
327
497
|
loadGeneration: 0,
|
|
328
498
|
switchGeneration: 0,
|
|
329
499
|
loadPromise: undefined as Promise<void> | undefined,
|
|
@@ -340,9 +510,6 @@ const useRemoteThreadList = (
|
|
|
340
510
|
};
|
|
341
511
|
});
|
|
342
512
|
|
|
343
|
-
// Config updates can invoke `reload()` in the same turn as the re-render, before the effect below runs.
|
|
344
|
-
session.adapter = adapter;
|
|
345
|
-
|
|
346
513
|
const listState = useSyncExternalStore(
|
|
347
514
|
(onStoreChange) => store.subscribe(onStoreChange),
|
|
348
515
|
() => store.value,
|
|
@@ -350,15 +517,21 @@ const useRemoteThreadList = (
|
|
|
350
517
|
);
|
|
351
518
|
|
|
352
519
|
const [mainThreadId, setMainThreadId] = useState(initialMainId);
|
|
520
|
+
const [startedIds, setStartedIds] = useState<readonly string[]>([
|
|
521
|
+
initialMainId,
|
|
522
|
+
]);
|
|
523
|
+
const [backgroundThreads] = useState(props.backgroundThreads === true);
|
|
353
524
|
const assignMainThreadId = useCallback(
|
|
354
525
|
(id: string) => {
|
|
355
526
|
session.mainThreadId = id;
|
|
356
527
|
setMainThreadId(id);
|
|
528
|
+
setStartedIds((prev) => (prev.includes(id) ? prev : [...prev, id]));
|
|
357
529
|
},
|
|
358
530
|
[session],
|
|
359
531
|
);
|
|
360
532
|
useThreadSelectionEvents(mainThreadId);
|
|
361
533
|
useEffect(() => {
|
|
534
|
+
// Publishing after commit keeps imperative actions on the adapter the committed tree renders with; an abandoned render must not reach them.
|
|
362
535
|
session.adapter = adapter;
|
|
363
536
|
session.mainThreadId = mainThreadId;
|
|
364
537
|
session.onThreadIdChange = onThreadIdChange;
|
|
@@ -390,30 +563,28 @@ const useRemoteThreadList = (
|
|
|
390
563
|
session.loadPromise = store
|
|
391
564
|
.optimisticUpdate({
|
|
392
565
|
execute: () => adapter.list(),
|
|
393
|
-
loading: (state) =>
|
|
566
|
+
loading: (state) => {
|
|
567
|
+
if (generation !== session.loadGeneration) return state;
|
|
568
|
+
return { ...state, isLoading: true, loadError: undefined };
|
|
569
|
+
},
|
|
394
570
|
then: (state, page) => {
|
|
395
571
|
if (generation !== session.loadGeneration) return state;
|
|
396
572
|
session.adapterAtLoad = adapter;
|
|
397
573
|
const fresh = classifyThreads(page.threads, {
|
|
398
574
|
threadIds: [],
|
|
399
575
|
archivedThreadIds: [],
|
|
400
|
-
threadIdMap: {},
|
|
401
|
-
threadData: {},
|
|
576
|
+
threadIdMap: { ...state.threadIdMap },
|
|
577
|
+
threadData: { ...state.threadData },
|
|
402
578
|
});
|
|
403
579
|
const merged = {
|
|
404
580
|
...state,
|
|
405
581
|
isLoading: false,
|
|
582
|
+
loadError: undefined,
|
|
406
583
|
cursor: normalizeCursor(page.nextCursor),
|
|
407
584
|
threadIds: fresh.threadIds,
|
|
408
585
|
archivedThreadIds: fresh.archivedThreadIds,
|
|
409
|
-
threadIdMap:
|
|
410
|
-
|
|
411
|
-
...fresh.threadIdMap,
|
|
412
|
-
},
|
|
413
|
-
threadData: {
|
|
414
|
-
...state.threadData,
|
|
415
|
-
...fresh.threadData,
|
|
416
|
-
},
|
|
586
|
+
threadIdMap: fresh.threadIdMap,
|
|
587
|
+
threadData: fresh.threadData,
|
|
417
588
|
};
|
|
418
589
|
return preserveMidLoadTransitions(merged, state, statusAtRequest);
|
|
419
590
|
},
|
|
@@ -425,6 +596,7 @@ const useRemoteThreadList = (
|
|
|
425
596
|
store.update({
|
|
426
597
|
...store.baseValue,
|
|
427
598
|
isLoading: false,
|
|
599
|
+
loadError: error,
|
|
428
600
|
});
|
|
429
601
|
})
|
|
430
602
|
.then(() => {});
|
|
@@ -438,12 +610,14 @@ const useRemoteThreadList = (
|
|
|
438
610
|
session.loadMorePromise = undefined;
|
|
439
611
|
if (adapterChanged) {
|
|
440
612
|
session.adapterGeneration++;
|
|
613
|
+
session.titleStates.clear();
|
|
441
614
|
session.switchGeneration++;
|
|
442
615
|
session.switchTask = undefined;
|
|
443
616
|
session.adapterAtLoad = adapter;
|
|
444
617
|
const seeded = seedNewThread(EMPTY_LIST);
|
|
445
618
|
store.reset({ ...seeded.state, isLoading: true });
|
|
446
619
|
assignMainThreadId(seeded.id);
|
|
620
|
+
setStartedIds([seeded.id]);
|
|
447
621
|
notifyRemoteId(undefined, true);
|
|
448
622
|
} else {
|
|
449
623
|
store.update({
|
|
@@ -461,6 +635,26 @@ const useRemoteThreadList = (
|
|
|
461
635
|
store,
|
|
462
636
|
]);
|
|
463
637
|
|
|
638
|
+
useEffect(() => {
|
|
639
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const retryAfterError = () => {
|
|
644
|
+
if (store.value.loadError !== undefined) void getLoadThreadsPromise();
|
|
645
|
+
};
|
|
646
|
+
const retryAfterVisible = () => {
|
|
647
|
+
if (document.visibilityState === "visible") retryAfterError();
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
window.addEventListener("online", retryAfterError);
|
|
651
|
+
document.addEventListener("visibilitychange", retryAfterVisible);
|
|
652
|
+
return () => {
|
|
653
|
+
window.removeEventListener("online", retryAfterError);
|
|
654
|
+
document.removeEventListener("visibilitychange", retryAfterVisible);
|
|
655
|
+
};
|
|
656
|
+
}, [getLoadThreadsPromise, store]);
|
|
657
|
+
|
|
464
658
|
useEffect(() => {
|
|
465
659
|
void getLoadThreadsPromise();
|
|
466
660
|
}, [getLoadThreadsPromise]);
|
|
@@ -486,7 +680,10 @@ const useRemoteThreadList = (
|
|
|
486
680
|
const task = store
|
|
487
681
|
.optimisticUpdate({
|
|
488
682
|
execute: () => currentAdapter.list({ after: cursor }),
|
|
489
|
-
loading: (state) =>
|
|
683
|
+
loading: (state) => {
|
|
684
|
+
if (generation !== session.loadGeneration) return state;
|
|
685
|
+
return { ...state, isLoadingMore: true };
|
|
686
|
+
},
|
|
490
687
|
then: (state, page) => {
|
|
491
688
|
if (generation !== session.loadGeneration) return state;
|
|
492
689
|
const appended = classifyThreads(page.threads, {
|
|
@@ -604,22 +801,30 @@ const useRemoteThreadList = (
|
|
|
604
801
|
throw threadNotFoundError(threadIdOrRemoteId, "switching to it");
|
|
605
802
|
}
|
|
606
803
|
if (isSameThread(store.value, data.id, session.mainThreadId)) return;
|
|
607
|
-
|
|
608
|
-
|
|
804
|
+
|
|
805
|
+
const targetId = data.id;
|
|
806
|
+
let current: RemoteThreadData | undefined = data;
|
|
807
|
+
|
|
808
|
+
if (current.status === "archived" && options?.unarchive !== false) {
|
|
609
809
|
const { remoteId } = await current.initializeTask;
|
|
610
810
|
if (generation !== session.switchGeneration) return;
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
811
|
+
current = getThreadData(store.value, targetId);
|
|
812
|
+
if (current?.id !== targetId) return;
|
|
813
|
+
if (current.status === "archived") {
|
|
814
|
+
await store.optimisticUpdate({
|
|
815
|
+
execute: () => session.adapter.unarchive(remoteId),
|
|
816
|
+
optimistic: (state) =>
|
|
817
|
+
updateStatusReducer(state, targetId, "regular"),
|
|
818
|
+
});
|
|
819
|
+
if (generation !== session.switchGeneration) return;
|
|
820
|
+
current = getThreadData(store.value, targetId);
|
|
821
|
+
if (current?.id !== targetId) return;
|
|
822
|
+
}
|
|
618
823
|
}
|
|
619
824
|
if (generation !== session.switchGeneration) return;
|
|
620
|
-
assignMainThreadId(
|
|
621
|
-
notifyRemoteId(
|
|
622
|
-
session.onSwitchToThread?.(
|
|
825
|
+
assignMainThreadId(current.id);
|
|
826
|
+
notifyRemoteId(current.remoteId, emitThreadIdChange);
|
|
827
|
+
session.onSwitchToThread?.(current.id);
|
|
623
828
|
}),
|
|
624
829
|
[assignMainThreadId, notifyRemoteId, session, startSwitch, store],
|
|
625
830
|
);
|
|
@@ -704,55 +909,60 @@ const useRemoteThreadList = (
|
|
|
704
909
|
requireAdapterGeneration(adapterGeneration);
|
|
705
910
|
return result;
|
|
706
911
|
}
|
|
912
|
+
requireAdapterGeneration(adapterGeneration);
|
|
913
|
+
const initializeTask = currentAdapter.initialize(threadId);
|
|
914
|
+
let removedMappingId: string | undefined;
|
|
915
|
+
let replacementMainThreadId: string | undefined;
|
|
707
916
|
const result = await store.optimisticUpdate({
|
|
708
|
-
execute: () =>
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
},
|
|
712
|
-
optimistic: (state) => updateStatusReducer(state, threadId, "regular"),
|
|
713
|
-
loading: (state, task) => {
|
|
714
|
-
const mappingId = createThreadMappingId(threadId);
|
|
715
|
-
return {
|
|
716
|
-
...state,
|
|
717
|
-
threadData: {
|
|
718
|
-
...state.threadData,
|
|
719
|
-
[mappingId]: {
|
|
720
|
-
...state.threadData[mappingId],
|
|
721
|
-
initializeTask: task,
|
|
722
|
-
},
|
|
723
|
-
},
|
|
724
|
-
};
|
|
725
|
-
},
|
|
917
|
+
execute: () => initializeTask,
|
|
918
|
+
optimistic: (state) =>
|
|
919
|
+
promoteNewThreadReducer(state, threadId, initializeTask),
|
|
726
920
|
then: (state, { remoteId, externalId }) => {
|
|
727
921
|
if (adapterGeneration !== session.adapterGeneration) return state;
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
},
|
|
746
|
-
};
|
|
922
|
+
// Background mode still owns the initializing body. Single-body mode
|
|
923
|
+
// has already replaced it, so retain the currently mounted body.
|
|
924
|
+
const retainedThreadId = backgroundThreads
|
|
925
|
+
? threadId
|
|
926
|
+
: session.mainThreadId;
|
|
927
|
+
const reconciliation = reconcileInitializedThread(
|
|
928
|
+
state,
|
|
929
|
+
threadId,
|
|
930
|
+
remoteId,
|
|
931
|
+
externalId,
|
|
932
|
+
retainedThreadId,
|
|
933
|
+
);
|
|
934
|
+
removedMappingId = reconciliation.removedMappingId;
|
|
935
|
+
if (removedMappingId === session.mainThreadId) {
|
|
936
|
+
replacementMainThreadId = reconciliation.survivorMappingId;
|
|
937
|
+
}
|
|
938
|
+
return reconciliation.state;
|
|
747
939
|
},
|
|
748
940
|
});
|
|
749
941
|
requireAdapterGeneration(adapterGeneration);
|
|
942
|
+
if (removedMappingId !== undefined) {
|
|
943
|
+
setStartedIds((prev) =>
|
|
944
|
+
prev.filter((startedId) => startedId !== removedMappingId),
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
if (
|
|
948
|
+
replacementMainThreadId !== undefined &&
|
|
949
|
+
session.mainThreadId === removedMappingId
|
|
950
|
+
) {
|
|
951
|
+
assignMainThreadId(replacementMainThreadId);
|
|
952
|
+
}
|
|
750
953
|
if (threadId === session.mainThreadId) {
|
|
751
954
|
notifyRemoteId(result.remoteId, true);
|
|
752
955
|
}
|
|
753
956
|
return toInitializeResult(result);
|
|
754
957
|
},
|
|
755
|
-
[
|
|
958
|
+
[
|
|
959
|
+
assignMainThreadId,
|
|
960
|
+
backgroundThreads,
|
|
961
|
+
notifyRemoteId,
|
|
962
|
+
requireAdapterGeneration,
|
|
963
|
+
session,
|
|
964
|
+
store,
|
|
965
|
+
],
|
|
756
966
|
);
|
|
757
967
|
|
|
758
968
|
const rename = useCallback(
|
|
@@ -764,27 +974,43 @@ const useRemoteThreadList = (
|
|
|
764
974
|
if (data.status === "new") {
|
|
765
975
|
throw threadStatusError(threadIdOrRemoteId, data.status, "be renamed");
|
|
766
976
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
977
|
+
const claim = startThreadTitleRename(
|
|
978
|
+
session.titleStates,
|
|
979
|
+
data.id,
|
|
980
|
+
newTitle,
|
|
981
|
+
);
|
|
982
|
+
return store
|
|
983
|
+
.optimisticUpdate({
|
|
984
|
+
execute: async () => {
|
|
985
|
+
const { remoteId } = await data.initializeTask;
|
|
986
|
+
requireAdapterGeneration(adapterGeneration);
|
|
987
|
+
return currentAdapter.rename(remoteId, newTitle);
|
|
988
|
+
},
|
|
989
|
+
optimistic: (state) => {
|
|
990
|
+
const current = getThreadData(state, threadIdOrRemoteId);
|
|
991
|
+
if (!current) return state;
|
|
992
|
+
return {
|
|
993
|
+
...state,
|
|
994
|
+
threadData: {
|
|
995
|
+
...state.threadData,
|
|
996
|
+
[current.id]: {
|
|
997
|
+
...current,
|
|
998
|
+
title: newTitle,
|
|
999
|
+
},
|
|
783
1000
|
},
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
|
|
1001
|
+
};
|
|
1002
|
+
},
|
|
1003
|
+
})
|
|
1004
|
+
.then(
|
|
1005
|
+
(result) => {
|
|
1006
|
+
finishThreadTitleRename(session.titleStates, data.id, claim, true);
|
|
1007
|
+
return result;
|
|
1008
|
+
},
|
|
1009
|
+
(error: unknown) => {
|
|
1010
|
+
finishThreadTitleRename(session.titleStates, data.id, claim, false);
|
|
1011
|
+
throw error;
|
|
1012
|
+
},
|
|
1013
|
+
);
|
|
788
1014
|
},
|
|
789
1015
|
[requireAdapterGeneration, session, store],
|
|
790
1016
|
);
|
|
@@ -912,8 +1138,7 @@ const useRemoteThreadList = (
|
|
|
912
1138
|
}
|
|
913
1139
|
await ensureNotMain(data.id);
|
|
914
1140
|
requireAdapterGeneration(adapterGeneration);
|
|
915
|
-
|
|
916
|
-
return store.optimisticUpdate({
|
|
1141
|
+
const result = await store.optimisticUpdate({
|
|
917
1142
|
execute: async () => {
|
|
918
1143
|
const { remoteId } = await data.initializeTask;
|
|
919
1144
|
requireAdapterGeneration(adapterGeneration);
|
|
@@ -921,6 +1146,13 @@ const useRemoteThreadList = (
|
|
|
921
1146
|
},
|
|
922
1147
|
optimistic: (state) => updateStatusReducer(state, data.id, "deleted"),
|
|
923
1148
|
});
|
|
1149
|
+
// An adapter swap resets the optimistic layer, and a listed thread's slot
|
|
1150
|
+
// id is its remote id, so a replacement adapter can re-list this slot
|
|
1151
|
+
// while the deletion is in flight.
|
|
1152
|
+
if (getThreadData(store.value, data.id) !== undefined) return result;
|
|
1153
|
+
clearThreadTitleState(session.titleStates, data.id);
|
|
1154
|
+
onDelete?.(data.id);
|
|
1155
|
+
return result;
|
|
924
1156
|
},
|
|
925
1157
|
[ensureNotMain, onDelete, requireAdapterGeneration, session, store],
|
|
926
1158
|
);
|
|
@@ -929,6 +1161,7 @@ const useRemoteThreadList = (
|
|
|
929
1161
|
async (
|
|
930
1162
|
threadIdOrRemoteId: string,
|
|
931
1163
|
messages: readonly ThreadMessage[] | undefined,
|
|
1164
|
+
options?: { automatic?: boolean },
|
|
932
1165
|
) => {
|
|
933
1166
|
const currentAdapter = session.adapter;
|
|
934
1167
|
const adapterGeneration = session.adapterGeneration;
|
|
@@ -945,38 +1178,65 @@ const useRemoteThreadList = (
|
|
|
945
1178
|
}
|
|
946
1179
|
const { remoteId } = await data.initializeTask;
|
|
947
1180
|
requireAdapterGeneration(adapterGeneration);
|
|
948
|
-
if (
|
|
1181
|
+
if (
|
|
1182
|
+
!backgroundThreads &&
|
|
1183
|
+
!isSameThread(store.value, data.id, session.mainThreadId)
|
|
1184
|
+
) {
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
949
1187
|
if (!messages) return;
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1188
|
+
await runThreadTitleGeneration({
|
|
1189
|
+
states: session.titleStates,
|
|
1190
|
+
threadId: data.id,
|
|
1191
|
+
automatic: options?.automatic === true,
|
|
1192
|
+
generate: async (onTitle) => {
|
|
1193
|
+
const stream = await currentAdapter.generateTitle(remoteId, messages);
|
|
1194
|
+
requireAdapterGeneration(adapterGeneration);
|
|
1195
|
+
await applyTitleStream(stream, onTitle);
|
|
1196
|
+
},
|
|
1197
|
+
rename: async (title) => {
|
|
1198
|
+
requireAdapterGeneration(adapterGeneration);
|
|
1199
|
+
await currentAdapter.rename(remoteId, title);
|
|
1200
|
+
},
|
|
1201
|
+
applyTitle: async (title) => {
|
|
1202
|
+
await store.optimisticUpdate({
|
|
1203
|
+
execute: async () => {},
|
|
1204
|
+
optimistic: (state) => {
|
|
1205
|
+
if (adapterGeneration !== session.adapterGeneration) return state;
|
|
1206
|
+
const current = getThreadData(state, data.id);
|
|
1207
|
+
if (!current) return state;
|
|
1208
|
+
return {
|
|
1209
|
+
...state,
|
|
1210
|
+
threadData: {
|
|
1211
|
+
...state.threadData,
|
|
1212
|
+
[current.id]: {
|
|
1213
|
+
...current,
|
|
1214
|
+
title,
|
|
1215
|
+
},
|
|
966
1216
|
},
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
}
|
|
1217
|
+
};
|
|
1218
|
+
},
|
|
1219
|
+
});
|
|
1220
|
+
},
|
|
971
1221
|
});
|
|
972
1222
|
},
|
|
973
|
-
[requireAdapterGeneration, session, store],
|
|
1223
|
+
[backgroundThreads, requireAdapterGeneration, session, store],
|
|
1224
|
+
);
|
|
1225
|
+
|
|
1226
|
+
const detach = useCallback(
|
|
1227
|
+
async (threadId: string) => {
|
|
1228
|
+
await ensureNotMain(threadId);
|
|
1229
|
+
setStartedIds((prev) => prev.filter((id) => id !== threadId));
|
|
1230
|
+
},
|
|
1231
|
+
[ensureNotMain],
|
|
974
1232
|
);
|
|
975
1233
|
|
|
976
1234
|
const { mainThreadClient, itemOrder, threadListItems } =
|
|
977
1235
|
useRemoteThreadListView({
|
|
978
1236
|
listState,
|
|
979
1237
|
mainThreadId,
|
|
1238
|
+
startedIds,
|
|
1239
|
+
backgroundThreads,
|
|
980
1240
|
threadFactory,
|
|
981
1241
|
useAdapters: adapter.unstable_useAdapters,
|
|
982
1242
|
onSwitchTo: (id, options) => switchToThread(id, options),
|
|
@@ -985,9 +1245,10 @@ const useRemoteThreadList = (
|
|
|
985
1245
|
onArchive: (id) => archive(id),
|
|
986
1246
|
onUnarchive: (id) => unarchive(id),
|
|
987
1247
|
onDelete: (id) => deleteThread(id),
|
|
988
|
-
onGenerateTitle: (id, messages) =>
|
|
1248
|
+
onGenerateTitle: (id, messages, options) =>
|
|
1249
|
+
generateTitle(id, messages, options),
|
|
989
1250
|
onInitialize: async (id) => toInitializeResult(await initialize(id)),
|
|
990
|
-
onDetach: (id) =>
|
|
1251
|
+
onDetach: (id) => detach(id),
|
|
991
1252
|
});
|
|
992
1253
|
|
|
993
1254
|
const mainRemoteId = getThreadData(listState, mainThreadId)?.remoteId;
|
|
@@ -1023,6 +1284,7 @@ const useRemoteThreadList = (
|
|
|
1023
1284
|
mainThreadId,
|
|
1024
1285
|
newThreadId: listState.newThreadId ?? null,
|
|
1025
1286
|
isLoading: listState.isLoading,
|
|
1287
|
+
loadError: listState.loadError,
|
|
1026
1288
|
isLoadingMore: listState.isLoadingMore,
|
|
1027
1289
|
hasMore: listState.cursor !== undefined,
|
|
1028
1290
|
threadIds: listState.threadIds,
|
|
@@ -1034,6 +1296,7 @@ const useRemoteThreadList = (
|
|
|
1034
1296
|
listState.archivedThreadIds,
|
|
1035
1297
|
listState.cursor,
|
|
1036
1298
|
listState.isLoading,
|
|
1299
|
+
listState.loadError,
|
|
1037
1300
|
listState.isLoadingMore,
|
|
1038
1301
|
listState.newThreadId,
|
|
1039
1302
|
listState.threadIds,
|
|
@@ -1098,6 +1361,9 @@ const useRemoteThreadList = (
|
|
|
1098
1361
|
* and attachments come from `unstable_useAdapters`. `useRemoteThreadListRuntime`
|
|
1099
1362
|
* uses the same hook when `unstable_Provider` is omitted. Key the factory
|
|
1100
1363
|
* with `withKey` so history reloads when the visible thread changes.
|
|
1364
|
+
* With `backgroundThreads`, every visited thread stays mounted: runs continue
|
|
1365
|
+
* across switches, per-item `isRunning` is live, and freshly initialized
|
|
1366
|
+
* threads title themselves.
|
|
1101
1367
|
*/
|
|
1102
1368
|
export const RemoteThreadList = resource(useRemoteThreadList);
|
|
1103
1369
|
|