@assistant-ui/core 0.3.14 → 0.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/voice.js +11 -0
- package/dist/adapters/voice.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +2 -1
- package/dist/internal.js +2 -1
- package/dist/model-context/frame/host.d.ts +1 -1
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +17 -3
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/frame/provider.d.ts +4 -0
- package/dist/model-context/frame/provider.d.ts.map +1 -1
- package/dist/model-context/frame/provider.js +50 -3
- package/dist/model-context/frame/provider.js.map +1 -1
- package/dist/model-context/frame/types.d.ts +4 -1
- package/dist/model-context/frame/types.d.ts.map +1 -1
- package/dist/model-context/frame/types.js.map +1 -1
- package/dist/react/client/RemoteThreadList.d.ts +1 -1
- package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
- package/dist/react/client/RemoteThreadList.js +95 -23
- package/dist/react/client/RemoteThreadList.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarCopy.js +7 -1
- package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts +3 -0
- package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadIsEmpty.js +3 -0
- package/dist/react/primitive-hooks/useThreadIsEmpty.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadIsRunning.d.ts +3 -0
- package/dist/react/primitive-hooks/useThreadIsRunning.d.ts.map +1 -1
- package/dist/react/primitive-hooks/useThreadIsRunning.js +3 -0
- package/dist/react/primitive-hooks/useThreadIsRunning.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +2 -0
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +6 -0
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +6 -0
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +192 -50
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.js +2 -0
- package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +1 -1
- package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +38 -5
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
- package/dist/runtime/utils/message-repository.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.js +27 -18
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js +2 -0
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +102 -0
- package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -0
- package/dist/runtimes/inert/InertThreadRuntimeCore.js +156 -0
- package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -0
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +25 -6
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +12 -60
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +10 -145
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
- package/dist/runtimes/remote-thread-list/adapter-changed.d.ts +9 -0
- package/dist/runtimes/remote-thread-list/adapter-changed.d.ts.map +1 -0
- package/dist/runtimes/remote-thread-list/adapter-changed.js +13 -0
- package/dist/runtimes/remote-thread-list/adapter-changed.js.map +1 -0
- package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/empty-thread-core.js +16 -155
- package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
- package/dist/runtimes/remote-thread-list/optimistic-state.d.ts +2 -0
- package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/optimistic-state.js +11 -0
- package/dist/runtimes/remote-thread-list/optimistic-state.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +3 -0
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/store/clients/external-thread.js +1 -1
- package/dist/store/clients/external-thread.js.map +1 -1
- package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -1
- package/dist/store/runtime-clients/handle-runtime-action.js +2 -0
- package/dist/store/runtime-clients/handle-runtime-action.js.map +1 -1
- package/dist/types/message.d.ts +1 -1
- package/dist/types/message.js.map +1 -1
- package/dist/utils/silent-runtime-action.d.ts +6 -0
- package/dist/utils/silent-runtime-action.d.ts.map +1 -0
- package/dist/utils/silent-runtime-action.js +7 -0
- package/dist/utils/silent-runtime-action.js.map +1 -0
- package/package.json +4 -4
- package/src/adapters/voice.test.ts +66 -1
- package/src/adapters/voice.ts +8 -0
- package/src/internal.ts +1 -0
- package/src/model-context/frame/host.test.ts +64 -5
- package/src/model-context/frame/host.ts +16 -3
- package/src/model-context/frame/provider.test.ts +347 -20
- package/src/model-context/frame/provider.ts +58 -2
- package/src/model-context/frame/types.ts +5 -0
- package/src/react/client/RemoteThreadList.test.ts +196 -0
- package/src/react/client/RemoteThreadList.ts +93 -24
- package/src/react/primitive-hooks/useActionBarCopy.test.ts +15 -0
- package/src/react/primitive-hooks/useActionBarCopy.ts +13 -3
- package/src/react/primitive-hooks/useThreadIsEmpty.ts +3 -0
- package/src/react/primitive-hooks/useThreadIsRunning.ts +3 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +12 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.republish.test.ts +87 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +268 -63
- package/src/react/runtimes/RemoteThreadResource.ts +2 -0
- package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +58 -0
- package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +3 -1
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +98 -3
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +68 -6
- package/src/react/runtimes/useLocalRuntime.test.tsx +5 -5
- package/src/runtime/utils/message-repository.ts +26 -18
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +4 -0
- package/src/runtimes/inert/InertThreadRuntimeCore.test.ts +253 -0
- package/src/runtimes/inert/InertThreadRuntimeCore.ts +232 -0
- package/src/runtimes/local/local-thread-runtime-core.test.ts +319 -0
- package/src/runtimes/local/local-thread-runtime-core.ts +38 -8
- package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +17 -209
- package/src/runtimes/remote-thread-list/adapter-changed.ts +10 -0
- package/src/runtimes/remote-thread-list/empty-thread-core.ts +28 -221
- package/src/runtimes/remote-thread-list/optimistic-state.test.ts +18 -0
- package/src/runtimes/remote-thread-list/optimistic-state.ts +13 -0
- package/src/runtimes/remote-thread-list/types.ts +4 -0
- package/src/store/clients/external-thread.ts +1 -1
- package/src/store/runtime-clients/handle-runtime-action.test.ts +40 -0
- package/src/store/runtime-clients/handle-runtime-action.ts +3 -0
- package/src/tests/MessageRepository.test.ts +48 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-errors.test.ts +2 -2
- package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +8 -6
- package/src/tests/empty-thread-core.test.ts +6 -0
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +20 -0
- package/src/tests/external-thread-isEmpty.test.tsx +100 -0
- package/src/tests/remote-thread-list-adapter-switch.test.ts +383 -0
- package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +67 -0
- package/src/types/message.ts +1 -1
- package/src/utils/silent-runtime-action.ts +6 -0
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import type { Unsubscribe } from "../../types/unsubscribe";
|
|
2
1
|
import type { ThreadMessage } from "../../types/message";
|
|
3
|
-
import
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
InertThreadRuntimeCore,
|
|
4
|
+
createInertComposer,
|
|
5
|
+
} from "../inert/InertThreadRuntimeCore";
|
|
5
6
|
|
|
6
7
|
const READONLY_THREAD_ERROR = new Error(
|
|
7
8
|
"This is a readonly thread. You cannot perform mutations on readonly threads.",
|
|
8
9
|
);
|
|
9
10
|
|
|
10
|
-
export class ReadonlyThreadRuntimeCore
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
export class ReadonlyThreadRuntimeCore extends InertThreadRuntimeCore {
|
|
12
|
+
protected get error() {
|
|
13
|
+
return READONLY_THREAD_ERROR;
|
|
14
|
+
}
|
|
15
|
+
|
|
14
16
|
private _messages: readonly ThreadMessage[] = [];
|
|
15
17
|
|
|
16
18
|
get messages() {
|
|
@@ -39,202 +41,6 @@ export class ReadonlyThreadRuntimeCore
|
|
|
39
41
|
return [messageId];
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
switchToBranch(): void {
|
|
43
|
-
throw READONLY_THREAD_ERROR;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
append(): void {
|
|
47
|
-
throw READONLY_THREAD_ERROR;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
deleteMessage(): void {
|
|
51
|
-
throw READONLY_THREAD_ERROR;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
startRun(): void {
|
|
55
|
-
throw READONLY_THREAD_ERROR;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
resumeRun(): void {
|
|
59
|
-
throw READONLY_THREAD_ERROR;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
cancelRun(): void {}
|
|
63
|
-
|
|
64
|
-
addToolResult(): void {
|
|
65
|
-
throw READONLY_THREAD_ERROR;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
resumeToolCall(): void {
|
|
69
|
-
throw READONLY_THREAD_ERROR;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
respondToToolApproval(): void {
|
|
73
|
-
throw READONLY_THREAD_ERROR;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
speak(): void {
|
|
77
|
-
throw READONLY_THREAD_ERROR;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
stopSpeaking(): void {}
|
|
81
|
-
|
|
82
|
-
connectVoice(): void {
|
|
83
|
-
throw READONLY_THREAD_ERROR;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
disconnectVoice(): void {}
|
|
87
|
-
|
|
88
|
-
getVoiceVolume = () => 0;
|
|
89
|
-
subscribeVoiceVolume = (): Unsubscribe => () => {};
|
|
90
|
-
|
|
91
|
-
muteVoice(): void {
|
|
92
|
-
throw READONLY_THREAD_ERROR;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
unmuteVoice(): void {
|
|
96
|
-
throw READONLY_THREAD_ERROR;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
submitFeedback(): void {
|
|
100
|
-
throw READONLY_THREAD_ERROR;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
getModelContext() {
|
|
104
|
-
return {};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
exportExternalState() {
|
|
108
|
-
throw READONLY_THREAD_ERROR;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
importExternalState(): void {
|
|
112
|
-
throw READONLY_THREAD_ERROR;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
unstable_notifySessionReset(): void {
|
|
116
|
-
throw READONLY_THREAD_ERROR;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
composer = {
|
|
120
|
-
attachments: [] as never[],
|
|
121
|
-
attachmentAccept: "*",
|
|
122
|
-
|
|
123
|
-
async addAttachment() {
|
|
124
|
-
throw READONLY_THREAD_ERROR;
|
|
125
|
-
},
|
|
126
|
-
|
|
127
|
-
async removeAttachment() {
|
|
128
|
-
throw READONLY_THREAD_ERROR;
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
isEditing: false as const,
|
|
132
|
-
canCancel: false,
|
|
133
|
-
canSend: false,
|
|
134
|
-
isEmpty: true,
|
|
135
|
-
text: "",
|
|
136
|
-
|
|
137
|
-
setText() {
|
|
138
|
-
throw READONLY_THREAD_ERROR;
|
|
139
|
-
},
|
|
140
|
-
|
|
141
|
-
role: "user" as const,
|
|
142
|
-
|
|
143
|
-
setRole() {
|
|
144
|
-
throw READONLY_THREAD_ERROR;
|
|
145
|
-
},
|
|
146
|
-
|
|
147
|
-
runConfig: {},
|
|
148
|
-
|
|
149
|
-
setRunConfig() {
|
|
150
|
-
throw READONLY_THREAD_ERROR;
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
async reset() {
|
|
154
|
-
// noop
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
async clearAttachments() {
|
|
158
|
-
// noop
|
|
159
|
-
},
|
|
160
|
-
|
|
161
|
-
send() {
|
|
162
|
-
throw READONLY_THREAD_ERROR;
|
|
163
|
-
},
|
|
164
|
-
|
|
165
|
-
cancel() {
|
|
166
|
-
// noop
|
|
167
|
-
},
|
|
168
|
-
|
|
169
|
-
queue: [] as never[],
|
|
170
|
-
moveQueueItem() {},
|
|
171
|
-
removeQueueItem() {},
|
|
172
|
-
|
|
173
|
-
dictation: undefined,
|
|
174
|
-
|
|
175
|
-
startDictation() {
|
|
176
|
-
throw READONLY_THREAD_ERROR;
|
|
177
|
-
},
|
|
178
|
-
|
|
179
|
-
stopDictation() {
|
|
180
|
-
// noop
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
quote: undefined,
|
|
184
|
-
|
|
185
|
-
setQuote() {
|
|
186
|
-
throw READONLY_THREAD_ERROR;
|
|
187
|
-
},
|
|
188
|
-
|
|
189
|
-
subscribe() {
|
|
190
|
-
return () => {};
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
unstable_on() {
|
|
194
|
-
return () => {};
|
|
195
|
-
},
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
getEditComposer() {
|
|
199
|
-
return undefined;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
beginEdit(): void {
|
|
203
|
-
throw READONLY_THREAD_ERROR;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
speech = undefined;
|
|
207
|
-
voice = undefined;
|
|
208
|
-
|
|
209
|
-
capabilities = {
|
|
210
|
-
switchToBranch: false,
|
|
211
|
-
switchBranchDuringRun: false,
|
|
212
|
-
edit: false,
|
|
213
|
-
delete: false,
|
|
214
|
-
reload: false,
|
|
215
|
-
refetchThread: false,
|
|
216
|
-
cancel: false,
|
|
217
|
-
unstable_copy: false,
|
|
218
|
-
speech: false,
|
|
219
|
-
dictation: false,
|
|
220
|
-
voice: false,
|
|
221
|
-
attachments: false,
|
|
222
|
-
feedback: false,
|
|
223
|
-
queue: false,
|
|
224
|
-
} as const;
|
|
225
|
-
|
|
226
|
-
isDisabled = false;
|
|
227
|
-
isSendDisabled = false;
|
|
228
|
-
isLoading = false;
|
|
229
|
-
|
|
230
|
-
state = null;
|
|
231
|
-
suggestions = [] as never[];
|
|
232
|
-
extras = undefined;
|
|
233
|
-
|
|
234
|
-
import(): void {
|
|
235
|
-
throw READONLY_THREAD_ERROR;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
44
|
export() {
|
|
239
45
|
return {
|
|
240
46
|
messages: this._messages.map((message, idx) => ({
|
|
@@ -244,11 +50,13 @@ export class ReadonlyThreadRuntimeCore
|
|
|
244
50
|
};
|
|
245
51
|
}
|
|
246
52
|
|
|
247
|
-
|
|
248
|
-
throw READONLY_THREAD_ERROR;
|
|
249
|
-
}
|
|
53
|
+
composer = createInertComposer(READONLY_THREAD_ERROR, false);
|
|
250
54
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
55
|
+
isLoading = false;
|
|
56
|
+
|
|
57
|
+
override cancelRun(): void {}
|
|
58
|
+
|
|
59
|
+
override stopSpeaking(): void {}
|
|
60
|
+
|
|
61
|
+
override disconnectVoice(): void {}
|
|
254
62
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SILENT_RUNTIME_ACTION } from "../../utils/silent-runtime-action";
|
|
2
|
+
|
|
3
|
+
export class ThreadListAdapterChangedError extends Error {
|
|
4
|
+
readonly [SILENT_RUNTIME_ACTION] = true;
|
|
5
|
+
|
|
6
|
+
constructor() {
|
|
7
|
+
super("Thread list adapter changed while an operation was pending.");
|
|
8
|
+
this.name = "ThreadListAdapterChangedError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -1,238 +1,45 @@
|
|
|
1
|
+
import type { Unsubscribe } from "../../types/unsubscribe";
|
|
1
2
|
import type { ThreadRuntimeCore } from "../../runtime/interfaces/thread-runtime-core";
|
|
3
|
+
import {
|
|
4
|
+
InertThreadRuntimeCore,
|
|
5
|
+
createInertComposer,
|
|
6
|
+
} from "../inert/InertThreadRuntimeCore";
|
|
2
7
|
|
|
3
8
|
const EMPTY_THREAD_ERROR = new Error(
|
|
4
9
|
"This is the empty thread, a placeholder for the main thread. You cannot perform any actions on this thread instance. This error is probably because you tried to call a thread method in your render function. Call the method inside a `useEffect` hook instead.",
|
|
5
10
|
);
|
|
6
|
-
export const EMPTY_THREAD_CORE: ThreadRuntimeCore = {
|
|
7
|
-
getMessageById() {
|
|
8
|
-
return undefined;
|
|
9
|
-
},
|
|
10
|
-
|
|
11
|
-
getBranches() {
|
|
12
|
-
return [];
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
switchToBranch() {
|
|
16
|
-
throw EMPTY_THREAD_ERROR;
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
append() {
|
|
20
|
-
throw EMPTY_THREAD_ERROR;
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
deleteMessage() {
|
|
24
|
-
throw EMPTY_THREAD_ERROR;
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
startRun() {
|
|
28
|
-
throw EMPTY_THREAD_ERROR;
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
resumeRun() {
|
|
32
|
-
throw EMPTY_THREAD_ERROR;
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
cancelRun() {
|
|
36
|
-
throw EMPTY_THREAD_ERROR;
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
addToolResult() {
|
|
40
|
-
throw EMPTY_THREAD_ERROR;
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
resumeToolCall() {
|
|
44
|
-
throw EMPTY_THREAD_ERROR;
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
respondToToolApproval() {
|
|
48
|
-
throw EMPTY_THREAD_ERROR;
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
speak() {
|
|
52
|
-
throw EMPTY_THREAD_ERROR;
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
stopSpeaking() {
|
|
56
|
-
throw EMPTY_THREAD_ERROR;
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
connectVoice() {
|
|
60
|
-
throw EMPTY_THREAD_ERROR;
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
disconnectVoice() {
|
|
64
|
-
throw EMPTY_THREAD_ERROR;
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
getVoiceVolume: () => 0,
|
|
68
|
-
subscribeVoiceVolume: () => () => {},
|
|
69
|
-
|
|
70
|
-
muteVoice() {
|
|
71
|
-
throw EMPTY_THREAD_ERROR;
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
unmuteVoice() {
|
|
75
|
-
throw EMPTY_THREAD_ERROR;
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
submitFeedback() {
|
|
79
|
-
throw EMPTY_THREAD_ERROR;
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
getModelContext() {
|
|
83
|
-
return {};
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
exportExternalState() {
|
|
87
|
-
throw EMPTY_THREAD_ERROR;
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
importExternalState() {
|
|
91
|
-
throw EMPTY_THREAD_ERROR;
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
unstable_notifySessionReset() {
|
|
95
|
-
throw EMPTY_THREAD_ERROR;
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
composer: {
|
|
99
|
-
attachments: [],
|
|
100
|
-
attachmentAccept: "*",
|
|
101
|
-
|
|
102
|
-
async addAttachment() {
|
|
103
|
-
throw EMPTY_THREAD_ERROR;
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
async removeAttachment() {
|
|
107
|
-
throw EMPTY_THREAD_ERROR;
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
isEditing: true,
|
|
111
|
-
|
|
112
|
-
canCancel: false,
|
|
113
|
-
canSend: false,
|
|
114
|
-
isEmpty: true,
|
|
115
11
|
|
|
116
|
-
|
|
12
|
+
class EmptyThreadRuntimeCore extends InertThreadRuntimeCore {
|
|
13
|
+
protected get error() {
|
|
14
|
+
return EMPTY_THREAD_ERROR;
|
|
15
|
+
}
|
|
117
16
|
|
|
118
|
-
|
|
119
|
-
throw EMPTY_THREAD_ERROR;
|
|
120
|
-
},
|
|
17
|
+
messages = [] as never[];
|
|
121
18
|
|
|
122
|
-
|
|
19
|
+
composer = createInertComposer(EMPTY_THREAD_ERROR, true);
|
|
123
20
|
|
|
124
|
-
|
|
125
|
-
throw EMPTY_THREAD_ERROR;
|
|
126
|
-
},
|
|
21
|
+
isLoading = true;
|
|
127
22
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
setRunConfig() {
|
|
131
|
-
throw EMPTY_THREAD_ERROR;
|
|
132
|
-
},
|
|
133
|
-
|
|
134
|
-
async reset() {
|
|
135
|
-
// noop
|
|
136
|
-
},
|
|
137
|
-
|
|
138
|
-
async clearAttachments() {
|
|
139
|
-
// noop
|
|
140
|
-
},
|
|
141
|
-
|
|
142
|
-
send() {
|
|
143
|
-
throw EMPTY_THREAD_ERROR;
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
cancel() {
|
|
147
|
-
// noop
|
|
148
|
-
},
|
|
149
|
-
|
|
150
|
-
queue: [] as never[],
|
|
151
|
-
moveQueueItem() {},
|
|
152
|
-
removeQueueItem() {},
|
|
153
|
-
|
|
154
|
-
dictation: undefined,
|
|
155
|
-
|
|
156
|
-
startDictation() {
|
|
157
|
-
throw EMPTY_THREAD_ERROR;
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
stopDictation() {
|
|
161
|
-
// noop
|
|
162
|
-
},
|
|
163
|
-
|
|
164
|
-
quote: undefined,
|
|
165
|
-
|
|
166
|
-
setQuote() {
|
|
167
|
-
throw EMPTY_THREAD_ERROR;
|
|
168
|
-
},
|
|
169
|
-
|
|
170
|
-
subscribe() {
|
|
171
|
-
return () => {};
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
unstable_on() {
|
|
175
|
-
return () => {};
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
|
|
179
|
-
getEditComposer() {
|
|
23
|
+
getMessageById() {
|
|
180
24
|
return undefined;
|
|
181
|
-
}
|
|
25
|
+
}
|
|
182
26
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
speech: undefined,
|
|
188
|
-
voice: undefined,
|
|
189
|
-
|
|
190
|
-
capabilities: {
|
|
191
|
-
switchToBranch: false,
|
|
192
|
-
switchBranchDuringRun: false,
|
|
193
|
-
edit: false,
|
|
194
|
-
delete: false,
|
|
195
|
-
reload: false,
|
|
196
|
-
refetchThread: false,
|
|
197
|
-
cancel: false,
|
|
198
|
-
unstable_copy: false,
|
|
199
|
-
speech: false,
|
|
200
|
-
dictation: false,
|
|
201
|
-
voice: false,
|
|
202
|
-
attachments: false,
|
|
203
|
-
feedback: false,
|
|
204
|
-
queue: false,
|
|
205
|
-
},
|
|
206
|
-
|
|
207
|
-
isDisabled: false,
|
|
208
|
-
isSendDisabled: false,
|
|
209
|
-
isLoading: true,
|
|
210
|
-
|
|
211
|
-
messages: [],
|
|
212
|
-
|
|
213
|
-
state: null,
|
|
214
|
-
|
|
215
|
-
suggestions: [],
|
|
216
|
-
|
|
217
|
-
extras: undefined,
|
|
218
|
-
|
|
219
|
-
subscribe() {
|
|
220
|
-
return () => {};
|
|
221
|
-
},
|
|
222
|
-
|
|
223
|
-
import() {
|
|
224
|
-
throw EMPTY_THREAD_ERROR;
|
|
225
|
-
},
|
|
27
|
+
getBranches() {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
226
30
|
|
|
227
31
|
export() {
|
|
228
32
|
return { messages: [] };
|
|
229
|
-
}
|
|
33
|
+
}
|
|
230
34
|
|
|
231
|
-
|
|
232
|
-
throw EMPTY_THREAD_ERROR;
|
|
233
|
-
},
|
|
234
|
-
|
|
235
|
-
unstable_on() {
|
|
35
|
+
override subscribe(): Unsubscribe {
|
|
236
36
|
return () => {};
|
|
237
|
-
}
|
|
238
|
-
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
override waitForUpdate(): Promise<void> {
|
|
40
|
+
return Promise.reject(EMPTY_THREAD_ERROR);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const EMPTY_THREAD_CORE: ThreadRuntimeCore =
|
|
45
|
+
new EmptyThreadRuntimeCore();
|
|
@@ -60,4 +60,22 @@ describe("OptimisticState", () => {
|
|
|
60
60
|
|
|
61
61
|
expect(state.value.title).toBe("Project Beta");
|
|
62
62
|
});
|
|
63
|
+
|
|
64
|
+
it("drops an in-flight transform after reset", async () => {
|
|
65
|
+
const state = new OptimisticState({ title: "Untitled", extra: 0 });
|
|
66
|
+
const request = deferred();
|
|
67
|
+
const update = state.optimisticUpdate({
|
|
68
|
+
execute: () => request.promise,
|
|
69
|
+
optimistic: (value) => ({ ...value, title: "Pending" }),
|
|
70
|
+
then: (value) => ({ ...value, extra: 1 }),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(state.value.title).toBe("Pending");
|
|
74
|
+
state.reset({ title: "Fresh", extra: 0 });
|
|
75
|
+
expect(state.value).toEqual({ title: "Fresh", extra: 0 });
|
|
76
|
+
|
|
77
|
+
request.resolve();
|
|
78
|
+
await update;
|
|
79
|
+
expect(state.value).toEqual({ title: "Fresh", extra: 0 });
|
|
80
|
+
});
|
|
63
81
|
});
|
|
@@ -48,6 +48,7 @@ export class OptimisticState<TState> extends BaseSubscribable {
|
|
|
48
48
|
[];
|
|
49
49
|
|
|
50
50
|
private _nextTransformOrder = 0;
|
|
51
|
+
private _epoch = 0;
|
|
51
52
|
|
|
52
53
|
private _baseValue: TState;
|
|
53
54
|
private _cachedValue: TState;
|
|
@@ -95,9 +96,20 @@ export class OptimisticState<TState> extends BaseSubscribable {
|
|
|
95
96
|
this._updateState();
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
public reset(state: TState): void {
|
|
100
|
+
// In-flight optimisticUpdate calls must not write into the replacement state.
|
|
101
|
+
this._epoch++;
|
|
102
|
+
this._pendingTransforms.length = 0;
|
|
103
|
+
this._completedOptimistics.length = 0;
|
|
104
|
+
this._baseValue = state;
|
|
105
|
+
this._cachedValue = state;
|
|
106
|
+
this._notifySubscribers();
|
|
107
|
+
}
|
|
108
|
+
|
|
98
109
|
public async optimisticUpdate<TResult>(
|
|
99
110
|
transform: Transform<TState, TResult>,
|
|
100
111
|
): Promise<TResult> {
|
|
112
|
+
const epoch = this._epoch;
|
|
101
113
|
const order = this._nextTransformOrder++;
|
|
102
114
|
const task = transform.execute();
|
|
103
115
|
const pendingTransform = {
|
|
@@ -110,6 +122,7 @@ export class OptimisticState<TState> extends BaseSubscribable {
|
|
|
110
122
|
this._updateState();
|
|
111
123
|
|
|
112
124
|
const result = await task;
|
|
125
|
+
if (epoch !== this._epoch) return result;
|
|
113
126
|
this._baseValue = pipeTransforms(this._baseValue, result, [
|
|
114
127
|
transform.optimistic,
|
|
115
128
|
transform.then,
|
|
@@ -96,6 +96,10 @@ export type RemoteThreadListAdapter = {
|
|
|
96
96
|
|
|
97
97
|
export type RemoteThreadListOptions = {
|
|
98
98
|
runtimeHook: () => AssistantRuntime;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The adapter reference should remain stable across renders. Replacing it reloads the list and drops cached threads that are not in the replacement page. In-flight mutations from the previous adapter are cancelled.
|
|
102
|
+
*/
|
|
99
103
|
adapter: RemoteThreadListAdapter;
|
|
100
104
|
|
|
101
105
|
/**
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { ThreadListAdapterChangedError } from "../../runtimes/remote-thread-list/adapter-changed";
|
|
3
|
+
import { handleRuntimeAction } from "./handle-runtime-action";
|
|
4
|
+
|
|
5
|
+
describe("handleRuntimeAction", () => {
|
|
6
|
+
it("does not log a silent adapter-change cancellation", async () => {
|
|
7
|
+
const error = new ThreadListAdapterChangedError();
|
|
8
|
+
const consoleError = vi
|
|
9
|
+
.spyOn(console, "error")
|
|
10
|
+
.mockImplementation(() => {});
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
await expect(
|
|
14
|
+
handleRuntimeAction("thread list archive", () => Promise.reject(error)),
|
|
15
|
+
).rejects.toBe(error);
|
|
16
|
+
expect(consoleError).not.toHaveBeenCalled();
|
|
17
|
+
} finally {
|
|
18
|
+
consoleError.mockRestore();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("logs an ordinary rejection", async () => {
|
|
23
|
+
const error = new Error("archive failed");
|
|
24
|
+
const consoleError = vi
|
|
25
|
+
.spyOn(console, "error")
|
|
26
|
+
.mockImplementation(() => {});
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
await expect(
|
|
30
|
+
handleRuntimeAction("thread list archive", () => Promise.reject(error)),
|
|
31
|
+
).rejects.toBe(error);
|
|
32
|
+
expect(consoleError).toHaveBeenCalledExactlyOnceWith(
|
|
33
|
+
"[assistant-ui] thread list archive failed:",
|
|
34
|
+
error,
|
|
35
|
+
);
|
|
36
|
+
} finally {
|
|
37
|
+
consoleError.mockRestore();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isSilentRuntimeAction } from "../../utils/silent-runtime-action";
|
|
2
|
+
|
|
1
3
|
export const handleRuntimeAction = (
|
|
2
4
|
label: string,
|
|
3
5
|
execute: () => Promise<void>,
|
|
@@ -5,6 +7,7 @@ export const handleRuntimeAction = (
|
|
|
5
7
|
const task = execute();
|
|
6
8
|
|
|
7
9
|
void task.catch((error: unknown) => {
|
|
10
|
+
if (isSilentRuntimeAction(error)) return;
|
|
8
11
|
console.error(`[assistant-ui] ${label} failed:`, error);
|
|
9
12
|
});
|
|
10
13
|
return task;
|