@assistant-ui/core 0.1.15 → 0.1.17
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/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.d.ts +104 -0
- package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -0
- package/dist/react/primitives/message/MessageGroupedParts.js +74 -0
- package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -0
- package/dist/react/primitives/message/MessageParts.d.ts +14 -1
- package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageParts.js +55 -35
- package/dist/react/primitives/message/MessageParts.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +3 -3
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +26 -5
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +2 -2
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +6 -5
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/utils/groupParts.d.ts +49 -0
- package/dist/react/utils/groupParts.d.ts.map +1 -0
- package/dist/react/utils/groupParts.js +97 -0
- package/dist/react/utils/groupParts.js.map +1 -0
- package/dist/runtime/api/bindings.d.ts +1 -0
- package/dist/runtime/api/bindings.d.ts.map +1 -1
- package/dist/runtime/api/composer-runtime.d.ts +7 -5
- package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
- package/dist/runtime/api/composer-runtime.js +1 -1
- package/dist/runtime/api/composer-runtime.js.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.d.ts +18 -3
- package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.js +1 -1
- package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
- package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-runtime.js +1 -0
- package/dist/runtime/api/thread-list-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +5 -5
- 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 +4 -3
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +54 -26
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +3 -3
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +11 -11
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/interfaces/composer-runtime-core.d.ts +28 -2
- package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +1 -0
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts +37 -2
- package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js +2 -2
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +2 -2
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +3 -0
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +13 -1
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.js +5 -6
- package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
- package/dist/store/scopes/composer.d.ts +11 -1
- package/dist/store/scopes/composer.d.ts.map +1 -1
- package/dist/store/scopes/thread-list-item.d.ts +11 -0
- package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
- package/dist/store/scopes/thread.d.ts +17 -0
- package/dist/store/scopes/thread.d.ts.map +1 -1
- package/dist/subscribable/subscribable.d.ts +4 -4
- package/dist/subscribable/subscribable.d.ts.map +1 -1
- package/dist/subscribable/subscribable.js +4 -4
- package/dist/subscribable/subscribable.js.map +1 -1
- package/package.json +21 -9
- package/src/index.ts +10 -0
- package/src/react/adapters/LocalStorageThreadListAdapter.tsx +2 -0
- package/src/react/index.ts +2 -0
- package/src/react/primitives/message/MessageGroupedParts.tsx +186 -0
- package/src/react/primitives/message/MessageParts.tsx +101 -49
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +40 -7
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +6 -12
- package/src/react/utils/groupParts.ts +152 -0
- package/src/runtime/api/bindings.ts +1 -0
- package/src/runtime/api/composer-runtime.ts +14 -11
- package/src/runtime/api/thread-list-item-runtime.ts +28 -6
- package/src/runtime/api/thread-list-runtime.ts +1 -0
- package/src/runtime/api/thread-runtime.ts +11 -7
- package/src/runtime/base/base-composer-runtime-core.ts +99 -35
- package/src/runtime/base/base-thread-runtime-core.ts +21 -12
- package/src/runtime/interfaces/composer-runtime-core.ts +39 -7
- package/src/runtime/interfaces/thread-list-runtime-core.ts +1 -0
- package/src/runtime/interfaces/thread-runtime-core.ts +44 -6
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +2 -2
- package/src/runtimes/local/local-thread-runtime-core.ts +2 -2
- package/src/runtimes/remote-thread-list/remote-thread-state.ts +3 -0
- package/src/runtimes/remote-thread-list/types.ts +13 -1
- package/src/store/runtime-clients/composer-runtime-client.ts +5 -9
- package/src/store/scopes/composer.ts +11 -0
- package/src/store/scopes/thread-list-item.ts +11 -0
- package/src/store/scopes/thread.ts +17 -0
- package/src/subscribable/subscribable.ts +10 -7
- package/src/tests/RemoteThreadListThreadListRuntimeCore-custom-metadata.test.ts +123 -0
- package/src/tests/base-composer-runtime-core-addAttachment.test.ts +217 -0
- package/src/tests/groupParts.test.ts +114 -0
|
@@ -21,6 +21,8 @@ import type {
|
|
|
21
21
|
SpeechState,
|
|
22
22
|
VoiceSessionState,
|
|
23
23
|
RuntimeCapabilities,
|
|
24
|
+
ThreadRuntimeEventCallback,
|
|
25
|
+
ThreadRuntimeEventPayload,
|
|
24
26
|
ThreadRuntimeEventType,
|
|
25
27
|
StartRunConfig,
|
|
26
28
|
ResumeRunConfig,
|
|
@@ -168,11 +170,14 @@ export abstract class BaseThreadRuntimeCore implements ThreadRuntimeCore {
|
|
|
168
170
|
for (const callback of this._subscriptions) callback();
|
|
169
171
|
}
|
|
170
172
|
|
|
171
|
-
public _notifyEventSubscribers
|
|
173
|
+
public _notifyEventSubscribers<E extends ThreadRuntimeEventType>(
|
|
174
|
+
event: E,
|
|
175
|
+
payload: ThreadRuntimeEventPayload[E],
|
|
176
|
+
) {
|
|
172
177
|
const subscribers = this._eventSubscribers.get(event);
|
|
173
178
|
if (!subscribers) return;
|
|
174
179
|
|
|
175
|
-
for (const callback of subscribers) callback();
|
|
180
|
+
for (const callback of subscribers) callback(payload);
|
|
176
181
|
}
|
|
177
182
|
|
|
178
183
|
public subscribe(callback: () => void): Unsubscribe {
|
|
@@ -432,7 +437,7 @@ export abstract class BaseThreadRuntimeCore implements ThreadRuntimeCore {
|
|
|
432
437
|
protected ensureInitialized() {
|
|
433
438
|
if (!this._isInitialized) {
|
|
434
439
|
this._isInitialized = true;
|
|
435
|
-
this._notifyEventSubscribers("initialize");
|
|
440
|
+
this._notifyEventSubscribers("initialize", {});
|
|
436
441
|
}
|
|
437
442
|
}
|
|
438
443
|
|
|
@@ -453,24 +458,28 @@ export abstract class BaseThreadRuntimeCore implements ThreadRuntimeCore {
|
|
|
453
458
|
|
|
454
459
|
private _eventSubscribers = new Map<
|
|
455
460
|
ThreadRuntimeEventType,
|
|
456
|
-
Set<() => void>
|
|
461
|
+
Set<(payload?: unknown) => void>
|
|
457
462
|
>();
|
|
458
463
|
|
|
459
|
-
public unstable_on
|
|
464
|
+
public unstable_on<E extends ThreadRuntimeEventType>(
|
|
465
|
+
event: E,
|
|
466
|
+
callback: ThreadRuntimeEventCallback<E>,
|
|
467
|
+
) {
|
|
468
|
+
const wrapped = callback as (payload?: unknown) => void;
|
|
460
469
|
if (event === "modelContextUpdate") {
|
|
461
|
-
|
|
470
|
+
// provider.subscribe is `() => void`; pump the typed empty payload to the user callback.
|
|
471
|
+
return this._contextProvider.subscribe?.(() => wrapped({})) ?? (() => {});
|
|
462
472
|
}
|
|
463
473
|
|
|
464
|
-
|
|
474
|
+
let subscribers = this._eventSubscribers.get(event);
|
|
465
475
|
if (!subscribers) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
subscribers.add(callback);
|
|
476
|
+
subscribers = new Set();
|
|
477
|
+
this._eventSubscribers.set(event, subscribers);
|
|
469
478
|
}
|
|
479
|
+
subscribers.add(wrapped);
|
|
470
480
|
|
|
471
481
|
return () => {
|
|
472
|
-
|
|
473
|
-
subscribers.delete(callback);
|
|
482
|
+
this._eventSubscribers.get(event)?.delete(wrapped);
|
|
474
483
|
};
|
|
475
484
|
}
|
|
476
485
|
}
|
|
@@ -5,10 +5,37 @@ import type { Unsubscribe } from "../../types/unsubscribe";
|
|
|
5
5
|
import type { RunConfig } from "../../types/message";
|
|
6
6
|
import type { DictationAdapter } from "../../adapters/speech";
|
|
7
7
|
|
|
8
|
-
export type
|
|
9
|
-
| "
|
|
10
|
-
| "
|
|
11
|
-
| "
|
|
8
|
+
export type AttachmentAddErrorReason =
|
|
9
|
+
| "no-adapter"
|
|
10
|
+
| "not-accepted"
|
|
11
|
+
| "adapter-error";
|
|
12
|
+
|
|
13
|
+
export type AttachmentAddErrorEvent = {
|
|
14
|
+
readonly reason: AttachmentAddErrorReason;
|
|
15
|
+
readonly message: string;
|
|
16
|
+
readonly attachmentId?: string;
|
|
17
|
+
readonly error?: Error;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type ComposerRuntimeEventPayload = {
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated State-derivable. Observe `state.text` clearing via
|
|
23
|
+
* `subscribe` + `getState` instead. Kept for backward compatibility.
|
|
24
|
+
*/
|
|
25
|
+
send: Record<string, never>;
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated State-derivable. Observe `state.attachments` via `subscribe` +
|
|
28
|
+
* `getState` instead. Kept for backward compatibility.
|
|
29
|
+
*/
|
|
30
|
+
attachmentAdd: Record<string, never>;
|
|
31
|
+
attachmentAddError: AttachmentAddErrorEvent;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type ComposerRuntimeEventType = keyof ComposerRuntimeEventPayload;
|
|
35
|
+
|
|
36
|
+
export type ComposerRuntimeEventCallback<E extends ComposerRuntimeEventType> = (
|
|
37
|
+
payload: ComposerRuntimeEventPayload[E],
|
|
38
|
+
) => void;
|
|
12
39
|
|
|
13
40
|
export type DictationState = {
|
|
14
41
|
readonly status: DictationAdapter.Status;
|
|
@@ -56,9 +83,14 @@ export type ComposerRuntimeCore = Readonly<{
|
|
|
56
83
|
|
|
57
84
|
subscribe: (callback: () => void) => Unsubscribe;
|
|
58
85
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
86
|
+
/**
|
|
87
|
+
* @deprecated This API is still under active development and might change without notice.
|
|
88
|
+
* For state-derivable transitions, prefer `subscribe` + `getState`. This channel is the
|
|
89
|
+
* escape hatch for transient occurrences not represented in state.
|
|
90
|
+
*/
|
|
91
|
+
unstable_on: <E extends ComposerRuntimeEventType>(
|
|
92
|
+
event: E,
|
|
93
|
+
callback: ComposerRuntimeEventCallback<E>,
|
|
62
94
|
) => Unsubscribe;
|
|
63
95
|
}>;
|
|
64
96
|
|
|
@@ -69,11 +69,41 @@ export type SubmittedFeedback = {
|
|
|
69
69
|
readonly type: "negative" | "positive";
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
-
export type
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
export type ThreadRuntimeEventPayload = {
|
|
73
|
+
/**
|
|
74
|
+
* @deprecated State-derivable. Observe `state.isRunning` flipping to `true`
|
|
75
|
+
* via `subscribe` + `getState` instead. Note: this event fires at the
|
|
76
|
+
* transition point and may run before the next subscriber notification.
|
|
77
|
+
* Kept for backward compatibility.
|
|
78
|
+
*/
|
|
79
|
+
runStart: Record<string, never>;
|
|
80
|
+
/**
|
|
81
|
+
* @deprecated State-derivable. Observe `state.isRunning` flipping to `false`
|
|
82
|
+
* via `subscribe` + `getState` instead. Note: this event fires at the
|
|
83
|
+
* transition point and may run before the next subscriber notification.
|
|
84
|
+
* Kept for backward compatibility.
|
|
85
|
+
*/
|
|
86
|
+
runEnd: Record<string, never>;
|
|
87
|
+
/**
|
|
88
|
+
* @deprecated State-derivable. This event fires at the initialization
|
|
89
|
+
* transition immediately BEFORE the first message is added, so reading state
|
|
90
|
+
* inside the handler still sees an empty thread; observe `state.messages`
|
|
91
|
+
* becoming non-empty via a regular `subscribe` callback instead. Kept for
|
|
92
|
+
* backward compatibility.
|
|
93
|
+
*/
|
|
94
|
+
initialize: Record<string, never>;
|
|
95
|
+
/**
|
|
96
|
+
* Truly transient. The model context lives in a provider, not in thread
|
|
97
|
+
* state, so this event has no state-derivable equivalent.
|
|
98
|
+
*/
|
|
99
|
+
modelContextUpdate: Record<string, never>;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export type ThreadRuntimeEventType = keyof ThreadRuntimeEventPayload;
|
|
103
|
+
|
|
104
|
+
export type ThreadRuntimeEventCallback<E extends ThreadRuntimeEventType> = (
|
|
105
|
+
payload: ThreadRuntimeEventPayload[E],
|
|
106
|
+
) => void;
|
|
77
107
|
|
|
78
108
|
export type StartRunConfig = {
|
|
79
109
|
parentId: string | null;
|
|
@@ -155,7 +185,15 @@ export type ThreadRuntimeCore = Readonly<{
|
|
|
155
185
|
|
|
156
186
|
reset(initialMessages?: readonly ThreadMessageLike[]): void;
|
|
157
187
|
|
|
158
|
-
|
|
188
|
+
/**
|
|
189
|
+
* @deprecated This API is still under active development and might change without notice.
|
|
190
|
+
* For state-derivable transitions, prefer `subscribe` + `getState`. This channel is the
|
|
191
|
+
* escape hatch for transient occurrences not represented in state.
|
|
192
|
+
*/
|
|
193
|
+
unstable_on<E extends ThreadRuntimeEventType>(
|
|
194
|
+
event: E,
|
|
195
|
+
callback: ThreadRuntimeEventCallback<E>,
|
|
196
|
+
): Unsubscribe;
|
|
159
197
|
|
|
160
198
|
/**
|
|
161
199
|
* @deprecated Use importExternalState instead. This method will be removed in 0.12.0.
|
|
@@ -236,9 +236,9 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
236
236
|
|
|
237
237
|
if ((oldStore?.isRunning ?? false) !== (store.isRunning ?? false)) {
|
|
238
238
|
if (store.isRunning) {
|
|
239
|
-
this._notifyEventSubscribers("runStart");
|
|
239
|
+
this._notifyEventSubscribers("runStart", {});
|
|
240
240
|
} else {
|
|
241
|
-
this._notifyEventSubscribers("runEnd");
|
|
241
|
+
this._notifyEventSubscribers("runEnd", {});
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
|
|
@@ -253,7 +253,7 @@ export class LocalThreadRuntimeCore
|
|
|
253
253
|
createdAt: new Date(),
|
|
254
254
|
};
|
|
255
255
|
|
|
256
|
-
this._notifyEventSubscribers("runStart");
|
|
256
|
+
this._notifyEventSubscribers("runStart", {});
|
|
257
257
|
|
|
258
258
|
try {
|
|
259
259
|
this._suggestions = [];
|
|
@@ -271,7 +271,7 @@ export class LocalThreadRuntimeCore
|
|
|
271
271
|
runCallback = undefined;
|
|
272
272
|
} while (shouldContinue(message, this._options.unstable_humanToolNames));
|
|
273
273
|
} finally {
|
|
274
|
-
this._notifyEventSubscribers("runEnd");
|
|
274
|
+
this._notifyEventSubscribers("runEnd", {});
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
this._suggestionsController = new AbortController();
|
|
@@ -7,6 +7,7 @@ export type RemoteThreadData =
|
|
|
7
7
|
readonly externalId: undefined;
|
|
8
8
|
readonly status: "new";
|
|
9
9
|
readonly title: undefined;
|
|
10
|
+
readonly custom: undefined;
|
|
10
11
|
}
|
|
11
12
|
| {
|
|
12
13
|
readonly id: string;
|
|
@@ -15,6 +16,7 @@ export type RemoteThreadData =
|
|
|
15
16
|
readonly externalId: undefined;
|
|
16
17
|
readonly status: "regular" | "archived";
|
|
17
18
|
readonly title?: string | undefined;
|
|
19
|
+
readonly custom: undefined;
|
|
18
20
|
}
|
|
19
21
|
| {
|
|
20
22
|
readonly id: string;
|
|
@@ -23,6 +25,7 @@ export type RemoteThreadData =
|
|
|
23
25
|
readonly externalId: string | undefined;
|
|
24
26
|
readonly status: "regular" | "archived";
|
|
25
27
|
readonly title?: string | undefined;
|
|
28
|
+
readonly custom?: Record<string, unknown> | undefined;
|
|
26
29
|
};
|
|
27
30
|
|
|
28
31
|
export type THREAD_MAPPING_ID = string & { __brand: "THREAD_MAPPING_ID" };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ComponentType, PropsWithChildren } from "react";
|
|
1
2
|
import type { ThreadMessage } from "../../types/message";
|
|
2
3
|
import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
|
|
3
4
|
import type { AssistantStream } from "assistant-stream";
|
|
@@ -12,6 +13,7 @@ export type RemoteThreadMetadata = {
|
|
|
12
13
|
readonly remoteId: string;
|
|
13
14
|
readonly externalId?: string | undefined;
|
|
14
15
|
readonly title?: string | undefined;
|
|
16
|
+
readonly custom?: Record<string, unknown> | undefined;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
export type RemoteThreadListResponse = {
|
|
@@ -32,7 +34,17 @@ export type RemoteThreadListAdapter = {
|
|
|
32
34
|
): Promise<AssistantStream>;
|
|
33
35
|
fetch(threadId: string): Promise<RemoteThreadMetadata>;
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Optional React component wrapped around each active thread. Use it to
|
|
39
|
+
* inject per-thread context such as a history or attachments adapter (see
|
|
40
|
+
* `useCloudThreadListAdapter` for the canonical shape).
|
|
41
|
+
*
|
|
42
|
+
* The Provider must render `children` on its first commit; deferring them
|
|
43
|
+
* behind a loading state, a Suspense boundary, or a `useEffect`-gated render
|
|
44
|
+
* is unsupported and leaves thread context unavailable to downstream
|
|
45
|
+
* consumers. Load data inside an always-mounted child instead.
|
|
46
|
+
*/
|
|
47
|
+
unstable_Provider?: ComponentType<PropsWithChildren> | undefined;
|
|
36
48
|
};
|
|
37
49
|
|
|
38
50
|
export type RemoteThreadListOptions = {
|
|
@@ -63,19 +63,15 @@ export const ComposerClient = resource(
|
|
|
63
63
|
unsubscribers.push(unsubscribe);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
// attachmentAddError carries the failed attachment ID
|
|
67
66
|
unsubscribers.push(
|
|
68
|
-
runtime.unstable_on("attachmentAddError", () => {
|
|
69
|
-
|
|
70
|
-
.getState()
|
|
71
|
-
.attachments.findLast(
|
|
72
|
-
(a) =>
|
|
73
|
-
a.status.type === "incomplete" && a.status.reason === "error",
|
|
74
|
-
);
|
|
67
|
+
runtime.unstable_on("attachmentAddError", (payload) => {
|
|
68
|
+
// payload.error omitted: raw Error is not store-serializable; use runtime.unstable_on for it.
|
|
75
69
|
emit("composer.attachmentAddError", {
|
|
76
70
|
threadId: threadIdRef.current,
|
|
77
71
|
...(messageIdRef && { messageId: messageIdRef.current }),
|
|
78
|
-
...(
|
|
72
|
+
...(payload.attachmentId && { attachmentId: payload.attachmentId }),
|
|
73
|
+
reason: payload.reason,
|
|
74
|
+
message: payload.message,
|
|
79
75
|
});
|
|
80
76
|
}),
|
|
81
77
|
);
|
|
@@ -4,6 +4,7 @@ import type { QuoteInfo } from "../../types/quote";
|
|
|
4
4
|
import type { RunConfig } from "../../types/message";
|
|
5
5
|
import type { ComposerRuntime } from "../../runtime/api/composer-runtime";
|
|
6
6
|
import type {
|
|
7
|
+
AttachmentAddErrorReason,
|
|
7
8
|
DictationState,
|
|
8
9
|
SendOptions,
|
|
9
10
|
} from "../../runtime/interfaces/composer-runtime-core";
|
|
@@ -91,12 +92,22 @@ export type ComposerMeta = {
|
|
|
91
92
|
};
|
|
92
93
|
|
|
93
94
|
export type ComposerEvents = {
|
|
95
|
+
/**
|
|
96
|
+
* @deprecated State-derivable. Observe composer `text` clearing via
|
|
97
|
+
* `useAuiState` instead. Kept for backward compatibility.
|
|
98
|
+
*/
|
|
94
99
|
"composer.send": { threadId: string; messageId?: string };
|
|
100
|
+
/**
|
|
101
|
+
* @deprecated State-derivable. Observe composer `attachments` via
|
|
102
|
+
* `useAuiState` instead. Kept for backward compatibility.
|
|
103
|
+
*/
|
|
95
104
|
"composer.attachmentAdd": { threadId: string; messageId?: string };
|
|
96
105
|
"composer.attachmentAddError": {
|
|
97
106
|
threadId: string;
|
|
98
107
|
messageId?: string;
|
|
99
108
|
attachmentId?: string;
|
|
109
|
+
reason: AttachmentAddErrorReason;
|
|
110
|
+
message: string;
|
|
100
111
|
};
|
|
101
112
|
};
|
|
102
113
|
|
|
@@ -7,6 +7,7 @@ export type ThreadListItemState = {
|
|
|
7
7
|
readonly externalId: string | undefined;
|
|
8
8
|
readonly title?: string | undefined;
|
|
9
9
|
readonly status: ThreadListItemStatus;
|
|
10
|
+
readonly custom?: Record<string, unknown> | undefined;
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
export type ThreadListItemMethods = {
|
|
@@ -31,7 +32,17 @@ export type ThreadListItemMeta = {
|
|
|
31
32
|
};
|
|
32
33
|
|
|
33
34
|
export type ThreadListItemEvents = {
|
|
35
|
+
/**
|
|
36
|
+
* @deprecated State-derivable. Compare `s.threads.mainThreadId` against the
|
|
37
|
+
* item's `s.threadListItem.id` via `useAuiState` instead. Kept for backward
|
|
38
|
+
* compatibility.
|
|
39
|
+
*/
|
|
34
40
|
"threadListItem.switchedTo": { threadId: string };
|
|
41
|
+
/**
|
|
42
|
+
* @deprecated State-derivable. Compare `s.threads.mainThreadId` against the
|
|
43
|
+
* item's `s.threadListItem.id` via `useAuiState` instead. Kept for backward
|
|
44
|
+
* compatibility.
|
|
45
|
+
*/
|
|
35
46
|
"threadListItem.switchedAway": { threadId: string };
|
|
36
47
|
};
|
|
37
48
|
|
|
@@ -130,9 +130,26 @@ export type ThreadMeta = {
|
|
|
130
130
|
};
|
|
131
131
|
|
|
132
132
|
export type ThreadEvents = {
|
|
133
|
+
/**
|
|
134
|
+
* @deprecated State-derivable. Observe `isRunning` flipping to `true` via
|
|
135
|
+
* `useAuiState` instead. Kept for backward compatibility.
|
|
136
|
+
*/
|
|
133
137
|
"thread.runStart": { threadId: string };
|
|
138
|
+
/**
|
|
139
|
+
* @deprecated State-derivable. Observe `isRunning` flipping to `false` via
|
|
140
|
+
* `useAuiState` instead. Kept for backward compatibility.
|
|
141
|
+
*/
|
|
134
142
|
"thread.runEnd": { threadId: string };
|
|
143
|
+
/**
|
|
144
|
+
* @deprecated State-derivable. This event fires before the first message is
|
|
145
|
+
* added; observe `messages` becoming non-empty via `useAuiState` instead of
|
|
146
|
+
* reading state inside this event handler. Kept for backward compatibility.
|
|
147
|
+
*/
|
|
135
148
|
"thread.initialize": { threadId: string };
|
|
149
|
+
/**
|
|
150
|
+
* Truly transient. Model context lives in a provider, not in thread state,
|
|
151
|
+
* so this event has no state-derivable equivalent.
|
|
152
|
+
*/
|
|
136
153
|
"thread.modelContextUpdate": { threadId: string };
|
|
137
154
|
};
|
|
138
155
|
|
|
@@ -21,7 +21,10 @@ export type EventSubscribable<TEvent extends string> = {
|
|
|
21
21
|
event: TEvent;
|
|
22
22
|
binding: SubscribableWithState<
|
|
23
23
|
| {
|
|
24
|
-
unstable_on: (
|
|
24
|
+
unstable_on: (
|
|
25
|
+
event: TEvent,
|
|
26
|
+
callback: (payload?: unknown) => void,
|
|
27
|
+
) => Unsubscribe;
|
|
25
28
|
}
|
|
26
29
|
| undefined,
|
|
27
30
|
unknown
|
|
@@ -87,7 +90,7 @@ export class BaseSubscribable {
|
|
|
87
90
|
|
|
88
91
|
// lazy connect/disconnect: only opens upstream subscription while it has subscribers
|
|
89
92
|
export abstract class BaseSubject {
|
|
90
|
-
private _subscriptions = new Set<() => void>();
|
|
93
|
+
private _subscriptions = new Set<(payload?: unknown) => void>();
|
|
91
94
|
private _connection: Unsubscribe | undefined;
|
|
92
95
|
|
|
93
96
|
protected get isConnected() {
|
|
@@ -96,8 +99,8 @@ export abstract class BaseSubject {
|
|
|
96
99
|
|
|
97
100
|
protected abstract _connect(): Unsubscribe;
|
|
98
101
|
|
|
99
|
-
protected notifySubscribers() {
|
|
100
|
-
for (const callback of this._subscriptions) callback();
|
|
102
|
+
protected notifySubscribers(payload?: unknown) {
|
|
103
|
+
for (const callback of this._subscriptions) callback(payload);
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
private _updateConnection() {
|
|
@@ -110,7 +113,7 @@ export abstract class BaseSubject {
|
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
public subscribe(callback: () => void) {
|
|
116
|
+
public subscribe(callback: (payload?: unknown) => void) {
|
|
114
117
|
this._subscriptions.add(callback);
|
|
115
118
|
this._updateConnection();
|
|
116
119
|
|
|
@@ -270,8 +273,8 @@ export class EventSubscriptionSubject<
|
|
|
270
273
|
}
|
|
271
274
|
|
|
272
275
|
protected _connect(): Unsubscribe {
|
|
273
|
-
const callback = () => {
|
|
274
|
-
this.notifySubscribers();
|
|
276
|
+
const callback = (payload?: unknown) => {
|
|
277
|
+
this.notifySubscribers(payload);
|
|
275
278
|
};
|
|
276
279
|
|
|
277
280
|
let lastState = this.config.binding.getState();
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { createCore, makeAdapter } from "./remote-thread-list-test-helpers";
|
|
3
|
+
|
|
4
|
+
describe("RemoteThreadListThreadListRuntimeCore custom metadata", () => {
|
|
5
|
+
it("preserves custom field from list() through to threadItems", async () => {
|
|
6
|
+
const adapter = makeAdapter({
|
|
7
|
+
list: vi.fn(async () => ({
|
|
8
|
+
threads: [
|
|
9
|
+
{
|
|
10
|
+
status: "regular" as const,
|
|
11
|
+
remoteId: "thread-1",
|
|
12
|
+
externalId: "ext-1",
|
|
13
|
+
title: "Test",
|
|
14
|
+
custom: { workspaceId: "ws-1", createdAt: "2026-04-26" },
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
})),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const core = createCore(adapter);
|
|
21
|
+
await core.getLoadThreadsPromise();
|
|
22
|
+
|
|
23
|
+
const item = core.getItemById("thread-1");
|
|
24
|
+
expect(item?.custom).toEqual({
|
|
25
|
+
workspaceId: "ws-1",
|
|
26
|
+
createdAt: "2026-04-26",
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("preserves custom field from fetch() through switchToThread", async () => {
|
|
31
|
+
const adapter = makeAdapter({
|
|
32
|
+
fetch: vi.fn(async (id: string) => ({
|
|
33
|
+
status: "regular" as const,
|
|
34
|
+
remoteId: id,
|
|
35
|
+
externalId: id,
|
|
36
|
+
title: "Test",
|
|
37
|
+
custom: { ownerId: "user-42" },
|
|
38
|
+
})),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const core = createCore(adapter);
|
|
42
|
+
await core.switchToThread("thread-2");
|
|
43
|
+
|
|
44
|
+
const item = core.getItemById("thread-2");
|
|
45
|
+
expect(item?.custom).toEqual({ ownerId: "user-42" });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("leaves custom undefined when adapter omits it", async () => {
|
|
49
|
+
const adapter = makeAdapter({
|
|
50
|
+
list: vi.fn(async () => ({
|
|
51
|
+
threads: [
|
|
52
|
+
{
|
|
53
|
+
status: "regular" as const,
|
|
54
|
+
remoteId: "thread-3",
|
|
55
|
+
externalId: "ext-3",
|
|
56
|
+
title: "Test",
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
})),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const core = createCore(adapter);
|
|
63
|
+
await core.getLoadThreadsPromise();
|
|
64
|
+
|
|
65
|
+
const item = core.getItemById("thread-3");
|
|
66
|
+
expect(item?.custom).toBeUndefined();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("preserves custom across rename optimistic update", async () => {
|
|
70
|
+
const adapter = makeAdapter({
|
|
71
|
+
list: vi.fn(async () => ({
|
|
72
|
+
threads: [
|
|
73
|
+
{
|
|
74
|
+
status: "regular" as const,
|
|
75
|
+
remoteId: "thread-4",
|
|
76
|
+
externalId: "ext-4",
|
|
77
|
+
title: "Old",
|
|
78
|
+
custom: { tag: "important" },
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
})),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const core = createCore(adapter);
|
|
85
|
+
await core.getLoadThreadsPromise();
|
|
86
|
+
await core.rename("thread-4", "New");
|
|
87
|
+
|
|
88
|
+
const item = core.getItemById("thread-4");
|
|
89
|
+
expect(item?.title).toBe("New");
|
|
90
|
+
expect(item?.custom).toEqual({ tag: "important" });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("preserves custom across archive and unarchive optimistic updates", async () => {
|
|
94
|
+
const adapter = makeAdapter({
|
|
95
|
+
list: vi.fn(async () => ({
|
|
96
|
+
threads: [
|
|
97
|
+
{
|
|
98
|
+
status: "regular" as const,
|
|
99
|
+
remoteId: "thread-5",
|
|
100
|
+
externalId: "ext-5",
|
|
101
|
+
title: "Test",
|
|
102
|
+
custom: { workspaceId: "ws-1" },
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
})),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const core = createCore(adapter);
|
|
109
|
+
await core.getLoadThreadsPromise();
|
|
110
|
+
|
|
111
|
+
await core.archive("thread-5");
|
|
112
|
+
expect(core.getItemById("thread-5")?.status).toBe("archived");
|
|
113
|
+
expect(core.getItemById("thread-5")?.custom).toEqual({
|
|
114
|
+
workspaceId: "ws-1",
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
await core.unarchive("thread-5");
|
|
118
|
+
expect(core.getItemById("thread-5")?.status).toBe("regular");
|
|
119
|
+
expect(core.getItemById("thread-5")?.custom).toEqual({
|
|
120
|
+
workspaceId: "ws-1",
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
});
|