@assistant-ui/core 0.3.15 → 0.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/internal.d.ts +7 -2
- package/dist/internal.js +8 -3
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +3 -2
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/frame/provider.d.ts +2 -0
- package/dist/model-context/frame/provider.d.ts.map +1 -1
- package/dist/model-context/frame/provider.js +88 -25
- package/dist/model-context/frame/provider.js.map +1 -1
- package/dist/model-context/interactable-composer-metadata.d.ts +1 -1
- package/dist/model-context/interactable-composer-metadata.js +1 -1
- package/dist/model-context/interactable-composer-metadata.js.map +1 -1
- package/dist/model-context/registry.d.ts +2 -2
- package/dist/model-context/registry.d.ts.map +1 -1
- package/dist/model-context/registry.js +9 -28
- package/dist/model-context/registry.js.map +1 -1
- package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
- package/dist/react/client/InMemoryThreadList.js +24 -20
- package/dist/react/client/InMemoryThreadList.js.map +1 -1
- package/dist/react/client/Interactables.d.ts.map +1 -1
- package/dist/react/client/Interactables.js +59 -177
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
- package/dist/react/client/RemoteThreadList.js +23 -56
- package/dist/react/client/RemoteThreadList.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
- package/dist/react/interactables-legacy/Interactables.js +56 -174
- package/dist/react/interactables-legacy/Interactables.js.map +1 -1
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +24 -0
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -0
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +138 -0
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -0
- package/dist/react/primitives/composer/ComposerIf.js.map +1 -1
- package/dist/react/primitives/composer/ComposerQueue.d.ts +2 -1
- package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageParts.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +23 -55
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/runManager.js +2 -12
- package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +3 -10
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +20 -67
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +12 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js +10 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js.map +1 -0
- package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -1
- package/dist/react/runtimes/createMessageConverter.d.ts +2 -1
- package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.d.ts +6 -25
- package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.js +33 -233
- package/dist/react/runtimes/external-message-converter.js.map +1 -1
- package/dist/runtime/api/composer-runtime.d.ts +1 -1
- package/dist/runtime/api/composer-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +1 -2
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +14 -29
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -4
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +3 -9
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.js +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
- package/dist/runtime/interfaces/composer-runtime-core.d.ts +1 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts +1 -1
- package/dist/runtime/internal.d.ts +2 -1
- package/dist/runtime/internal.js +2 -1
- package/dist/runtime/queue/external-thread-queue-adapter.d.ts +1 -1
- package/dist/runtime/queue/message-queue.d.ts.map +1 -1
- package/dist/runtime/queue/message-queue.js +1 -1
- package/dist/runtime/queue/message-queue.js.map +1 -1
- package/dist/runtime/queue/queue-item.d.ts +12 -0
- package/dist/runtime/queue/queue-item.d.ts.map +1 -0
- package/dist/runtime/queue/queue-item.js +6 -0
- package/dist/runtime/queue/queue-item.js.map +1 -0
- package/dist/runtime/utils/abortable-thread-load.d.ts +15 -0
- package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -0
- package/dist/runtime/utils/abortable-thread-load.js +35 -0
- package/dist/runtime/utils/abortable-thread-load.js.map +1 -0
- package/dist/runtime/utils/attachment-add-operations.d.ts +18 -0
- package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -0
- package/dist/runtime/utils/attachment-add-operations.js +43 -0
- package/dist/runtime/utils/attachment-add-operations.js.map +1 -0
- package/dist/runtime/utils/auto-status.d.ts +6 -1
- package/dist/runtime/utils/auto-status.d.ts.map +1 -1
- package/dist/runtime/utils/auto-status.js +7 -1
- package/dist/runtime/utils/auto-status.js.map +1 -1
- package/dist/runtime/utils/external-message-conversion.d.ts +47 -0
- package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -0
- package/dist/runtime/utils/external-message-conversion.js +221 -0
- package/dist/runtime/utils/external-message-conversion.js.map +1 -0
- package/dist/runtime/utils/external-store-message.d.ts +2 -1
- package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
- package/dist/runtime/utils/external-store-message.js +2 -1
- package/dist/runtime/utils/external-store-message.js.map +1 -1
- package/dist/runtime/utils/message-repository-session.d.ts +30 -0
- package/dist/runtime/utils/message-repository-session.d.ts.map +1 -0
- package/dist/runtime/utils/message-repository-session.js +130 -0
- package/dist/runtime/utils/message-repository-session.js.map +1 -0
- package/dist/runtime/utils/message-repository.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.js +3 -4
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtime/utils/pending-tool-calls.d.ts +20 -0
- package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -0
- package/dist/runtime/utils/pending-tool-calls.js +22 -0
- package/dist/runtime/utils/pending-tool-calls.js.map +1 -0
- package/dist/runtime/utils/thread-message-like.d.ts +2 -1
- package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +2 -5
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +3 -10
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +19 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js +122 -20
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +20 -2
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.js +77 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -0
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
- package/dist/store/clients/external-thread.d.ts.map +1 -1
- package/dist/store/clients/external-thread.js +74 -99
- package/dist/store/clients/external-thread.js.map +1 -1
- package/dist/store/clients/single-thread-list.d.ts.map +1 -1
- package/dist/store/clients/single-thread-list.js +1 -1
- package/dist/store/clients/single-thread-list.js.map +1 -1
- package/dist/store/clients/suggestions.d.ts.map +1 -1
- package/dist/store/clients/suggestions.js +16 -7
- package/dist/store/clients/suggestions.js.map +1 -1
- package/dist/store/index.d.ts +4 -2
- package/dist/store/scope-registration.d.ts +1 -1
- package/dist/store/scopes/composer.d.ts +2 -1
- package/dist/store/scopes/composer.d.ts.map +1 -1
- package/dist/store/scopes/message.d.ts +1 -1
- package/dist/store/scopes/queue-item.d.ts +2 -9
- package/dist/store/scopes/queue-item.d.ts.map +1 -1
- package/dist/store/scopes/queue-item.js +0 -6
- package/dist/types/message.d.ts +7 -3
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js.map +1 -1
- package/dist/utils/data-url.d.ts +14 -1
- package/dist/utils/data-url.d.ts.map +1 -1
- package/dist/utils/data-url.js +13 -1
- package/dist/utils/data-url.js.map +1 -1
- package/dist/utils/invoke-user-callback.d.ts +5 -0
- package/dist/utils/invoke-user-callback.d.ts.map +1 -0
- package/dist/utils/invoke-user-callback.js +19 -0
- package/dist/utils/invoke-user-callback.js.map +1 -0
- package/dist/utils/normalizePartStatus.js.map +1 -1
- package/package.json +10 -10
- package/src/internal.ts +12 -0
- package/src/model-context/frame/host.test.ts +91 -7
- package/src/model-context/frame/host.ts +7 -2
- package/src/model-context/frame/provider.test.ts +267 -12
- package/src/model-context/frame/provider.ts +113 -31
- package/src/model-context/interactable-composer-metadata.ts +1 -1
- package/src/model-context/registry.test.ts +44 -0
- package/src/model-context/registry.ts +13 -35
- package/src/react/client/InMemoryThreadList.test.tsx +49 -0
- package/src/react/client/InMemoryThreadList.ts +7 -1
- package/src/react/client/Interactables.ts +19 -178
- package/src/react/client/RemoteThreadList.test.ts +96 -0
- package/src/react/client/RemoteThreadList.ts +27 -59
- package/src/react/interactables-legacy/Interactables.ts +20 -177
- package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +161 -0
- package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +206 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +26 -56
- package/src/react/runtimes/assistant-transport/runManager.ts +4 -17
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +92 -0
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +40 -141
- package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.test.ts +37 -0
- package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.ts +18 -0
- package/src/react/runtimes/external-message-converter.test.ts +0 -14
- package/src/react/runtimes/external-message-converter.test.tsx +77 -0
- package/src/react/runtimes/external-message-converter.ts +58 -481
- package/src/runtime/api/composer-runtime.ts +1 -1
- package/src/runtime/api/message-runtime.test.ts +28 -1
- package/src/runtime/base/base-composer-runtime-core.ts +20 -40
- package/src/runtime/base/base-thread-runtime-core.test.ts +16 -0
- package/src/runtime/base/base-thread-runtime-core.ts +6 -11
- package/src/runtime/base/default-thread-composer-runtime-core.ts +1 -4
- package/src/runtime/interfaces/composer-runtime-core.ts +1 -1
- package/src/runtime/interfaces/thread-runtime-core.ts +1 -1
- package/src/runtime/internal.ts +5 -0
- package/src/runtime/queue/external-thread-queue-adapter.ts +1 -1
- package/src/runtime/queue/message-queue.ts +1 -4
- package/src/runtime/queue/queue-item.ts +10 -0
- package/src/runtime/utils/abortable-thread-load.test.ts +87 -0
- package/src/runtime/utils/abortable-thread-load.ts +53 -0
- package/src/runtime/utils/attachment-add-operations.ts +59 -0
- package/src/runtime/utils/auto-status.ts +33 -0
- package/src/runtime/utils/external-message-conversion.test.ts +113 -0
- package/src/runtime/utils/external-message-conversion.ts +494 -0
- package/src/runtime/utils/external-store-message.ts +2 -0
- package/src/runtime/utils/message-repository-session.test.ts +195 -0
- package/src/runtime/utils/message-repository-session.ts +176 -0
- package/src/runtime/utils/message-repository.ts +7 -4
- package/src/runtime/utils/pending-tool-calls.test.ts +57 -0
- package/src/runtime/utils/pending-tool-calls.ts +35 -0
- package/src/runtime/utils/thread-message-like.ts +2 -0
- package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +6 -14
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +175 -28
- package/src/runtimes/local/local-thread-runtime-core.test.ts +172 -1
- package/src/runtimes/local/local-thread-runtime-core.ts +48 -3
- package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +37 -0
- package/src/runtimes/remote-thread-list/remote-thread-state.ts +123 -0
- package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -0
- package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +140 -0
- package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +3 -0
- package/src/store/clients/external-thread.ts +16 -61
- package/src/store/clients/single-thread-list.ts +4 -1
- package/src/store/clients/suggestions.test.ts +31 -0
- package/src/store/clients/suggestions.ts +18 -19
- package/src/store/scopes/message.ts +1 -1
- package/src/store/scopes/queue-item.ts +2 -9
- package/src/tests/MessageRepository.test.ts +77 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +194 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +158 -0
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +164 -0
- package/src/tests/external-store-thread-runtime-core.test.ts +642 -0
- package/src/tests/external-thread-attachments.test.tsx +173 -0
- package/src/tests/single-thread-list-item.test.tsx +47 -0
- package/src/tests/thread-message-like.test.ts +39 -1
- package/src/types/message.ts +8 -3
- package/src/utils/data-url.test.ts +52 -1
- package/src/utils/data-url.ts +21 -0
- package/src/utils/invoke-user-callback.test.ts +82 -0
- package/src/utils/invoke-user-callback.ts +31 -0
- package/src/utils/normalizePartStatus.ts +1 -1
- package/dist/store/scopes/queue-item.js.map +0 -1
|
@@ -2,12 +2,12 @@ import type { Tool } from "assistant-stream";
|
|
|
2
2
|
import {
|
|
3
3
|
type ModelContext,
|
|
4
4
|
type ModelContextProvider,
|
|
5
|
-
mergeModelContexts,
|
|
6
5
|
type AssistantToolProps,
|
|
7
6
|
type AssistantInstructionsConfig,
|
|
8
7
|
} from "./types";
|
|
9
|
-
import type { Unsubscribe } from "../types/unsubscribe";
|
|
10
8
|
import { notifySubscribers as notifyStateSubscribers } from "../subscribable/subscribable";
|
|
9
|
+
import { CompositeContextProvider } from "../utils/composite-context-provider";
|
|
10
|
+
import type { Unsubscribe } from "../types/unsubscribe";
|
|
11
11
|
import type {
|
|
12
12
|
ModelContextRegistryToolHandle,
|
|
13
13
|
ModelContextRegistryInstructionHandle,
|
|
@@ -17,9 +17,14 @@ import type {
|
|
|
17
17
|
export class ModelContextRegistry implements ModelContextProvider {
|
|
18
18
|
private _tools = new Map<symbol, AssistantToolProps<any, any>>();
|
|
19
19
|
private _instructions = new Map<symbol, string>();
|
|
20
|
-
private
|
|
20
|
+
private _contextProviders = new CompositeContextProvider();
|
|
21
21
|
private _subscribers = new Set<() => void>();
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
this._contextProviders.subscribe(() => {
|
|
25
|
+
this.notifySubscribers();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
23
28
|
|
|
24
29
|
getModelContext(): ModelContext {
|
|
25
30
|
const instructions = Array.from(this._instructions.values()).filter(
|
|
@@ -35,9 +40,7 @@ export class ModelContextRegistry implements ModelContextProvider {
|
|
|
35
40
|
tools[toolName] = tool;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
const providerContexts =
|
|
39
|
-
new Set(this._providers.values()),
|
|
40
|
-
);
|
|
43
|
+
const providerContexts = this._contextProviders.getModelContext();
|
|
41
44
|
|
|
42
45
|
const context: ModelContext = {
|
|
43
46
|
system,
|
|
@@ -139,36 +142,11 @@ export class ModelContextRegistry implements ModelContextProvider {
|
|
|
139
142
|
addProvider(
|
|
140
143
|
provider: ModelContextProvider,
|
|
141
144
|
): ModelContextRegistryProviderHandle {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
this._providers.set(id, provider);
|
|
145
|
-
|
|
146
|
-
let unsubscribe: Unsubscribe | undefined;
|
|
147
|
-
try {
|
|
148
|
-
unsubscribe = provider.subscribe?.(() => {
|
|
149
|
-
this.notifySubscribers();
|
|
150
|
-
});
|
|
151
|
-
} catch (error) {
|
|
152
|
-
this._providers.delete(id);
|
|
153
|
-
try {
|
|
154
|
-
this.notifySubscribers();
|
|
155
|
-
} catch (notifyError) {
|
|
156
|
-
console.error(notifyError);
|
|
157
|
-
}
|
|
158
|
-
throw error;
|
|
159
|
-
}
|
|
160
|
-
this._providerUnsubscribes.set(id, unsubscribe);
|
|
161
|
-
|
|
162
|
-
this.notifySubscribers();
|
|
145
|
+
const unregister =
|
|
146
|
+
this._contextProviders.registerModelContextProvider(provider);
|
|
163
147
|
|
|
164
148
|
return {
|
|
165
|
-
remove:
|
|
166
|
-
this._providers.delete(id);
|
|
167
|
-
const unsubscribe = this._providerUnsubscribes.get(id);
|
|
168
|
-
unsubscribe?.();
|
|
169
|
-
this._providerUnsubscribes.delete(id);
|
|
170
|
-
this.notifySubscribers();
|
|
171
|
-
},
|
|
149
|
+
remove: unregister,
|
|
172
150
|
};
|
|
173
151
|
}
|
|
174
152
|
}
|
|
@@ -146,3 +146,52 @@ describe("InMemoryThreadList delete", () => {
|
|
|
146
146
|
expect(state.mainThreadId).not.toBe("main");
|
|
147
147
|
});
|
|
148
148
|
});
|
|
149
|
+
|
|
150
|
+
describe("InMemoryThreadList item index selectors", () => {
|
|
151
|
+
it("resolves index selectors within the archived and regular subsets", async () => {
|
|
152
|
+
const { getAui } = setup();
|
|
153
|
+
await act(async () => {});
|
|
154
|
+
|
|
155
|
+
await act(async () => {
|
|
156
|
+
getAui().threads.switchToNewThread();
|
|
157
|
+
});
|
|
158
|
+
await act(async () => {});
|
|
159
|
+
const b = getAui().threads.getState().mainThreadId;
|
|
160
|
+
|
|
161
|
+
await act(async () => {
|
|
162
|
+
getAui().threads.switchToNewThread();
|
|
163
|
+
});
|
|
164
|
+
await act(async () => {});
|
|
165
|
+
|
|
166
|
+
await act(async () => {
|
|
167
|
+
getAui().threads.item({ id: b }).archive();
|
|
168
|
+
});
|
|
169
|
+
await act(async () => {});
|
|
170
|
+
|
|
171
|
+
const state = getAui().threads.getState();
|
|
172
|
+
expect(state.archivedThreadIds).toEqual([b]);
|
|
173
|
+
expect(state.threadIds).toHaveLength(2);
|
|
174
|
+
|
|
175
|
+
for (const [index, id] of state.archivedThreadIds.entries()) {
|
|
176
|
+
expect(
|
|
177
|
+
getAui().threads.item({ index, archived: true }).getState().id,
|
|
178
|
+
).toBe(id);
|
|
179
|
+
}
|
|
180
|
+
for (const [index, id] of state.threadIds.entries()) {
|
|
181
|
+
expect(getAui().threads.item({ index }).getState().id).toBe(id);
|
|
182
|
+
expect(
|
|
183
|
+
getAui().threads.item({ index, archived: false }).getState().id,
|
|
184
|
+
).toBe(id);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
expect(() =>
|
|
188
|
+
getAui().threads.item({ index: state.threadIds.length }),
|
|
189
|
+
).toThrow("out of bounds");
|
|
190
|
+
expect(() =>
|
|
191
|
+
getAui().threads.item({
|
|
192
|
+
index: state.archivedThreadIds.length,
|
|
193
|
+
archived: true,
|
|
194
|
+
}),
|
|
195
|
+
).toThrow("out of bounds");
|
|
196
|
+
});
|
|
197
|
+
});
|
|
@@ -236,7 +236,13 @@ const useInMemoryThreadList = (
|
|
|
236
236
|
const index = threads.findIndex((t) => t.id === selector.id);
|
|
237
237
|
return threadListItems.get({ index });
|
|
238
238
|
}
|
|
239
|
-
|
|
239
|
+
// The lookup is keyed over the combined thread array, while index
|
|
240
|
+
// selectors address the regular/archived subset the primitives render.
|
|
241
|
+
const ids = selector.archived ? state.archivedThreadIds : state.threadIds;
|
|
242
|
+
const id = ids[selector.index];
|
|
243
|
+
if (id === undefined) return threadListItems.get({ index: -1 });
|
|
244
|
+
const index = threads.findIndex((t) => t.id === id);
|
|
245
|
+
return threadListItems.get({ index });
|
|
240
246
|
},
|
|
241
247
|
thread: () => mainThreadClient.methods,
|
|
242
248
|
};
|
|
@@ -24,8 +24,7 @@ import {
|
|
|
24
24
|
interactableToolName,
|
|
25
25
|
} from "../../model-context/interactable-composer-metadata";
|
|
26
26
|
import { notifySubscribers as notifyStateSubscribers } from "../../subscribable/subscribable";
|
|
27
|
-
|
|
28
|
-
const PERSISTENCE_DEBOUNCE_MS = 500;
|
|
27
|
+
import { useInteractablePersistenceQueue } from "../interactables-shared/useInteractablePersistenceQueue";
|
|
29
28
|
|
|
30
29
|
type RestorePersistedStateOptions = {
|
|
31
30
|
stash: Map<string, unknown>;
|
|
@@ -109,26 +108,6 @@ const useInteractablesResource = ({
|
|
|
109
108
|
const adapterRef = useRef<
|
|
110
109
|
Unstable_InteractablePersistenceAdapter | undefined
|
|
111
110
|
>(undefined);
|
|
112
|
-
const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(
|
|
113
|
-
undefined,
|
|
114
|
-
);
|
|
115
|
-
const syncSeqRef = useRef(0);
|
|
116
|
-
const latestSyncSeqByIdRef = useRef(new Map<string, number>());
|
|
117
|
-
const inFlightPersistenceRef = useRef(0);
|
|
118
|
-
const flushResolversRef = useRef<Array<() => void>>([]);
|
|
119
|
-
const dirtyIdsRef = useRef(new Set<string>());
|
|
120
|
-
|
|
121
|
-
type PersistenceBatch = {
|
|
122
|
-
adapter: Unstable_InteractablePersistenceAdapter;
|
|
123
|
-
payload: Unstable_InteractablePersistedState;
|
|
124
|
-
dirtyIds: Set<string>;
|
|
125
|
-
seq: number;
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
const outgoingQueueRef = useRef<PersistenceBatch[]>([]);
|
|
129
|
-
const runPersistenceRef = useRef<(batch?: PersistenceBatch) => void>(
|
|
130
|
-
() => {},
|
|
131
|
-
);
|
|
132
111
|
|
|
133
112
|
const setStateAndRef = useCallback(
|
|
134
113
|
(
|
|
@@ -152,149 +131,28 @@ const useInteractablesResource = ({
|
|
|
152
131
|
return result;
|
|
153
132
|
}, []);
|
|
154
133
|
|
|
155
|
-
const
|
|
134
|
+
const updatePersistenceStatus = useCallback(
|
|
156
135
|
(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
const enqueuePersistence = useCallback(
|
|
170
|
-
(adapter: Unstable_InteractablePersistenceAdapter) => {
|
|
171
|
-
const batch = takeDirtyBatch(adapter);
|
|
172
|
-
if (!batch) return;
|
|
173
|
-
if (inFlightPersistenceRef.current === 0) {
|
|
174
|
-
runPersistenceRef.current(batch);
|
|
175
|
-
} else {
|
|
176
|
-
outgoingQueueRef.current.push(batch);
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
|
-
[takeDirtyBatch],
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
const runPersistence = useCallback(
|
|
183
|
-
async (batch?: PersistenceBatch) => {
|
|
184
|
-
const resolved =
|
|
185
|
-
batch ??
|
|
186
|
-
(adapterRef.current ? takeDirtyBatch(adapterRef.current) : undefined);
|
|
187
|
-
if (!resolved) {
|
|
188
|
-
if (inFlightPersistenceRef.current === 0) {
|
|
189
|
-
for (const resolve of flushResolversRef.current) resolve();
|
|
190
|
-
flushResolversRef.current = [];
|
|
191
|
-
}
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const { adapter, payload, dirtyIds, seq } = resolved;
|
|
196
|
-
inFlightPersistenceRef.current += 1;
|
|
197
|
-
|
|
198
|
-
setStateAndRef((prev) => ({
|
|
199
|
-
...prev,
|
|
200
|
-
persistence: {
|
|
201
|
-
...prev.persistence,
|
|
202
|
-
...Object.fromEntries(
|
|
203
|
-
[...dirtyIds].map((id) => [
|
|
204
|
-
id,
|
|
205
|
-
{ isPending: true, error: undefined },
|
|
206
|
-
]),
|
|
207
|
-
),
|
|
208
|
-
},
|
|
209
|
-
}));
|
|
210
|
-
|
|
211
|
-
try {
|
|
212
|
-
await adapter.save(payload);
|
|
213
|
-
setStateAndRef((prev) => {
|
|
214
|
-
let changed = false;
|
|
215
|
-
const persistence = { ...prev.persistence };
|
|
216
|
-
for (const id of dirtyIds) {
|
|
217
|
-
if (
|
|
218
|
-
latestSyncSeqByIdRef.current.get(id) !== seq ||
|
|
219
|
-
dirtyIdsRef.current.has(id)
|
|
220
|
-
)
|
|
221
|
-
continue;
|
|
222
|
-
latestSyncSeqByIdRef.current.delete(id);
|
|
223
|
-
delete persistence[id];
|
|
224
|
-
changed = true;
|
|
225
|
-
}
|
|
226
|
-
return changed ? { ...prev, persistence } : prev;
|
|
227
|
-
});
|
|
228
|
-
} catch (e) {
|
|
229
|
-
setStateAndRef((prev) => {
|
|
230
|
-
let changed = false;
|
|
231
|
-
const persistence = { ...prev.persistence };
|
|
232
|
-
for (const id of dirtyIds) {
|
|
233
|
-
if (
|
|
234
|
-
latestSyncSeqByIdRef.current.get(id) !== seq ||
|
|
235
|
-
dirtyIdsRef.current.has(id)
|
|
236
|
-
)
|
|
237
|
-
continue;
|
|
238
|
-
latestSyncSeqByIdRef.current.delete(id);
|
|
239
|
-
persistence[id] = { isPending: false, error: e };
|
|
240
|
-
changed = true;
|
|
241
|
-
}
|
|
242
|
-
return changed ? { ...prev, persistence } : prev;
|
|
243
|
-
});
|
|
244
|
-
} finally {
|
|
245
|
-
inFlightPersistenceRef.current -= 1;
|
|
246
|
-
const next =
|
|
247
|
-
outgoingQueueRef.current.shift() ??
|
|
248
|
-
(adapterRef.current && dirtyIdsRef.current.size > 0
|
|
249
|
-
? takeDirtyBatch(adapterRef.current)
|
|
250
|
-
: undefined);
|
|
251
|
-
if (next) {
|
|
252
|
-
if (debounceTimerRef.current !== undefined) {
|
|
253
|
-
clearTimeout(debounceTimerRef.current);
|
|
254
|
-
debounceTimerRef.current = undefined;
|
|
255
|
-
}
|
|
256
|
-
runPersistenceRef.current(next);
|
|
257
|
-
} else if (inFlightPersistenceRef.current === 0) {
|
|
258
|
-
for (const resolve of flushResolversRef.current) resolve();
|
|
259
|
-
flushResolversRef.current = [];
|
|
260
|
-
}
|
|
261
|
-
}
|
|
136
|
+
updater: (
|
|
137
|
+
prev: Unstable_InteractablesState["persistence"],
|
|
138
|
+
) => Unstable_InteractablesState["persistence"],
|
|
139
|
+
) => {
|
|
140
|
+
setStateAndRef((prev) => {
|
|
141
|
+
const persistence = updater(prev.persistence);
|
|
142
|
+
return persistence === prev.persistence
|
|
143
|
+
? prev
|
|
144
|
+
: { ...prev, persistence };
|
|
145
|
+
});
|
|
262
146
|
},
|
|
263
|
-
[setStateAndRef
|
|
147
|
+
[setStateAndRef],
|
|
264
148
|
);
|
|
265
|
-
runPersistenceRef.current = (nextBatch) => {
|
|
266
|
-
void runPersistence(nextBatch);
|
|
267
|
-
};
|
|
268
149
|
|
|
269
|
-
const flushIfPending
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}, [enqueuePersistence]);
|
|
276
|
-
|
|
277
|
-
const schedulePersistence = useCallback(
|
|
278
|
-
(id: string) => {
|
|
279
|
-
if (!adapterRef.current) return;
|
|
280
|
-
dirtyIdsRef.current.add(id);
|
|
281
|
-
if (debounceTimerRef.current !== undefined) {
|
|
282
|
-
clearTimeout(debounceTimerRef.current);
|
|
283
|
-
}
|
|
284
|
-
debounceTimerRef.current = setTimeout(() => {
|
|
285
|
-
debounceTimerRef.current = undefined;
|
|
286
|
-
if (inFlightPersistenceRef.current === 0 && adapterRef.current) {
|
|
287
|
-
enqueuePersistence(adapterRef.current);
|
|
288
|
-
} else {
|
|
289
|
-
debounceTimerRef.current = setTimeout(() => {
|
|
290
|
-
debounceTimerRef.current = undefined;
|
|
291
|
-
if (adapterRef.current) enqueuePersistence(adapterRef.current);
|
|
292
|
-
}, PERSISTENCE_DEBOUNCE_MS);
|
|
293
|
-
}
|
|
294
|
-
}, PERSISTENCE_DEBOUNCE_MS);
|
|
295
|
-
},
|
|
296
|
-
[enqueuePersistence],
|
|
297
|
-
);
|
|
150
|
+
const { flushIfPending, schedulePersistence, flush } =
|
|
151
|
+
useInteractablePersistenceQueue({
|
|
152
|
+
adapterRef,
|
|
153
|
+
snapshot: exportState,
|
|
154
|
+
updatePersistenceStatus,
|
|
155
|
+
});
|
|
298
156
|
|
|
299
157
|
const restorePersistedState = useCallback(
|
|
300
158
|
(
|
|
@@ -394,23 +252,6 @@ const useInteractablesResource = ({
|
|
|
394
252
|
};
|
|
395
253
|
}, [persistence, setPersistenceAdapter]);
|
|
396
254
|
|
|
397
|
-
const flush = useCallback(async () => {
|
|
398
|
-
if (debounceTimerRef.current !== undefined) {
|
|
399
|
-
clearTimeout(debounceTimerRef.current);
|
|
400
|
-
debounceTimerRef.current = undefined;
|
|
401
|
-
}
|
|
402
|
-
const hasWork =
|
|
403
|
-
inFlightPersistenceRef.current > 0 ||
|
|
404
|
-
dirtyIdsRef.current.size > 0 ||
|
|
405
|
-
outgoingQueueRef.current.length > 0;
|
|
406
|
-
if (!hasWork) return;
|
|
407
|
-
const p = new Promise<void>((resolve) => {
|
|
408
|
-
flushResolversRef.current.push(resolve);
|
|
409
|
-
});
|
|
410
|
-
if (adapterRef.current) enqueuePersistence(adapterRef.current);
|
|
411
|
-
return p;
|
|
412
|
-
}, [enqueuePersistence]);
|
|
413
|
-
|
|
414
255
|
const setDefState = useCallback(
|
|
415
256
|
(id: string, updater: (prev: unknown) => unknown) => {
|
|
416
257
|
touchedIdsRef.current.add(id);
|
|
@@ -234,6 +234,37 @@ describe("RemoteThreadList", () => {
|
|
|
234
234
|
handle.destroy();
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
+
it("keeps a thread initialized during the list() flight after switching away", async () => {
|
|
238
|
+
const listDeferred =
|
|
239
|
+
deferred<Awaited<ReturnType<RemoteThreadListAdapter["list"]>>>();
|
|
240
|
+
const adapter = makeAdapter({
|
|
241
|
+
list: vi.fn(() => listDeferred.promise),
|
|
242
|
+
});
|
|
243
|
+
const { handle } = mountList(adapter);
|
|
244
|
+
const aui = handle.getClient();
|
|
245
|
+
|
|
246
|
+
const loadPromise = aui.threads.getLoadThreadsPromise();
|
|
247
|
+
const initializedId = aui.threads.getState().mainThreadId;
|
|
248
|
+
await aui.threads.item("main").initialize();
|
|
249
|
+
await vi.waitFor(() => {
|
|
250
|
+
expect(aui.threads.getState().threadIds).toContain(initializedId);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
listDeferred.resolve({
|
|
254
|
+
threads: [{ status: "regular" as const, remoteId: "t1", title: "One" }],
|
|
255
|
+
});
|
|
256
|
+
await loadPromise;
|
|
257
|
+
|
|
258
|
+
flushTapSync(() => aui.threads.switchToNewThread());
|
|
259
|
+
await vi.waitFor(() => {
|
|
260
|
+
expect(aui.threads.getState().mainThreadId).not.toBe(initializedId);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
expect(aui.threads.getState().threadIds[0]).toBe(initializedId);
|
|
264
|
+
expect(aui.threads.getState().threadIds).toContain("t1");
|
|
265
|
+
handle.destroy();
|
|
266
|
+
});
|
|
267
|
+
|
|
237
268
|
it("switches to a listed thread and back to a new thread", async () => {
|
|
238
269
|
const adapter = makeAdapter({
|
|
239
270
|
list: vi.fn(async () => ({
|
|
@@ -331,6 +362,71 @@ describe("RemoteThreadList", () => {
|
|
|
331
362
|
handle.destroy();
|
|
332
363
|
});
|
|
333
364
|
|
|
365
|
+
it("preserves generated titles across an overlapping reload", async () => {
|
|
366
|
+
const reload = deferred<{
|
|
367
|
+
threads: {
|
|
368
|
+
status: "regular";
|
|
369
|
+
remoteId: string;
|
|
370
|
+
title: string;
|
|
371
|
+
}[];
|
|
372
|
+
}>();
|
|
373
|
+
const adapter = makeAdapter({
|
|
374
|
+
list: vi
|
|
375
|
+
.fn()
|
|
376
|
+
.mockResolvedValueOnce({
|
|
377
|
+
threads: [
|
|
378
|
+
{ status: "regular" as const, remoteId: "t1", title: "One" },
|
|
379
|
+
],
|
|
380
|
+
})
|
|
381
|
+
.mockImplementationOnce(() => reload.promise),
|
|
382
|
+
generateTitle: vi.fn(
|
|
383
|
+
async () =>
|
|
384
|
+
new ReadableStream({
|
|
385
|
+
start(controller) {
|
|
386
|
+
controller.enqueue({
|
|
387
|
+
type: "part-start",
|
|
388
|
+
path: [],
|
|
389
|
+
part: { type: "text" },
|
|
390
|
+
});
|
|
391
|
+
controller.enqueue({
|
|
392
|
+
type: "text-delta",
|
|
393
|
+
path: [0],
|
|
394
|
+
textDelta: "Generated",
|
|
395
|
+
});
|
|
396
|
+
controller.enqueue({ type: "part-finish", path: [0] });
|
|
397
|
+
controller.close();
|
|
398
|
+
},
|
|
399
|
+
}) as never,
|
|
400
|
+
),
|
|
401
|
+
});
|
|
402
|
+
const { handle } = mountList(adapter);
|
|
403
|
+
const aui = handle.getClient();
|
|
404
|
+
await aui.threads.getLoadThreadsPromise();
|
|
405
|
+
flushTapSync(() => aui.threads.switchToThread("t1"));
|
|
406
|
+
await vi.waitFor(() => {
|
|
407
|
+
expect(aui.threads.getState().mainThreadId).toBe("t1");
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const reloadPromise = aui.threads.reload();
|
|
411
|
+
await vi.waitFor(() => {
|
|
412
|
+
expect(adapter.list).toHaveBeenCalledTimes(2);
|
|
413
|
+
});
|
|
414
|
+
await aui.threads.item({ id: "t1" }).rename("Renamed");
|
|
415
|
+
await aui.threads.item({ id: "t1" }).generateTitle();
|
|
416
|
+
await vi.waitFor(() => {
|
|
417
|
+
expect(aui.threads.item({ id: "t1" }).getState().title).toBe("Generated");
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
reload.resolve({
|
|
421
|
+
threads: [{ status: "regular", remoteId: "t1", title: "One" }],
|
|
422
|
+
});
|
|
423
|
+
await reloadPromise;
|
|
424
|
+
await vi.waitFor(() => {
|
|
425
|
+
expect(aui.threads.item({ id: "t1" }).getState().title).toBe("Generated");
|
|
426
|
+
});
|
|
427
|
+
handle.destroy();
|
|
428
|
+
});
|
|
429
|
+
|
|
334
430
|
it("opens a controlled threadId without echoing it back", async () => {
|
|
335
431
|
const adapter = makeAdapter({
|
|
336
432
|
list: vi.fn(async () => ({
|
|
@@ -13,16 +13,19 @@ import {
|
|
|
13
13
|
useClientResource,
|
|
14
14
|
} from "@assistant-ui/store/client";
|
|
15
15
|
import { isDevelopment, useThreadSelectionEvents } from "../../store/internal";
|
|
16
|
-
import { generateId } from "../../utils/id";
|
|
17
16
|
import { OptimisticState } from "../../runtimes/remote-thread-list/optimistic-state";
|
|
18
17
|
import {
|
|
19
18
|
classifyThreads,
|
|
19
|
+
createEmptyRemoteThreadState,
|
|
20
20
|
createThreadMappingId,
|
|
21
21
|
getThreadData,
|
|
22
22
|
normalizeCursor,
|
|
23
23
|
updateStatusReducer,
|
|
24
24
|
type RemoteThreadData,
|
|
25
25
|
type RemoteThreadState,
|
|
26
|
+
preserveMidLoadTransitions,
|
|
27
|
+
seedNewThread,
|
|
28
|
+
statusSnapshot,
|
|
26
29
|
} from "../../runtimes/remote-thread-list/remote-thread-state";
|
|
27
30
|
import type {
|
|
28
31
|
RemoteThreadInitializeResponse,
|
|
@@ -40,16 +43,7 @@ import { AdaptedRemoteThread } from "./AdaptedRemoteThread";
|
|
|
40
43
|
|
|
41
44
|
const RESOLVED_PROMISE = Promise.resolve();
|
|
42
45
|
|
|
43
|
-
const EMPTY_LIST
|
|
44
|
-
isLoading: true,
|
|
45
|
-
isLoadingMore: false,
|
|
46
|
-
cursor: undefined,
|
|
47
|
-
newThreadId: undefined,
|
|
48
|
-
threadIds: [],
|
|
49
|
-
archivedThreadIds: [],
|
|
50
|
-
threadIdMap: {},
|
|
51
|
-
threadData: {},
|
|
52
|
-
};
|
|
46
|
+
const EMPTY_LIST = createEmptyRemoteThreadState();
|
|
53
47
|
|
|
54
48
|
export type RemoteThreadListProps = {
|
|
55
49
|
/**
|
|
@@ -91,46 +85,14 @@ const toInitializeResult = (
|
|
|
91
85
|
|
|
92
86
|
const applyTitleStream = async (
|
|
93
87
|
stream: Parameters<typeof AssistantMessageStream.fromAssistantStream>[0],
|
|
94
|
-
onTitle: (title: string | undefined) => void
|
|
88
|
+
onTitle: (title: string | undefined) => Promise<void>,
|
|
95
89
|
) => {
|
|
96
90
|
const messageStream = AssistantMessageStream.fromAssistantStream(stream);
|
|
97
91
|
for await (const result of messageStream) {
|
|
98
|
-
onTitle(result.parts.filter((part) => part.type === "text")[0]?.text);
|
|
92
|
+
await onTitle(result.parts.filter((part) => part.type === "text")[0]?.text);
|
|
99
93
|
}
|
|
100
94
|
};
|
|
101
95
|
|
|
102
|
-
const seedNewThread = (
|
|
103
|
-
state: RemoteThreadState,
|
|
104
|
-
): { id: string; state: RemoteThreadState } => {
|
|
105
|
-
let id: string;
|
|
106
|
-
do {
|
|
107
|
-
id = `__LOCALID_${generateId()}`;
|
|
108
|
-
} while (state.threadIdMap[id]);
|
|
109
|
-
const mappingId = createThreadMappingId(id);
|
|
110
|
-
return {
|
|
111
|
-
id,
|
|
112
|
-
state: {
|
|
113
|
-
...state,
|
|
114
|
-
newThreadId: id,
|
|
115
|
-
threadIdMap: {
|
|
116
|
-
...state.threadIdMap,
|
|
117
|
-
[id]: mappingId,
|
|
118
|
-
},
|
|
119
|
-
threadData: {
|
|
120
|
-
...state.threadData,
|
|
121
|
-
[mappingId]: {
|
|
122
|
-
status: "new",
|
|
123
|
-
id,
|
|
124
|
-
remoteId: undefined,
|
|
125
|
-
externalId: undefined,
|
|
126
|
-
title: undefined,
|
|
127
|
-
custom: undefined,
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
};
|
|
132
|
-
};
|
|
133
|
-
|
|
134
96
|
const useThreadListItemClient = (props: {
|
|
135
97
|
data: RemoteThreadData;
|
|
136
98
|
isRunning: boolean;
|
|
@@ -424,6 +386,7 @@ const useRemoteThreadList = (
|
|
|
424
386
|
if (session.loadPromise) return session.loadPromise;
|
|
425
387
|
const generation = session.loadGeneration;
|
|
426
388
|
const adapter = session.adapter;
|
|
389
|
+
const statusAtRequest = statusSnapshot(store.baseValue);
|
|
427
390
|
session.loadPromise = store
|
|
428
391
|
.optimisticUpdate({
|
|
429
392
|
execute: () => adapter.list(),
|
|
@@ -437,7 +400,7 @@ const useRemoteThreadList = (
|
|
|
437
400
|
threadIdMap: {},
|
|
438
401
|
threadData: {},
|
|
439
402
|
});
|
|
440
|
-
|
|
403
|
+
const merged = {
|
|
441
404
|
...state,
|
|
442
405
|
isLoading: false,
|
|
443
406
|
cursor: normalizeCursor(page.nextCursor),
|
|
@@ -452,6 +415,7 @@ const useRemoteThreadList = (
|
|
|
452
415
|
...fresh.threadData,
|
|
453
416
|
},
|
|
454
417
|
};
|
|
418
|
+
return preserveMidLoadTransitions(merged, state, statusAtRequest);
|
|
455
419
|
},
|
|
456
420
|
})
|
|
457
421
|
.catch((error: unknown) => {
|
|
@@ -985,19 +949,23 @@ const useRemoteThreadList = (
|
|
|
985
949
|
if (!messages) return;
|
|
986
950
|
const stream = await currentAdapter.generateTitle(remoteId, messages);
|
|
987
951
|
requireAdapterGeneration(adapterGeneration);
|
|
988
|
-
await applyTitleStream(stream, (newTitle) => {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
952
|
+
await applyTitleStream(stream, async (newTitle) => {
|
|
953
|
+
await store.optimisticUpdate({
|
|
954
|
+
execute: async () => {},
|
|
955
|
+
optimistic: (state) => {
|
|
956
|
+
if (adapterGeneration !== session.adapterGeneration) return state;
|
|
957
|
+
const current = getThreadData(state, data.id);
|
|
958
|
+
if (!current) return state;
|
|
959
|
+
return {
|
|
960
|
+
...state,
|
|
961
|
+
threadData: {
|
|
962
|
+
...state.threadData,
|
|
963
|
+
[current.id]: {
|
|
964
|
+
...current,
|
|
965
|
+
title: newTitle,
|
|
966
|
+
},
|
|
967
|
+
},
|
|
968
|
+
};
|
|
1001
969
|
},
|
|
1002
970
|
});
|
|
1003
971
|
});
|