@assistant-ui/react-langchain 0.0.31 → 0.0.32

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 (44) hide show
  1. package/dist/attachSubagentTranscripts.d.ts +14 -6
  2. package/dist/attachSubagentTranscripts.d.ts.map +1 -1
  3. package/dist/attachSubagentTranscripts.js +8 -4
  4. package/dist/attachSubagentTranscripts.js.map +1 -1
  5. package/dist/convertMessages.d.ts +3 -3
  6. package/dist/convertMessages.d.ts.map +1 -1
  7. package/dist/converter.d.ts +9 -10
  8. package/dist/converter.d.ts.map +1 -1
  9. package/dist/findForkCheckpointInHistory.d.ts +2 -3
  10. package/dist/findForkCheckpointInHistory.d.ts.map +1 -1
  11. package/dist/hooks.d.ts +14 -15
  12. package/dist/hooks.d.ts.map +1 -1
  13. package/dist/resolveForkCheckpoint.d.ts +1 -2
  14. package/dist/resolveForkCheckpoint.d.ts.map +1 -1
  15. package/dist/runtimeExtras.d.ts +1 -2
  16. package/dist/runtimeExtras.d.ts.map +1 -1
  17. package/dist/sdkIdentity.d.ts +1 -2
  18. package/dist/sdkIdentity.d.ts.map +1 -1
  19. package/dist/sdkIdentity.js +1 -1
  20. package/dist/streamingTiming.d.ts +2 -3
  21. package/dist/streamingTiming.d.ts.map +1 -1
  22. package/dist/types.d.ts +8 -8
  23. package/dist/types.d.ts.map +1 -1
  24. package/dist/uiMessages.d.ts +22 -7
  25. package/dist/uiMessages.d.ts.map +1 -1
  26. package/dist/uiMessages.js +23 -5
  27. package/dist/uiMessages.js.map +1 -1
  28. package/dist/useStreamRuntime.d.ts +3 -3
  29. package/dist/useStreamRuntime.d.ts.map +1 -1
  30. package/dist/useStreamRuntime.js +4 -4
  31. package/dist/useStreamRuntime.js.map +1 -1
  32. package/dist/useSubagentTranscripts.d.ts +3 -4
  33. package/dist/useSubagentTranscripts.d.ts.map +1 -1
  34. package/dist/useSubagentTranscripts.js +127 -33
  35. package/dist/useSubagentTranscripts.js.map +1 -1
  36. package/package.json +12 -11
  37. package/src/attachSubagentTranscripts.test.ts +13 -5
  38. package/src/attachSubagentTranscripts.ts +26 -7
  39. package/src/uiMessages.test.ts +43 -0
  40. package/src/uiMessages.ts +36 -4
  41. package/src/useStreamRuntime.test.tsx +112 -0
  42. package/src/useStreamRuntime.ts +9 -6
  43. package/src/useSubagentTranscripts.test.tsx +795 -52
  44. package/src/useSubagentTranscripts.ts +271 -49
@@ -1,7 +1,13 @@
1
1
  "use client";
2
2
 
3
3
  import { useEffect, useRef, useSyncExternalStore } from "react";
4
- import type { ThreadMessage } from "@assistant-ui/core";
4
+ import {
5
+ stepStreamingTiming,
6
+ type MessageTiming,
7
+ type StreamingTimingState,
8
+ type ThreadMessage,
9
+ type ToolCallTiming,
10
+ } from "@assistant-ui/core";
5
11
  import {
6
12
  convertExternalMessages,
7
13
  createExternalMessageConversionCache,
@@ -10,51 +16,94 @@ import {
10
16
  } from "@assistant-ui/core/react";
11
17
  import { STREAM_CONTROLLER, type AnyStream } from "@langchain/react";
12
18
  import type { BaseMessage } from "@langchain/core/messages";
13
- import { messagesProjection } from "@langchain/langgraph-sdk/stream";
19
+ import {
20
+ channelProjection,
21
+ messagesProjection,
22
+ type Event,
23
+ } from "@langchain/langgraph-sdk/stream";
14
24
  import type { SubagentDiscoverySnapshot } from "@langchain/react";
15
25
  import {
16
26
  attachSubagentTranscripts,
17
27
  type AttachMemo,
18
28
  createAttachMemo,
29
+ type SubagentTranscript,
19
30
  } from "./attachSubagentTranscripts";
20
31
  import { convertLangChainBaseMessage } from "./convertMessages";
32
+ import { groupUIMessagesByParent } from "./converter";
33
+ import { langChainStreamingTimingAccessors } from "./streamingTiming";
21
34
  import type { LangChainBaseMessage, UIMessage } from "./types";
35
+ import {
36
+ createUIFoldMemo,
37
+ foldUIUpdates,
38
+ mergeUIMessages,
39
+ UI_CUSTOM_CHANNELS,
40
+ type UIFoldMemo,
41
+ } from "./uiMessages";
22
42
 
23
43
  export const MAX_SUBAGENT_DEPTH = 16;
24
44
 
45
+ const ROOT_UI_CHANNEL_DEPTH = 1;
46
+
25
47
  const TRANSCRIPT_METADATA = {};
26
48
 
27
- type ProjectionStore = {
28
- getSnapshot(): BaseMessage[];
49
+ const NO_UI_MESSAGES: readonly UIMessage[] = [];
50
+
51
+ const NO_MESSAGE_TIMING: Record<string, MessageTiming> = {};
52
+
53
+ type ProjectionStore<T> = {
54
+ getSnapshot(): T;
29
55
  subscribe(listener: () => void): () => void;
30
56
  };
31
57
 
32
58
  type ProjectionResource = {
33
59
  snapshot: SubagentDiscoverySnapshot;
34
60
  namespace: readonly string[];
35
- store: ProjectionStore;
36
- unsubscribe: () => void;
37
- release: () => void;
61
+ store: ProjectionStore<BaseMessage[]>;
62
+ uiStore: ProjectionStore<readonly Event[]> | undefined;
63
+ dispose: () => void;
38
64
  storeSnapshot: BaseMessage[] | undefined;
39
65
  status: SubagentDiscoverySnapshot["status"] | undefined;
66
+ uiFoldMemo: UIFoldMemo;
67
+ localUiMessages: readonly UIMessage[];
68
+ rootUiMessagesByParent: Map<string, UIMessage[]>;
69
+ uiMessagesByParent: Map<string, UIMessage[]>;
70
+ timingState: StreamingTimingState | null;
71
+ messageTiming: Record<string, MessageTiming>;
72
+ convertedMessageTiming: Record<string, MessageTiming> | undefined;
73
+ convert: useExternalMessageConverter.Callback<LangChainBaseMessage>;
40
74
  uiMessages: readonly UIMessage[];
41
75
  converted: readonly ThreadMessage[] | undefined;
42
- childTranscripts: ReadonlyMap<string, readonly ThreadMessage[]> | undefined;
76
+ childTranscripts: ReadonlyMap<string, SubagentTranscript> | undefined;
43
77
  transcript: readonly ThreadMessage[] | undefined;
78
+ timing: ToolCallTiming | undefined;
79
+ entry: SubagentTranscript | undefined;
44
80
  memo: AttachMemo;
45
81
  cache: ExternalMessageConversionCache;
46
82
  };
47
83
 
84
+ type MeasuredTiming = {
85
+ timing: ToolCallTiming | undefined;
86
+ timingState: StreamingTimingState | null;
87
+ messageTiming: Record<string, MessageTiming>;
88
+ };
89
+
90
+ type NamespaceRequest = {
91
+ id: string;
92
+ attempts: number;
93
+ pending: boolean;
94
+ retryQueued: boolean;
95
+ status: SubagentDiscoverySnapshot["status"];
96
+ };
97
+
48
98
  type SubagentTranscriptSource = {
49
99
  resources: Map<string, ProjectionResource>;
50
- requestedNamespaceIds: Set<string>;
51
- snapshot: ReadonlyMap<string, readonly ThreadMessage[]>;
100
+ namespaceRequests: Map<string, NamespaceRequest>;
101
+ snapshot: ReadonlyMap<string, SubagentTranscript>;
52
102
  listeners: Set<() => void>;
53
103
  controller: AnyStream[typeof STREAM_CONTROLLER] | undefined;
54
104
  uiMessagesByParent: Map<string, UIMessage[]>;
55
- convert: useExternalMessageConverter.Callback<LangChainBaseMessage>;
56
105
  subscribe(listener: () => void): () => void;
57
- getSnapshot(): ReadonlyMap<string, readonly ThreadMessage[]>;
106
+ getSnapshot(): ReadonlyMap<string, SubagentTranscript>;
58
107
  reconcile(
59
108
  controller: AnyStream[typeof STREAM_CONTROLLER],
60
109
  subagents: AnyStream["subagents"],
@@ -66,9 +115,47 @@ type SubagentTranscriptSource = {
66
115
  const sameNamespace = (a: readonly string[], b: readonly string[]) =>
67
116
  a.length === b.length && a.every((segment, index) => segment === b[index]);
68
117
 
118
+ const needsNamespaceResolution = (snapshot: SubagentDiscoverySnapshot) =>
119
+ snapshot.namespace.length === 1 &&
120
+ snapshot.namespace[0] === `tools:${snapshot.id}`;
121
+
122
+ const requestSubagentNamespace = (
123
+ source: SubagentTranscriptSource,
124
+ controller: AnyStream[typeof STREAM_CONTROLLER],
125
+ request: NamespaceRequest,
126
+ ) => {
127
+ request.attempts += 1;
128
+ request.pending = true;
129
+ request.retryQueued = false;
130
+ void controller
131
+ .resolveSubagentNamespace(request.id)
132
+ .catch(() => {})
133
+ .finally(() => {
134
+ if (
135
+ source.controller !== controller ||
136
+ source.namespaceRequests.get(request.id) !== request
137
+ )
138
+ return;
139
+
140
+ request.pending = false;
141
+ if (
142
+ !request.retryQueued &&
143
+ request.attempts === 1 &&
144
+ request.status !== "running"
145
+ ) {
146
+ request.retryQueued = true;
147
+ }
148
+ if (!request.retryQueued || request.attempts >= 2) {
149
+ request.retryQueued = false;
150
+ return;
151
+ }
152
+ requestSubagentNamespace(source, controller, request);
153
+ });
154
+ };
155
+
69
156
  const sameTranscriptEntries = (
70
- a: ReadonlyMap<string, readonly ThreadMessage[]> | undefined,
71
- b: ReadonlyMap<string, readonly ThreadMessage[]>,
157
+ a: ReadonlyMap<string, SubagentTranscript> | undefined,
158
+ b: ReadonlyMap<string, SubagentTranscript>,
72
159
  ) =>
73
160
  a?.size === b.size &&
74
161
  [...b].every(([id, transcript]) => a.get(id) === transcript);
@@ -89,23 +176,61 @@ const collectUIMessages = (
89
176
  const sameUIMessages = (a: readonly UIMessage[], b: readonly UIMessage[]) =>
90
177
  a.length === b.length && a.every((ui, index) => ui === b[index]);
91
178
 
92
- const convertWithUIMessages =
179
+ const foldLocalUIMessages = (resource: ProjectionResource) => {
180
+ const events = resource.uiStore?.getSnapshot();
181
+ if (events === undefined) return NO_UI_MESSAGES;
182
+ return foldUIUpdates(events, resource.uiFoldMemo);
183
+ };
184
+
185
+ const mergeLocalUIMessages = (
186
+ uiMessagesByParent: Map<string, UIMessage[]>,
187
+ localUiMessages: readonly UIMessage[],
188
+ ) => {
189
+ const merged = new Map(uiMessagesByParent);
190
+ for (const [parentId, messages] of groupUIMessagesByParent<UIMessage>(
191
+ localUiMessages,
192
+ )) {
193
+ merged.set(parentId, mergeUIMessages(messages, merged.get(parentId)));
194
+ }
195
+ return merged;
196
+ };
197
+
198
+ const createConverter =
93
199
  (
94
200
  uiMessagesByParent: Map<string, UIMessage[]>,
201
+ messageTiming: Record<string, MessageTiming>,
95
202
  ): useExternalMessageConverter.Callback<LangChainBaseMessage> =>
96
203
  (message, metadata) =>
97
- convertLangChainBaseMessage(message, { ...metadata, uiMessagesByParent });
204
+ convertLangChainBaseMessage(message, {
205
+ ...metadata,
206
+ uiMessagesByParent,
207
+ messageTiming,
208
+ });
209
+
210
+ const updateTiming = (resource: ProjectionResource) => {
211
+ if (resource.timing === undefined) return;
212
+ const startedAt = resource.snapshot.startedAt.getTime();
213
+ const completedAt = resource.snapshot.completedAt?.getTime();
214
+ if (
215
+ resource.timing.startedAt === startedAt &&
216
+ resource.timing.completedAt === completedAt
217
+ )
218
+ return;
219
+ resource.timing = {
220
+ startedAt,
221
+ ...(completedAt !== undefined && { completedAt }),
222
+ };
223
+ };
98
224
 
99
225
  const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
100
226
  const uiMessagesByParent = new Map<string, UIMessage[]>();
101
227
  const source: SubagentTranscriptSource = {
102
228
  resources: new Map(),
103
- requestedNamespaceIds: new Set(),
229
+ namespaceRequests: new Map(),
104
230
  snapshot: new Map(),
105
231
  listeners: new Set(),
106
232
  controller: undefined,
107
233
  uiMessagesByParent,
108
- convert: convertWithUIMessages(uiMessagesByParent),
109
234
  subscribe(listener) {
110
235
  source.listeners.add(listener);
111
236
  return () => source.listeners.delete(listener);
@@ -114,18 +239,19 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
114
239
  return source.snapshot;
115
240
  },
116
241
  reconcile(controller, subagents, uiMessagesByParent) {
117
- if (source.uiMessagesByParent !== uiMessagesByParent) {
118
- source.uiMessagesByParent = uiMessagesByParent;
119
- source.convert = convertWithUIMessages(uiMessagesByParent);
120
- }
242
+ source.uiMessagesByParent = uiMessagesByParent;
243
+ // A resource is rebuilt from scratch when its namespace resolves, which
244
+ // routinely happens after the subagent finished. Its timing was measured
245
+ // while the client watched the task run and cannot be measured again.
246
+ const rebound = new Map<string, MeasuredTiming>();
121
247
 
122
248
  if (source.controller !== controller) {
123
249
  source.dispose();
124
250
  source.controller = controller;
125
251
  }
126
252
 
127
- for (const id of source.requestedNamespaceIds) {
128
- if (!subagents.has(id)) source.requestedNamespaceIds.delete(id);
253
+ for (const id of source.namespaceRequests.keys()) {
254
+ if (!subagents.has(id)) source.namespaceRequests.delete(id);
129
255
  }
130
256
 
131
257
  for (const [id, resource] of source.resources) {
@@ -135,8 +261,13 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
135
261
  snapshot.depth > MAX_SUBAGENT_DEPTH ||
136
262
  !sameNamespace(resource.namespace, snapshot.namespace)
137
263
  ) {
138
- resource.unsubscribe();
139
- resource.release();
264
+ if (snapshot && snapshot.depth <= MAX_SUBAGENT_DEPTH)
265
+ rebound.set(id, {
266
+ timing: resource.timing,
267
+ timingState: resource.timingState,
268
+ messageTiming: resource.messageTiming,
269
+ });
270
+ resource.dispose();
140
271
  source.resources.delete(id);
141
272
  continue;
142
273
  }
@@ -145,52 +276,96 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
145
276
 
146
277
  for (const snapshot of subagents.values()) {
147
278
  if (snapshot.depth > MAX_SUBAGENT_DEPTH) continue;
148
- if (!source.requestedNamespaceIds.has(snapshot.id)) {
149
- source.requestedNamespaceIds.add(snapshot.id);
150
- void controller.resolveSubagentNamespace(snapshot.id).catch(() => {
151
- if (source.controller === controller)
152
- source.requestedNamespaceIds.delete(snapshot.id);
153
- });
279
+ if (!needsNamespaceResolution(snapshot)) {
280
+ source.namespaceRequests.delete(snapshot.id);
281
+ } else {
282
+ const request = source.namespaceRequests.get(snapshot.id);
283
+ if (!request) {
284
+ const nextRequest: NamespaceRequest = {
285
+ id: snapshot.id,
286
+ attempts: 0,
287
+ pending: false,
288
+ retryQueued: false,
289
+ status: snapshot.status,
290
+ };
291
+ source.namespaceRequests.set(snapshot.id, nextRequest);
292
+ requestSubagentNamespace(source, controller, nextRequest);
293
+ } else if (request.status !== snapshot.status) {
294
+ request.status = snapshot.status;
295
+ if (request.pending) {
296
+ request.retryQueued = request.attempts < 2;
297
+ } else if (request.attempts < 2) {
298
+ requestSubagentNamespace(source, controller, request);
299
+ }
300
+ }
154
301
  }
155
302
  if (source.resources.has(snapshot.id)) continue;
156
303
  const acquired = controller.registry.acquire(
157
304
  messagesProjection(snapshot.namespace),
158
305
  );
306
+ const acquiredUI =
307
+ snapshot.namespace.length > ROOT_UI_CHANNEL_DEPTH
308
+ ? controller.registry.acquire(
309
+ channelProjection(UI_CUSTOM_CHANNELS, snapshot.namespace),
310
+ )
311
+ : undefined;
159
312
  const resource: ProjectionResource = {
160
313
  snapshot,
161
314
  namespace: snapshot.namespace,
162
315
  store: acquired.store,
163
- unsubscribe: () => {},
164
- release: acquired.release,
316
+ uiStore: acquiredUI?.store,
317
+ dispose: () => {},
165
318
  storeSnapshot: undefined,
166
319
  status: undefined,
320
+ uiFoldMemo: createUIFoldMemo(),
321
+ localUiMessages: NO_UI_MESSAGES,
322
+ rootUiMessagesByParent: source.uiMessagesByParent,
323
+ uiMessagesByParent: source.uiMessagesByParent,
324
+ timingState: rebound.get(snapshot.id)?.timingState ?? null,
325
+ messageTiming:
326
+ rebound.get(snapshot.id)?.messageTiming ?? NO_MESSAGE_TIMING,
327
+ convertedMessageTiming: undefined,
328
+ convert: createConverter(
329
+ source.uiMessagesByParent,
330
+ NO_MESSAGE_TIMING,
331
+ ),
167
332
  uiMessages: [],
168
333
  converted: undefined,
169
334
  childTranscripts: undefined,
170
335
  transcript: undefined,
336
+ timing: rebound.has(snapshot.id)
337
+ ? rebound.get(snapshot.id)!.timing
338
+ : snapshot.status === "running"
339
+ ? { startedAt: snapshot.startedAt.getTime() }
340
+ : undefined,
341
+ entry: undefined,
171
342
  memo: createAttachMemo(),
172
343
  cache: createExternalMessageConversionCache(),
173
344
  };
174
- resource.unsubscribe = resource.store.subscribe(() => rebuild());
345
+ const unsubscribe = resource.store.subscribe(() => rebuild());
346
+ const unsubscribeUI = resource.uiStore?.subscribe(() => rebuild());
347
+ resource.dispose = () => {
348
+ unsubscribe();
349
+ unsubscribeUI?.();
350
+ acquired.release();
351
+ acquiredUI?.release();
352
+ };
175
353
  source.resources.set(snapshot.id, resource);
176
354
  }
177
355
 
178
356
  rebuild();
179
357
  },
180
358
  dispose() {
181
- for (const resource of source.resources.values()) {
182
- resource.unsubscribe();
183
- resource.release();
184
- }
359
+ for (const resource of source.resources.values()) resource.dispose();
185
360
  source.resources.clear();
186
- source.requestedNamespaceIds.clear();
361
+ source.namespaceRequests.clear();
187
362
  source.snapshot = new Map();
188
363
  for (const listener of source.listeners) listener();
189
364
  },
190
365
  };
191
366
 
192
367
  const rebuild = () => {
193
- const { convert, uiMessagesByParent } = source;
368
+ const { uiMessagesByParent } = source;
194
369
  const resources = [...source.resources.values()];
195
370
  const childrenByParent = new Map<string, ProjectionResource[]>();
196
371
 
@@ -202,7 +377,7 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
202
377
  else childrenByParent.set(parentId, [resource]);
203
378
  }
204
379
 
205
- const transcripts = new Map<string, readonly ThreadMessage[]>();
380
+ const transcripts = new Map<string, SubagentTranscript>();
206
381
  let changed = source.snapshot.size !== resources.length;
207
382
  const built = new Set<string>();
208
383
 
@@ -216,24 +391,57 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
216
391
  for (const child of children) build(child, depth + 1);
217
392
  const childTranscripts = new Map(
218
393
  children.flatMap((child) =>
219
- child.transcript
220
- ? [[child.snapshot.id, child.transcript] as const]
221
- : [],
394
+ child.entry ? [[child.snapshot.id, child.entry] as const] : [],
222
395
  ),
223
396
  );
224
397
  const storeSnapshot = resource.store.getSnapshot();
225
398
  const status = resource.snapshot.status;
226
- const uiMessages = collectUIMessages(storeSnapshot, uiMessagesByParent);
399
+ const stepped = stepStreamingTiming(
400
+ resource.timingState,
401
+ storeSnapshot as LangChainBaseMessage[],
402
+ status === "running",
403
+ langChainStreamingTimingAccessors,
404
+ undefined,
405
+ );
406
+ resource.timingState = stepped.state;
407
+ if (Object.keys(stepped.timings).length > 0) {
408
+ resource.messageTiming = {
409
+ ...resource.messageTiming,
410
+ ...stepped.timings,
411
+ };
412
+ }
413
+ const localUiMessages = foldLocalUIMessages(resource);
414
+ if (
415
+ resource.localUiMessages !== localUiMessages ||
416
+ resource.rootUiMessagesByParent !== uiMessagesByParent ||
417
+ resource.convertedMessageTiming !== resource.messageTiming
418
+ ) {
419
+ resource.localUiMessages = localUiMessages;
420
+ resource.rootUiMessagesByParent = uiMessagesByParent;
421
+ resource.uiMessagesByParent =
422
+ localUiMessages.length === 0
423
+ ? uiMessagesByParent
424
+ : mergeLocalUIMessages(uiMessagesByParent, localUiMessages);
425
+ resource.convert = createConverter(
426
+ resource.uiMessagesByParent,
427
+ resource.messageTiming,
428
+ );
429
+ }
430
+ const uiMessages = collectUIMessages(
431
+ storeSnapshot,
432
+ resource.uiMessagesByParent,
433
+ );
227
434
 
228
435
  const conversionChanged =
229
436
  resource.converted === undefined ||
230
437
  resource.storeSnapshot !== storeSnapshot ||
231
438
  resource.status !== status ||
439
+ resource.convertedMessageTiming !== resource.messageTiming ||
232
440
  !sameUIMessages(resource.uiMessages, uiMessages);
233
441
  if (conversionChanged) {
234
442
  resource.converted = convertExternalMessages(
235
443
  storeSnapshot as LangChainBaseMessage[],
236
- convert,
444
+ resource.convert,
237
445
  status === "running",
238
446
  TRANSCRIPT_METADATA,
239
447
  resource.cache,
@@ -241,6 +449,7 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
241
449
  resource.storeSnapshot = storeSnapshot;
242
450
  resource.status = status;
243
451
  resource.uiMessages = uiMessages;
452
+ resource.convertedMessageTiming = resource.messageTiming;
244
453
  }
245
454
 
246
455
  if (
@@ -258,8 +467,21 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
258
467
  resource.childTranscripts = childTranscripts;
259
468
  }
260
469
 
470
+ updateTiming(resource);
471
+ if (
472
+ resource.entry === undefined ||
473
+ resource.entry.messages !== resource.transcript ||
474
+ resource.entry.timing !== resource.timing
475
+ ) {
476
+ resource.entry = {
477
+ messages: resource.transcript!,
478
+ ...(resource.timing && { timing: resource.timing }),
479
+ };
480
+ changed = true;
481
+ }
482
+
261
483
  if (!source.snapshot.has(resource.snapshot.id)) changed = true;
262
- transcripts.set(resource.snapshot.id, resource.transcript);
484
+ transcripts.set(resource.snapshot.id, resource.entry);
263
485
  };
264
486
 
265
487
  for (const resource of resources) {
@@ -282,7 +504,7 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
282
504
  export const useSubagentTranscripts = (
283
505
  stream: AnyStream,
284
506
  uiMessagesByParent: Map<string, UIMessage[]>,
285
- ): ReadonlyMap<string, readonly ThreadMessage[]> => {
507
+ ): ReadonlyMap<string, SubagentTranscript> => {
286
508
  const sourceRef = useRef<SubagentTranscriptSource | undefined>(undefined);
287
509
  if (!sourceRef.current) {
288
510
  sourceRef.current = createSubagentTranscriptSource();