@assistant-ui/core 0.1.16 → 0.2.0

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.
Files changed (173) hide show
  1. package/dist/index.d.ts +7 -7
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -2
  4. package/dist/index.js.map +1 -1
  5. package/dist/react/index.d.ts +3 -1
  6. package/dist/react/index.d.ts.map +1 -1
  7. package/dist/react/index.js +2 -0
  8. package/dist/react/index.js.map +1 -1
  9. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts +5 -0
  10. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -0
  11. package/dist/react/primitive-hooks/useThreadListLoadMore.js +11 -0
  12. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -0
  13. package/dist/react/primitives/message/MessageGroupedParts.d.ts +104 -0
  14. package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -0
  15. package/dist/react/primitives/message/MessageGroupedParts.js +74 -0
  16. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -0
  17. package/dist/react/primitives/message/MessageParts.d.ts +8 -1
  18. package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
  19. package/dist/react/primitives/message/MessageParts.js +45 -42
  20. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  21. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +2 -4
  22. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  23. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +4 -3
  24. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  25. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +8 -6
  26. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  27. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +86 -38
  28. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  29. package/dist/react/runtimes/useLocalRuntime.d.ts +1 -1
  30. package/dist/react/utils/groupParts.d.ts +49 -0
  31. package/dist/react/utils/groupParts.d.ts.map +1 -0
  32. package/dist/react/utils/groupParts.js +97 -0
  33. package/dist/react/utils/groupParts.js.map +1 -0
  34. package/dist/runtime/api/assistant-runtime.d.ts +0 -33
  35. package/dist/runtime/api/assistant-runtime.d.ts.map +1 -1
  36. package/dist/runtime/api/assistant-runtime.js +0 -23
  37. package/dist/runtime/api/assistant-runtime.js.map +1 -1
  38. package/dist/runtime/api/bindings.d.ts +1 -3
  39. package/dist/runtime/api/bindings.d.ts.map +1 -1
  40. package/dist/runtime/api/composer-runtime.d.ts +3 -3
  41. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  42. package/dist/runtime/api/composer-runtime.js +1 -1
  43. package/dist/runtime/api/composer-runtime.js.map +1 -1
  44. package/dist/runtime/api/message-runtime.d.ts +1 -6
  45. package/dist/runtime/api/message-runtime.d.ts.map +1 -1
  46. package/dist/runtime/api/message-runtime.js.map +1 -1
  47. package/dist/runtime/api/thread-list-item-runtime.d.ts +18 -3
  48. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  49. package/dist/runtime/api/thread-list-item-runtime.js +1 -1
  50. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  51. package/dist/runtime/api/thread-list-runtime.d.ts +4 -0
  52. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  53. package/dist/runtime/api/thread-list-runtime.js +6 -0
  54. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  55. package/dist/runtime/api/thread-runtime.d.ts +6 -29
  56. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  57. package/dist/runtime/api/thread-runtime.js +2 -21
  58. package/dist/runtime/api/thread-runtime.js.map +1 -1
  59. package/dist/runtime/base/base-composer-runtime-core.d.ts +4 -3
  60. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  61. package/dist/runtime/base/base-composer-runtime-core.js +47 -33
  62. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  63. package/dist/runtime/base/base-thread-runtime-core.d.ts +3 -4
  64. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  65. package/dist/runtime/base/base-thread-runtime-core.js +11 -11
  66. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  67. package/dist/runtime/interfaces/composer-runtime-core.d.ts +28 -2
  68. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  69. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +3 -0
  70. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  71. package/dist/runtime/interfaces/thread-runtime-core.d.ts +35 -4
  72. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  73. package/dist/runtime/utils/chat-model-adapter.d.ts +0 -4
  74. package/dist/runtime/utils/chat-model-adapter.d.ts.map +1 -1
  75. package/dist/runtime/utils/external-store-message.d.ts +0 -4
  76. package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
  77. package/dist/runtime/utils/external-store-message.js +0 -7
  78. package/dist/runtime/utils/external-store-message.js.map +1 -1
  79. package/dist/runtimes/assistant-transport/utils.d.ts +0 -9
  80. package/dist/runtimes/assistant-transport/utils.d.ts.map +1 -1
  81. package/dist/runtimes/assistant-transport/utils.js +0 -13
  82. package/dist/runtimes/assistant-transport/utils.js.map +1 -1
  83. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +0 -1
  84. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  85. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +2 -5
  86. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  87. package/dist/runtimes/local/local-thread-runtime-core.d.ts +0 -1
  88. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  89. package/dist/runtimes/local/local-thread-runtime-core.js +2 -6
  90. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  91. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +0 -1
  92. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  93. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +0 -3
  94. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  95. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
  96. package/dist/runtimes/remote-thread-list/empty-thread-core.js +0 -3
  97. package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
  98. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
  99. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  100. package/dist/runtimes/remote-thread-list/remote-thread-state.js +34 -0
  101. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  102. package/dist/runtimes/remote-thread-list/types.d.ts +5 -1
  103. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  104. package/dist/store/clients/thread-message-client.d.ts.map +1 -1
  105. package/dist/store/clients/thread-message-client.js +0 -1
  106. package/dist/store/clients/thread-message-client.js.map +1 -1
  107. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  108. package/dist/store/runtime-clients/composer-runtime-client.js +5 -6
  109. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  110. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  111. package/dist/store/runtime-clients/thread-list-runtime-client.js +3 -0
  112. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  113. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  114. package/dist/store/runtime-clients/thread-runtime-client.js +0 -1
  115. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  116. package/dist/store/scopes/composer.d.ts +11 -1
  117. package/dist/store/scopes/composer.d.ts.map +1 -1
  118. package/dist/store/scopes/message.d.ts +1 -3
  119. package/dist/store/scopes/message.d.ts.map +1 -1
  120. package/dist/store/scopes/thread-list-item.d.ts +10 -0
  121. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  122. package/dist/store/scopes/thread.d.ts +17 -4
  123. package/dist/store/scopes/thread.d.ts.map +1 -1
  124. package/dist/store/scopes/threads.d.ts +3 -0
  125. package/dist/store/scopes/threads.d.ts.map +1 -1
  126. package/dist/subscribable/subscribable.d.ts +4 -4
  127. package/dist/subscribable/subscribable.d.ts.map +1 -1
  128. package/dist/subscribable/subscribable.js +4 -4
  129. package/dist/subscribable/subscribable.js.map +1 -1
  130. package/package.json +25 -13
  131. package/src/index.ts +12 -6
  132. package/src/react/index.ts +3 -0
  133. package/src/react/primitive-hooks/useThreadListLoadMore.ts +15 -0
  134. package/src/react/primitives/message/MessageGroupedParts.tsx +186 -0
  135. package/src/react/primitives/message/MessageParts.tsx +80 -55
  136. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +7 -6
  137. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +96 -43
  138. package/src/react/utils/groupParts.ts +152 -0
  139. package/src/runtime/api/assistant-runtime.ts +0 -62
  140. package/src/runtime/api/bindings.ts +1 -6
  141. package/src/runtime/api/composer-runtime.ts +10 -9
  142. package/src/runtime/api/message-runtime.ts +1 -8
  143. package/src/runtime/api/thread-list-item-runtime.ts +28 -6
  144. package/src/runtime/api/thread-list-runtime.ts +10 -0
  145. package/src/runtime/api/thread-runtime.ts +12 -53
  146. package/src/runtime/base/base-composer-runtime-core.ts +85 -42
  147. package/src/runtime/base/base-thread-runtime-core.ts +21 -13
  148. package/src/runtime/interfaces/composer-runtime-core.ts +39 -7
  149. package/src/runtime/interfaces/thread-list-runtime-core.ts +3 -0
  150. package/src/runtime/interfaces/thread-runtime-core.ts +42 -9
  151. package/src/runtime/utils/chat-model-adapter.ts +0 -5
  152. package/src/runtime/utils/external-store-message.ts +0 -8
  153. package/src/runtimes/assistant-transport/utils.ts +0 -28
  154. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +2 -6
  155. package/src/runtimes/local/local-thread-runtime-core.ts +2 -7
  156. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +0 -4
  157. package/src/runtimes/remote-thread-list/empty-thread-core.ts +0 -4
  158. package/src/runtimes/remote-thread-list/remote-thread-state.ts +54 -1
  159. package/src/runtimes/remote-thread-list/types.ts +6 -1
  160. package/src/store/clients/thread-message-client.ts +0 -1
  161. package/src/store/runtime-clients/composer-runtime-client.ts +5 -9
  162. package/src/store/runtime-clients/thread-list-runtime-client.ts +3 -0
  163. package/src/store/runtime-clients/thread-runtime-client.ts +0 -1
  164. package/src/store/scopes/composer.ts +11 -0
  165. package/src/store/scopes/message.ts +1 -6
  166. package/src/store/scopes/thread-list-item.ts +10 -0
  167. package/src/store/scopes/thread.ts +17 -5
  168. package/src/store/scopes/threads.ts +3 -0
  169. package/src/subscribable/subscribable.ts +10 -7
  170. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +448 -0
  171. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +6 -1
  172. package/src/tests/base-composer-runtime-core-addAttachment.test.ts +63 -0
  173. package/src/tests/groupParts.test.ts +114 -0
@@ -5,12 +5,13 @@ import { OptimisticState } from "../../runtimes/remote-thread-list/optimistic-st
5
5
  import { EMPTY_THREAD_CORE } from "../../runtimes/remote-thread-list/empty-thread-core";
6
6
  import type {
7
7
  RemoteThreadData,
8
- THREAD_MAPPING_ID,
9
8
  RemoteThreadState,
10
9
  } from "../../runtimes/remote-thread-list/remote-thread-state";
11
10
  import {
11
+ classifyThreads,
12
12
  createThreadMappingId,
13
13
  getThreadData,
14
+ normalizeCursor,
14
15
  updateStatusReducer,
15
16
  } from "../../runtimes/remote-thread-list/remote-thread-state";
16
17
  import type { RemoteThreadListOptions } from "../../runtimes/remote-thread-list/types";
@@ -29,11 +30,14 @@ export class RemoteThreadListThreadListRuntimeCore
29
30
  private readonly _hookManager: RemoteThreadListHookInstanceManager;
30
31
 
31
32
  private _loadThreadsPromise: Promise<void> | undefined;
33
+ private _loadMorePromise: Promise<void> | undefined;
32
34
  private _loadGeneration = 0;
33
35
 
34
36
  private _mainThreadId!: string;
35
37
  private readonly _state = new OptimisticState<RemoteThreadState>({
36
38
  isLoading: true,
39
+ isLoadingMore: false,
40
+ cursor: undefined,
37
41
  newThreadId: undefined,
38
42
  threadIds: [],
39
43
  archivedThreadIds: [],
@@ -61,62 +65,33 @@ export class RemoteThreadListThreadListRuntimeCore
61
65
  // biome-ignore lint/suspicious/noThenProperty: OptimisticState reducer pattern
62
66
  then: (state, l) => {
63
67
  if (generation !== this._loadGeneration) return state;
64
- const newThreadIds = [];
65
- const newArchivedThreadIds = [];
66
- const newThreadIdMap = {} as Record<string, THREAD_MAPPING_ID>;
67
- const newThreadData = {} as Record<
68
- THREAD_MAPPING_ID,
69
- RemoteThreadData
70
- >;
71
-
72
- for (const thread of l.threads) {
73
- switch (thread.status) {
74
- case "regular":
75
- newThreadIds.push(thread.remoteId);
76
- break;
77
- case "archived":
78
- newArchivedThreadIds.push(thread.remoteId);
79
- break;
80
- default: {
81
- const _exhaustiveCheck: never = thread.status;
82
- throw new Error(`Unsupported state: ${_exhaustiveCheck}`);
83
- }
84
- }
85
-
86
- const mappingId = createThreadMappingId(thread.remoteId);
87
- newThreadIdMap[thread.remoteId] = mappingId;
88
- newThreadData[mappingId] = {
89
- id: thread.remoteId,
90
- remoteId: thread.remoteId,
91
- externalId: thread.externalId,
92
- status: thread.status,
93
- title: thread.title,
94
- custom: thread.custom,
95
- initializeTask: Promise.resolve({
96
- remoteId: thread.remoteId,
97
- externalId: thread.externalId,
98
- }),
99
- };
100
- }
68
+ const fresh = classifyThreads(l.threads, {
69
+ threadIds: [],
70
+ archivedThreadIds: [],
71
+ threadIdMap: {},
72
+ threadData: {},
73
+ });
101
74
 
102
75
  return {
103
76
  ...state,
104
77
  isLoading: false,
105
- threadIds: newThreadIds,
106
- archivedThreadIds: newArchivedThreadIds,
78
+ cursor: normalizeCursor(l.nextCursor),
79
+ threadIds: fresh.threadIds,
80
+ archivedThreadIds: fresh.archivedThreadIds,
107
81
  threadIdMap: {
108
82
  ...state.threadIdMap,
109
- ...newThreadIdMap,
83
+ ...fresh.threadIdMap,
110
84
  },
111
85
  threadData: {
112
86
  ...state.threadData,
113
- ...newThreadData,
87
+ ...fresh.threadData,
114
88
  },
115
89
  };
116
90
  },
117
91
  })
118
- .catch(() => {
92
+ .catch((error: unknown) => {
119
93
  if (generation !== this._loadGeneration) return;
94
+ console.error("[assistant-ui] thread list load failed:", error);
120
95
  this._loadThreadsPromise = undefined;
121
96
  this._state.update({
122
97
  ...this._state.baseValue,
@@ -129,6 +104,58 @@ export class RemoteThreadListThreadListRuntimeCore
129
104
  return this._loadThreadsPromise;
130
105
  }
131
106
 
107
+ public loadMore(): Promise<void> {
108
+ if (this._loadMorePromise) return this._loadMorePromise;
109
+
110
+ const initialState = this._state.value;
111
+ if (initialState.cursor === undefined || initialState.isLoading) {
112
+ return Promise.resolve();
113
+ }
114
+
115
+ const generation = this._loadGeneration;
116
+ const adapter = this._options.adapter;
117
+ const cursor = initialState.cursor;
118
+
119
+ const dedup = this._state
120
+ .optimisticUpdate({
121
+ execute: () => adapter.list({ after: cursor }),
122
+ loading: (state) => ({ ...state, isLoadingMore: true }),
123
+ // biome-ignore lint/suspicious/noThenProperty: OptimisticState reducer pattern
124
+ then: (state, l) => {
125
+ if (generation !== this._loadGeneration) return state;
126
+ if (adapter !== this._options.adapter) return state;
127
+
128
+ const appended = classifyThreads(l.threads, {
129
+ threadIds: [...state.threadIds],
130
+ archivedThreadIds: [...state.archivedThreadIds],
131
+ threadIdMap: { ...state.threadIdMap },
132
+ threadData: { ...state.threadData },
133
+ });
134
+
135
+ return {
136
+ ...state,
137
+ isLoadingMore: false,
138
+ cursor: normalizeCursor(l.nextCursor),
139
+ threadIds: appended.threadIds,
140
+ archivedThreadIds: appended.archivedThreadIds,
141
+ threadIdMap: appended.threadIdMap,
142
+ threadData: appended.threadData,
143
+ };
144
+ },
145
+ })
146
+ .catch((error: unknown) => {
147
+ console.error("[assistant-ui] thread list loadMore failed:", error);
148
+ })
149
+ .then(() => {
150
+ if (this._loadMorePromise === dedup) {
151
+ this._loadMorePromise = undefined;
152
+ }
153
+ });
154
+
155
+ this._loadMorePromise = dedup;
156
+ return dedup;
157
+ }
158
+
132
159
  private readonly contextProvider: ModelContextProvider;
133
160
 
134
161
  constructor(
@@ -156,6 +183,9 @@ export class RemoteThreadListThreadListRuntimeCore
156
183
  public __internal_setOptions(options: RemoteThreadListOptions) {
157
184
  if (this._options === options) return;
158
185
 
186
+ const adapterChanged =
187
+ this._options !== undefined && this._options.adapter !== options.adapter;
188
+
159
189
  this._options = options;
160
190
 
161
191
  const Provider = options.adapter.unstable_Provider ?? Fragment;
@@ -164,6 +194,16 @@ export class RemoteThreadListThreadListRuntimeCore
164
194
  }
165
195
 
166
196
  this._hookManager.setRuntimeHook(options.runtimeHook);
197
+
198
+ if (adapterChanged) {
199
+ this._loadGeneration++;
200
+ this._loadThreadsPromise = undefined;
201
+ this._loadMorePromise = undefined;
202
+ this._state.update({
203
+ ...this._state.baseValue,
204
+ cursor: undefined,
205
+ });
206
+ }
167
207
  }
168
208
 
169
209
  public __internal_load() {
@@ -179,6 +219,11 @@ export class RemoteThreadListThreadListRuntimeCore
179
219
  public reload() {
180
220
  this._loadGeneration++;
181
221
  this._loadThreadsPromise = undefined;
222
+ this._loadMorePromise = undefined;
223
+ this._state.update({
224
+ ...this._state.baseValue,
225
+ cursor: undefined,
226
+ });
182
227
  return this.getLoadThreadsPromise();
183
228
  }
184
229
 
@@ -186,6 +231,14 @@ export class RemoteThreadListThreadListRuntimeCore
186
231
  return this._state.value.isLoading;
187
232
  }
188
233
 
234
+ public get isLoadingMore() {
235
+ return this._state.value.isLoadingMore;
236
+ }
237
+
238
+ public get hasMore() {
239
+ return this._state.value.cursor !== undefined;
240
+ }
241
+
189
242
  public get threadIds() {
190
243
  return this._state.value.threadIds;
191
244
  }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Hierarchical adjacent-coalescing grouping for message parts.
3
+ *
4
+ * Given a group path per part (from `groupBy`), builds a tree of group
5
+ * nodes wrapping individual parts. Adjacent parts sharing a path prefix
6
+ * coalesce into the same group; ungrouped parts are direct children of
7
+ * the root.
8
+ *
9
+ * Each node gets a structural `nodeKey` built from sibling indices
10
+ * (`"0.1.0"`), stable under append-only streaming.
11
+ */
12
+
13
+ /**
14
+ * Public group key type. Group keys must be prefixed with `group-` so
15
+ * that a unified `switch (part.type)` in the renderer can distinguish
16
+ * a group key (e.g. `"group-thought"`) from a real part type
17
+ * (`"text"`, `"tool-call"`).
18
+ */
19
+ export type GroupKey<TKey extends `group-${string}` = `group-${string}`> =
20
+ | TKey
21
+ | readonly TKey[]
22
+ | null
23
+ | undefined;
24
+
25
+ export type GroupNode = GroupNodeGroup | GroupNodePart;
26
+
27
+ export interface GroupNodeGroup {
28
+ readonly type: "group";
29
+ /** Current-level group key (last segment of the path). */
30
+ readonly key: string;
31
+ /** Structural React key: sibling-index path, e.g. `"0.1.0"`. */
32
+ readonly nodeKey: string;
33
+ /** Indices of parts in this subtree, in order. */
34
+ readonly indices: readonly number[];
35
+ readonly children: readonly GroupNode[];
36
+ }
37
+
38
+ export interface GroupNodePart {
39
+ readonly type: "part";
40
+ /** Index of the part in the message. */
41
+ readonly index: number;
42
+ /** Structural React key: sibling-index path within parent. */
43
+ readonly nodeKey: string;
44
+ }
45
+
46
+ const EMPTY_PATH: readonly string[] = Object.freeze([]);
47
+
48
+ /**
49
+ * Normalize a `groupBy` return value to a path array.
50
+ * `null`/`undefined`/`[]` → `[]` (ungrouped).
51
+ * `"foo"` → `["foo"]`. Arrays pass through.
52
+ */
53
+ export const normalizeGroupKey = (key: GroupKey): readonly string[] => {
54
+ if (key == null) return EMPTY_PATH;
55
+ if (typeof key === "string") return [key];
56
+ return key;
57
+ };
58
+
59
+ interface BuildFrame {
60
+ key: string;
61
+ nodeKey: string;
62
+ indices: number[];
63
+ children: GroupNode[];
64
+ nextChildIdx: number;
65
+ }
66
+
67
+ const makeChildNodeKey = (parent: BuildFrame): string => {
68
+ const idx = parent.nextChildIdx++;
69
+ return parent.nodeKey === "" ? String(idx) : `${parent.nodeKey}.${idx}`;
70
+ };
71
+
72
+ /**
73
+ * Build the group tree from an array of normalized group paths.
74
+ * `paths[i]` is the path for part `i`. The output tree contains one
75
+ * `part` node per part and one `group` node per coalesced run.
76
+ */
77
+ export const buildGroupTree = (
78
+ paths: readonly (readonly string[])[],
79
+ ): readonly GroupNode[] => {
80
+ const root: BuildFrame = {
81
+ key: "",
82
+ nodeKey: "",
83
+ indices: [],
84
+ children: [],
85
+ nextChildIdx: 0,
86
+ };
87
+ const stack: BuildFrame[] = [root];
88
+
89
+ const closeTop = (): void => {
90
+ const closing = stack.pop()!;
91
+ const parent = stack[stack.length - 1]!;
92
+ parent.children.push({
93
+ type: "group",
94
+ key: closing.key,
95
+ nodeKey: closing.nodeKey,
96
+ indices: closing.indices,
97
+ children: closing.children,
98
+ });
99
+ };
100
+
101
+ for (let i = 0; i < paths.length; i++) {
102
+ const path = paths[i]!;
103
+
104
+ // Find the longest prefix shared between currently-open groups
105
+ // (excluding root) and this part's path.
106
+ let common = 0;
107
+ while (
108
+ common < stack.length - 1 &&
109
+ common < path.length &&
110
+ stack[common + 1]!.key === path[common]
111
+ ) {
112
+ common++;
113
+ }
114
+
115
+ // Close groups not on this path.
116
+ while (stack.length - 1 > common) {
117
+ closeTop();
118
+ }
119
+
120
+ // Open new groups down to the part's depth.
121
+ while (stack.length - 1 < path.length) {
122
+ const parent = stack[stack.length - 1]!;
123
+ stack.push({
124
+ key: path[stack.length - 1]!,
125
+ nodeKey: makeChildNodeKey(parent),
126
+ indices: [],
127
+ children: [],
128
+ nextChildIdx: 0,
129
+ });
130
+ }
131
+
132
+ // Push this part as a leaf in the deepest open group (or root).
133
+ const top = stack[stack.length - 1]!;
134
+ top.children.push({
135
+ type: "part",
136
+ index: i,
137
+ nodeKey: makeChildNodeKey(top),
138
+ });
139
+
140
+ // Record the part index in every open ancestor group.
141
+ for (let s = 1; s < stack.length; s++) {
142
+ stack[s]!.indices.push(i);
143
+ }
144
+ }
145
+
146
+ // Close any still-open groups.
147
+ while (stack.length > 1) {
148
+ closeTop();
149
+ }
150
+
151
+ return root.children;
152
+ };
@@ -6,8 +6,6 @@ import {
6
6
  type ThreadListRuntime,
7
7
  ThreadListRuntimeImpl,
8
8
  } from "./thread-list-runtime";
9
- import { ExportedMessageRepository } from "../utils/message-repository";
10
- import type { ThreadMessageLike } from "../utils/thread-message-like";
11
9
 
12
10
  export type AssistantRuntime = {
13
11
  /**
@@ -20,49 +18,16 @@ export type AssistantRuntime = {
20
18
  */
21
19
  readonly thread: ThreadRuntime;
22
20
 
23
- /**
24
- * @deprecated This field was renamed to `threads`.
25
- */
26
- readonly threadList: ThreadListRuntime;
27
-
28
- /**
29
- * Switch to a new thread.
30
- *
31
- * @deprecated This method was moved to `threads.switchToNewThread`.
32
- */
33
- switchToNewThread(): void;
34
-
35
- /**
36
- * Switch to a thread.
37
- *
38
- * @param threadId The thread ID to switch to.
39
- * @deprecated This method was moved to `threads.switchToThread`.
40
- */
41
- switchToThread(threadId: string): void;
42
-
43
21
  /**
44
22
  * Register a model context provider. Model context providers are configuration such as system message, temperature, etc. that are set in the frontend.
45
23
  *
46
24
  * @param provider The model context provider to register.
47
25
  */
48
26
  registerModelContextProvider(provider: ModelContextProvider): Unsubscribe;
49
-
50
- /**
51
- * @deprecated This method was renamed to `registerModelContextProvider`.
52
- */
53
- registerModelConfigProvider(provider: ModelContextProvider): Unsubscribe;
54
-
55
- /**
56
- * @deprecated Use `runtime.thread.reset(initialMessages)`.
57
- */
58
- reset: unknown; // make it a type error
59
27
  };
60
28
 
61
29
  export class AssistantRuntimeImpl implements AssistantRuntime {
62
30
  public readonly threads;
63
- public get threadList() {
64
- return this.threads;
65
- }
66
31
 
67
32
  public readonly _thread: ThreadRuntime;
68
33
 
@@ -74,42 +39,15 @@ export class AssistantRuntimeImpl implements AssistantRuntime {
74
39
  }
75
40
 
76
41
  protected __internal_bindMethods() {
77
- this.switchToNewThread = this.switchToNewThread.bind(this);
78
- this.switchToThread = this.switchToThread.bind(this);
79
42
  this.registerModelContextProvider =
80
43
  this.registerModelContextProvider.bind(this);
81
- this.registerModelConfigProvider =
82
- this.registerModelConfigProvider.bind(this);
83
- this.reset = this.reset.bind(this);
84
44
  }
85
45
 
86
46
  public get thread() {
87
47
  return this._thread;
88
48
  }
89
49
 
90
- public switchToNewThread() {
91
- return this._core.threads.switchToNewThread();
92
- }
93
-
94
- public switchToThread(threadId: string) {
95
- return this._core.threads.switchToThread(threadId);
96
- }
97
-
98
50
  public registerModelContextProvider(provider: ModelContextProvider) {
99
51
  return this._core.registerModelContextProvider(provider);
100
52
  }
101
-
102
- public registerModelConfigProvider(provider: ModelContextProvider) {
103
- return this.registerModelContextProvider(provider);
104
- }
105
-
106
- public reset({
107
- initialMessages,
108
- }: {
109
- initialMessages?: ThreadMessageLike[];
110
- } = {}) {
111
- return this._core.threads
112
- .getMainThreadRuntimeCore()
113
- .import(ExportedMessageRepository.fromArray(initialMessages ?? []));
114
- }
115
53
  }
@@ -5,10 +5,7 @@ import type {
5
5
  EditComposerRuntimeCore,
6
6
  ThreadComposerRuntimeCore,
7
7
  } from "../interfaces/composer-runtime-core";
8
- import type {
9
- SpeechState,
10
- SubmittedFeedback,
11
- } from "../interfaces/thread-runtime-core";
8
+ import type { SpeechState } from "../interfaces/thread-runtime-core";
12
9
  import type { ComposerRuntimePath, MessageRuntimePath } from "./paths";
13
10
 
14
11
  export type ComposerRuntimeCoreBinding = SubscribableWithState<
@@ -34,8 +31,6 @@ export type MessageStateBinding = SubscribableWithState<
34
31
  readonly branchNumber: number;
35
32
  readonly branchCount: number;
36
33
  readonly speech: SpeechState | undefined;
37
- /** @deprecated Use `message.metadata.submittedFeedback` instead. This will be removed in 0.12.0. */
38
- readonly submittedFeedback: SubmittedFeedback | undefined;
39
34
  },
40
35
  MessageRuntimePath
41
36
  >;
@@ -12,6 +12,7 @@ import {
12
12
  SKIP_UPDATE,
13
13
  } from "../../subscribable/subscribable";
14
14
  import type {
15
+ ComposerRuntimeEventCallback,
15
16
  ComposerRuntimeEventType,
16
17
  DictationState,
17
18
  EditComposerRuntimeCore,
@@ -222,9 +223,9 @@ export type ComposerRuntime = {
222
223
  /**
223
224
  * @deprecated This API is still under active development and might change without notice.
224
225
  */
225
- unstable_on(
226
- event: ComposerRuntimeEventType,
227
- callback: () => void,
226
+ unstable_on<E extends ComposerRuntimeEventType>(
227
+ event: E,
228
+ callback: ComposerRuntimeEventCallback<E>,
228
229
  ): Unsubscribe;
229
230
  };
230
231
 
@@ -332,19 +333,19 @@ export abstract class ComposerRuntimeImpl implements ComposerRuntime {
332
333
  EventSubscriptionSubject<ComposerRuntimeEventType>
333
334
  >();
334
335
 
335
- public unstable_on(
336
- event: ComposerRuntimeEventType,
337
- callback: () => void,
336
+ public unstable_on<E extends ComposerRuntimeEventType>(
337
+ event: E,
338
+ callback: ComposerRuntimeEventCallback<E>,
338
339
  ): Unsubscribe {
339
340
  let subject = this._eventSubscriptionSubjects.get(event);
340
341
  if (!subject) {
341
- subject = new EventSubscriptionSubject({
342
- event: event,
342
+ subject = new EventSubscriptionSubject<ComposerRuntimeEventType>({
343
+ event,
343
344
  binding: this._core,
344
345
  });
345
346
  this._eventSubscriptionSubjects.set(event, subject);
346
347
  }
347
- return subject.subscribe(callback);
348
+ return subject.subscribe(callback as (payload?: unknown) => void);
348
349
  }
349
350
 
350
351
  public abstract getAttachmentByIndex(idx: number): AttachmentRuntime;
@@ -1,7 +1,4 @@
1
- import type {
2
- SpeechState,
3
- SubmittedFeedback,
4
- } from "../interfaces/thread-runtime-core";
1
+ import type { SpeechState } from "../interfaces/thread-runtime-core";
5
2
  import { symbolInnerMessage } from "../utils/external-store-message";
6
3
  import type {
7
4
  ToolCallMessagePartStatus,
@@ -90,10 +87,6 @@ export type MessageState = ThreadMessage & {
90
87
  * @deprecated This API is still under active development and might change without notice.
91
88
  */
92
89
  readonly speech: SpeechState | undefined;
93
- /**
94
- * @deprecated Use `message.metadata.submittedFeedback` instead. This will be removed in 0.12.0.
95
- */
96
- readonly submittedFeedback: SubmittedFeedback | undefined;
97
90
  };
98
91
 
99
92
  export type { MessageStateBinding } from "./bindings";
@@ -3,7 +3,26 @@ import type { SubscribableWithState } from "../../subscribable/subscribable";
3
3
  import type { ThreadListItemRuntimePath } from "./paths";
4
4
  import type { ThreadListRuntimeCoreBinding } from "./thread-list-runtime";
5
5
 
6
- export type ThreadListItemEventType = "switchedTo" | "switchedAway";
6
+ export type ThreadListItemEventPayload = {
7
+ /**
8
+ * @deprecated State-derivable. Compare `s.threads.mainThreadId` against the
9
+ * item's `s.threadListItem.id` via `useAuiState` instead. Kept for backward
10
+ * compatibility.
11
+ */
12
+ switchedTo: Record<string, never>;
13
+ /**
14
+ * @deprecated State-derivable. Compare `s.threads.mainThreadId` against the
15
+ * item's `s.threadListItem.id` via `useAuiState` instead. Kept for backward
16
+ * compatibility.
17
+ */
18
+ switchedAway: Record<string, never>;
19
+ };
20
+
21
+ export type ThreadListItemEventType = keyof ThreadListItemEventPayload;
22
+
23
+ export type ThreadListItemEventCallback<E extends ThreadListItemEventType> = (
24
+ payload: ThreadListItemEventPayload[E],
25
+ ) => void;
7
26
 
8
27
  import type { ThreadListItemState } from "./bindings";
9
28
  import type { ThreadListItemStatus } from "../interfaces/thread-list-runtime-core";
@@ -27,9 +46,9 @@ export type ThreadListItemRuntime = {
27
46
 
28
47
  subscribe(callback: () => void): Unsubscribe;
29
48
 
30
- unstable_on(
31
- event: ThreadListItemEventType,
32
- callback: () => void,
49
+ unstable_on<E extends ThreadListItemEventType>(
50
+ event: E,
51
+ callback: ThreadListItemEventCallback<E>,
33
52
  ): Unsubscribe;
34
53
 
35
54
  __internal_getRuntime(): ThreadListItemRuntime;
@@ -112,7 +131,10 @@ export class ThreadListItemRuntimeImpl implements ThreadListItemRuntime {
112
131
  return this._threadListBinding.generateTitle(state.id);
113
132
  }
114
133
 
115
- public unstable_on(event: ThreadListItemEventType, callback: () => void) {
134
+ public unstable_on<E extends ThreadListItemEventType>(
135
+ event: E,
136
+ callback: ThreadListItemEventCallback<E>,
137
+ ) {
116
138
  let prevIsMain = this._core.getState().isMain;
117
139
  let prevThreadId = this._core.getState().id;
118
140
  return this.subscribe(() => {
@@ -125,7 +147,7 @@ export class ThreadListItemRuntimeImpl implements ThreadListItemRuntime {
125
147
 
126
148
  if (event === "switchedTo" && !newIsMain) return;
127
149
  if (event === "switchedAway" && newIsMain) return;
128
- callback();
150
+ (callback as (payload?: unknown) => void)({});
129
151
  });
130
152
  }
131
153
 
@@ -28,6 +28,8 @@ export type ThreadListState = {
28
28
  readonly threadIds: readonly string[];
29
29
  readonly archivedThreadIds: readonly string[];
30
30
  readonly isLoading: boolean;
31
+ readonly isLoadingMore: boolean;
32
+ readonly hasMore: boolean;
31
33
  readonly threadItems: Readonly<
32
34
  Record<string, Omit<ThreadListItemState, "isMain" | "threadId">>
33
35
  >;
@@ -51,6 +53,7 @@ export type ThreadListRuntime = {
51
53
 
52
54
  getLoadThreadsPromise(): Promise<void>;
53
55
  reload(): Promise<void>;
56
+ loadMore(): Promise<void>;
54
57
  };
55
58
 
56
59
  const getThreadListState = (
@@ -62,6 +65,8 @@ const getThreadListState = (
62
65
  threadIds: threadList.threadIds,
63
66
  archivedThreadIds: threadList.archivedThreadIds,
64
67
  isLoading: threadList.isLoading,
68
+ isLoadingMore: threadList.isLoadingMore ?? false,
69
+ hasMore: threadList.hasMore ?? false,
65
70
  threadItems: threadList.threadItems,
66
71
  };
67
72
  };
@@ -138,6 +143,7 @@ export class ThreadListRuntimeImpl implements ThreadListRuntime {
138
143
  this.switchToNewThread = this.switchToNewThread.bind(this);
139
144
  this.getLoadThreadsPromise = this.getLoadThreadsPromise.bind(this);
140
145
  this.reload = this.reload.bind(this);
146
+ this.loadMore = this.loadMore.bind(this);
141
147
  this.getState = this.getState.bind(this);
142
148
  this.subscribe = this.subscribe.bind(this);
143
149
  this.getById = this.getById.bind(this);
@@ -162,6 +168,10 @@ export class ThreadListRuntimeImpl implements ThreadListRuntime {
162
168
  return this._core.reload?.() ?? RESOLVED_PROMISE;
163
169
  }
164
170
 
171
+ public loadMore(): Promise<void> {
172
+ return this._core.loadMore?.() ?? RESOLVED_PROMISE;
173
+ }
174
+
165
175
  public getState(): ThreadListState {
166
176
  return this._getState();
167
177
  }