@assistant-ui/react-ag-ui 0.1.5 → 0.1.6

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.
@@ -63,11 +63,41 @@ const scanJSONContainerDelta = (scanner, delta) => {
63
63
  return completed && scanner.state === "complete";
64
64
  };
65
65
  const isPlainObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
66
+ const createMessageBuffer = (id) => ({
67
+ id,
68
+ textParts: /* @__PURE__ */ new Map(),
69
+ openTextFrames: /* @__PURE__ */ new Set(),
70
+ activeTextMessageIdByScope: /* @__PURE__ */ new Map(),
71
+ reasoningParts: /* @__PURE__ */ new Map(),
72
+ reasoningSignatures: /* @__PURE__ */ new Map(),
73
+ reasoningSignatureIds: /* @__PURE__ */ new Map(),
74
+ hiddenSignatures: /* @__PURE__ */ new Map(),
75
+ hiddenSignatureAnchors: /* @__PURE__ */ new Map(),
76
+ hiddenBlockAnchors: /* @__PURE__ */ new Map(),
77
+ hiddenReasoningIds: /* @__PURE__ */ new Set(),
78
+ hiddenActiveReasoningId: void 0,
79
+ hasEmittedOpaqueReasoning: false,
80
+ loggedDroppedOpaqueIds: /* @__PURE__ */ new Set(),
81
+ reasoningMessageIds: /* @__PURE__ */ new Map(),
82
+ activeReasoningKeyByScope: /* @__PURE__ */ new Map(),
83
+ subagentRuns: /* @__PURE__ */ new Map(),
84
+ nestingMemo: void 0,
85
+ toolCalls: /* @__PURE__ */ new Map(),
86
+ a2uiBuckets: /* @__PURE__ */ new Map(),
87
+ a2uiToolCallIds: /* @__PURE__ */ new Set(),
88
+ activityParts: /* @__PURE__ */ new Map(),
89
+ lastResolvedToolCallIdByScope: /* @__PURE__ */ new Map(),
90
+ partOrder: [],
91
+ lastReasoningBeforeToolCall: void 0,
92
+ textPartCounter: 0,
93
+ reportedServerMessageId: void 0
94
+ });
66
95
  /**
67
96
  * Collects AG-UI events into assistant-ui run snapshots that can be yielded from a ChatModelAdapter.
68
97
  *
69
- * The aggregator keeps a single assistant message worth of parts. Each incoming event updates the parts and
70
- * emits a fresh snapshot through the provided `emit` callback. `CUSTOM` events
98
+ * Each incoming event updates the parts of the assistant message it names and
99
+ * emits a fresh snapshot: through `emit` for the message the run streams into,
100
+ * and through `onRetainedMessage` for one it moved on from. `CUSTOM` events
71
101
  * become canonical data parts; integration plumbing names such as
72
102
  * `on_interrupt`, `PredictState`, `Exit`, `hook_error`, `state_update_error`,
73
103
  * `system:*`, and `MultiAgentHandoff` are forwarded the same way, so apps only
@@ -80,36 +110,17 @@ var RunAggregator = class {
80
110
  logger;
81
111
  onServerMessageId;
82
112
  onTextMessageStart;
113
+ onRetainedMessage;
114
+ canRetainMessage;
83
115
  status;
84
116
  interrupts;
85
117
  pendingToolCallIds;
86
- textParts = /* @__PURE__ */ new Map();
87
118
  pendingSnapshotText = /* @__PURE__ */ new Map();
88
- activeTextMessageIdByScope = /* @__PURE__ */ new Map();
89
- reasoningParts = /* @__PURE__ */ new Map();
90
- reasoningSignatures = /* @__PURE__ */ new Map();
91
- reasoningSignatureIds = /* @__PURE__ */ new Map();
92
- hiddenSignatures = /* @__PURE__ */ new Map();
93
- hiddenSignatureAnchors = /* @__PURE__ */ new Map();
94
- hiddenBlockAnchors = /* @__PURE__ */ new Map();
95
- hiddenReasoningIds = /* @__PURE__ */ new Set();
96
- hiddenActiveReasoningId;
97
- hasEmittedOpaqueReasoning = false;
98
- loggedDroppedOpaqueIds = /* @__PURE__ */ new Set();
99
- reasoningMessageIds = /* @__PURE__ */ new Map();
100
- activeReasoningKeyByScope = /* @__PURE__ */ new Map();
101
- subagentRuns = /* @__PURE__ */ new Map();
102
- nestingMemo;
103
- toolCalls = /* @__PURE__ */ new Map();
104
- a2uiBuckets = /* @__PURE__ */ new Map();
105
- a2uiToolCallIds = /* @__PURE__ */ new Set();
106
- activityParts = /* @__PURE__ */ new Map();
107
- lastResolvedToolCallIdByScope = /* @__PURE__ */ new Map();
108
- partOrder = [];
109
- lastReasoningBeforeToolCall;
110
- textPartCounter = 0;
119
+ current = createMessageBuffer();
120
+ buf = this.current;
121
+ retained = /* @__PURE__ */ new Map();
122
+ unannounced;
111
123
  serverMessageIdReported = false;
112
- reportedServerMessageId;
113
124
  lastTextMessageId;
114
125
  streamStartTime;
115
126
  firstTokenTime;
@@ -121,41 +132,186 @@ var RunAggregator = class {
121
132
  this.logger = options.logger;
122
133
  this.onServerMessageId = options.onServerMessageId;
123
134
  this.onTextMessageStart = options.onTextMessageStart;
135
+ this.onRetainedMessage = options.onRetainedMessage;
136
+ this.canRetainMessage = options.canRetainMessage;
124
137
  }
125
138
  hasToolCall(toolCallId) {
126
- return this.toolCalls.has(toolCallId);
139
+ return this.buffers().some((buffer) => buffer.toolCalls.has(toolCallId));
140
+ }
141
+ isRetained(messageId) {
142
+ return this.retained.has(messageId);
127
143
  }
128
- rebaseMessage(messageId) {
129
- const snapshotText = this.pendingSnapshotText.get(messageId);
130
- this.resetMessageParts();
144
+ /**
145
+ * Makes `messageId` the message the run streams into. The previous message
146
+ * keeps its parts under `previousId`, and returns true when it does; call
147
+ * `announceRetained` to publish its state once the caller can apply it.
148
+ */
149
+ rebaseMessage(messageId, previousId) {
150
+ const retained = this.openMessage(messageId, previousId);
131
151
  this.lastTextMessageId = messageId;
132
- this.reportedServerMessageId = messageId;
133
- if (snapshotText !== void 0) this.pendingSnapshotText.set(messageId, snapshotText);
152
+ this.current.reportedServerMessageId = messageId;
134
153
  this.startTextMessage(ROOT_SCOPE, messageId);
154
+ if (retained) this.unannounced = retained;
155
+ return retained !== void 0;
156
+ }
157
+ announceRetained() {
158
+ const buffer = this.unannounced;
159
+ this.unannounced = void 0;
160
+ if (buffer && this.retained.get(buffer.id) === buffer) this.withBuffer(buffer, () => this.emit());
135
161
  }
136
162
  handle(event) {
163
+ const owner = this.ownerOf(event);
164
+ if (owner === this.current) this.apply(event);
165
+ else this.withBuffer(owner, () => this.apply(event));
166
+ }
167
+ buffers() {
168
+ return [this.current, ...this.retained.values()];
169
+ }
170
+ withBuffer(buffer, fn) {
171
+ const previous = this.buf;
172
+ this.buf = buffer;
173
+ try {
174
+ fn();
175
+ } finally {
176
+ this.buf = previous;
177
+ }
178
+ }
179
+ openMessage(messageId, previousId) {
180
+ const outgoing = this.current;
181
+ const next = this.retained.get(messageId) ?? createMessageBuffer(messageId);
182
+ this.retained.delete(messageId);
183
+ let retained;
184
+ if (previousId !== void 0 && previousId !== messageId) {
185
+ outgoing.id = previousId;
186
+ this.retained.set(previousId, outgoing);
187
+ retained = outgoing;
188
+ }
189
+ this.current = next;
190
+ this.buf = next;
191
+ return retained;
192
+ }
193
+ ownerForMessage(messageId) {
194
+ if (!messageId || messageId === this.current.id) return this.current;
195
+ const retained = this.retained.get(messageId);
196
+ if (retained) return retained;
197
+ if (this.canRetainMessage?.(messageId)) {
198
+ const buffer = createMessageBuffer(messageId);
199
+ this.retained.set(messageId, buffer);
200
+ if (this.pendingSnapshotText.has(messageId)) this.withBuffer(buffer, () => this.startTextMessage(ROOT_SCOPE, messageId));
201
+ return buffer;
202
+ }
203
+ return this.current;
204
+ }
205
+ ownerForToolCall(toolCallId) {
206
+ if (typeof toolCallId !== "string") return void 0;
207
+ return this.buffers().find((buffer) => buffer.toolCalls.has(toolCallId));
208
+ }
209
+ ownerForSubagent(subagentRunId) {
210
+ return this.buffers().find((buffer) => buffer.subagentRuns.has(subagentRunId)) ?? this.current;
211
+ }
212
+ ownerForActivity(event) {
213
+ if (event.subagentRunId !== void 0) return this.ownerForSubagent(event.subagentRunId);
214
+ if (event.messageId === void 0) return this.current;
215
+ const key = `message:${event.messageId}`;
216
+ return this.buffers().find((buffer) => buffer.activityParts.has(key)) ?? this.current;
217
+ }
218
+ ownerOf(event) {
219
+ if (this.joinStrategy === "concat-content") return this.current;
137
220
  switch (event.type) {
138
- case "RUN_STARTED":
139
- this.resetMessageParts();
221
+ case "TEXT_MESSAGE_START":
222
+ case "TEXT_MESSAGE_CONTENT":
223
+ case "TEXT_MESSAGE_END": return event.subagentRunId !== void 0 ? this.ownerForSubagent(event.subagentRunId) : this.ownerForMessage(event.messageId);
224
+ case "TOOL_CALL_START": return event.subagentRunId !== void 0 ? this.ownerForSubagent(event.subagentRunId) : this.ownerForMessage(event.parentMessageId);
225
+ case "TOOL_CALL_ARGS":
226
+ case "TOOL_CALL_END":
227
+ case "TOOL_CALL_RESULT": return this.ownerForToolCall(event.toolCallId) ?? (event.subagentRunId !== void 0 ? this.ownerForSubagent(event.subagentRunId) : this.current);
228
+ case "SUBAGENT_STARTED": {
229
+ const existing = this.buffers().find((buffer) => buffer.subagentRuns.has(event.subagentRunId));
230
+ if (existing) return existing;
231
+ if (event.parentToolCallId !== void 0) {
232
+ const owner = this.ownerForToolCall(event.parentToolCallId);
233
+ if (owner) return owner;
234
+ }
235
+ if (event.parentSubagentRunId !== void 0) return this.ownerForSubagent(event.parentSubagentRunId);
236
+ return this.ownerForMessage(event.parentMessageId);
237
+ }
238
+ case "SUBAGENT_FINISHED":
239
+ case "SUBAGENT_ERROR": return this.ownerForSubagent(event.subagentRunId);
240
+ case "ACTIVITY_SNAPSHOT":
241
+ if (event.activityType === "mcp-apps") {
242
+ const owner = this.ownerForToolCall(event.content.toolCallId);
243
+ if (owner) return owner;
244
+ }
245
+ if (event.activityType === "a2ui-surface") return this.current;
246
+ return this.ownerForActivity(event);
247
+ case "ACTIVITY_DELTA": return this.ownerForActivity(event);
248
+ case "REASONING_START":
249
+ case "REASONING_MESSAGE_START":
250
+ case "REASONING_MESSAGE_CONTENT":
251
+ case "REASONING_MESSAGE_END":
252
+ case "REASONING_END":
253
+ case "REASONING_ENCRYPTED_VALUE": {
254
+ if (event.subagentRunId !== void 0) return this.ownerForSubagent(event.subagentRunId);
255
+ const id = event.type === "REASONING_ENCRYPTED_VALUE" ? event.entityId : event.messageId;
256
+ return this.buffers().find((buffer) => buffer.reasoningParts.has(id) || buffer.hiddenReasoningIds.has(id)) ?? this.current;
257
+ }
258
+ default: return this.current;
259
+ }
260
+ }
261
+ retainedState(buffer) {
262
+ const openToolCallIds = Array.from(buffer.toolCalls.values()).filter((call) => call.result === void 0 && !buffer.a2uiToolCallIds.has(call.toolCallId)).map((call) => call.toolCallId);
263
+ return {
264
+ messageId: buffer.id,
265
+ openToolCallIds,
266
+ streaming: buffer.openTextFrames.size > 0 || Array.from(buffer.subagentRuns.values()).some((run) => run.status.type === "running")
267
+ };
268
+ }
269
+ apply(event) {
270
+ switch (event.type) {
271
+ case "RUN_STARTED": {
272
+ this.retained.clear();
273
+ this.unannounced = void 0;
274
+ const { hasEmittedOpaqueReasoning } = this.current;
275
+ this.current = createMessageBuffer();
276
+ this.current.hasEmittedOpaqueReasoning = hasEmittedOpaqueReasoning;
277
+ this.buf = this.current;
278
+ this.pendingSnapshotText.clear();
140
279
  this.interrupts = void 0;
141
280
  this.serverMessageIdReported = false;
142
281
  this.lastTextMessageId = void 0;
143
- this.reportedServerMessageId = void 0;
144
282
  this.streamStartTime = Date.now();
145
283
  this.firstTokenTime = void 0;
146
284
  this.totalChunks = 0;
147
285
  this.status = { type: "running" };
148
286
  this.emit();
149
287
  break;
150
- case "MESSAGES_SNAPSHOT":
288
+ }
289
+ case "MESSAGES_SNAPSHOT": {
151
290
  this.pendingSnapshotText.clear();
291
+ const buffers = this.buffers();
152
292
  for (const message of event.messages) {
153
- if (message.role !== "assistant" || typeof message.content !== "string") continue;
154
- const part = this.textParts.get(message.id);
155
- if (part) part.buffer = message.content;
156
- else this.pendingSnapshotText.set(message.id, message.content);
293
+ if (message?.role !== "assistant") continue;
294
+ if (typeof message.content === "string") {
295
+ const part = buffers.map((buffer) => buffer.textParts.get(message.id)).find((entry) => entry !== void 0);
296
+ if (part) part.buffer = message.content;
297
+ else this.pendingSnapshotText.set(message.id, message.content);
298
+ }
299
+ for (const call of Array.isArray(message.toolCalls) ? message.toolCalls : []) {
300
+ const args = call?.function?.arguments;
301
+ if (typeof args !== "string" || args.length === 0) continue;
302
+ for (const buffer of buffers) {
303
+ const entry = buffer.toolCalls.get(call.id);
304
+ if (entry === void 0 || entry.argsText !== "") continue;
305
+ entry.argsText = args;
306
+ entry.argsTextScanner = createJSONContainerScanner();
307
+ scanJSONContainerDelta(entry.argsTextScanner, args);
308
+ const parsed = tryParseJSON(args);
309
+ entry.parsedArgs = isPlainObject(parsed) ? parsed : void 0;
310
+ }
311
+ }
157
312
  }
158
313
  break;
314
+ }
159
315
  case "RUN_FINISHED": {
160
316
  if (event.outcome?.type === "cancelled") {
161
317
  this.interrupts = void 0;
@@ -179,9 +335,9 @@ var RunAggregator = class {
179
335
  this.interrupts = void 0;
180
336
  const pendingToolCallIds = event.outcome?.type === "success" ? event.outcome.pendingToolCallIds : void 0;
181
337
  const hasUnresolvedToolCalls = pendingToolCallIds && pendingToolCallIds.length > 0 ? pendingToolCallIds.some((id) => {
182
- const call = this.toolCalls.get(id);
338
+ const call = this.buf.toolCalls.get(id);
183
339
  return call !== void 0 && call.result === void 0;
184
- }) : Array.from(this.toolCalls.values()).some((tc) => tc.result === void 0);
340
+ }) : Array.from(this.buf.toolCalls.values()).some((tc) => tc.result === void 0);
185
341
  this.status = hasUnresolvedToolCalls ? {
186
342
  type: "requires-action",
187
343
  reason: "tool-calls"
@@ -214,11 +370,12 @@ var RunAggregator = class {
214
370
  break;
215
371
  case "TEXT_MESSAGE_START": {
216
372
  const scope = this.scopeOf(event);
217
- if (scope === ROOT_SCOPE) {
373
+ if (scope === ROOT_SCOPE && this.buf === this.current) {
218
374
  this.beginDistinctTextMessage(event.messageId);
219
375
  this.reportServerMessageId(event.messageId);
220
376
  if (event.messageId) this.lastTextMessageId = event.messageId;
221
377
  }
378
+ if (scope === ROOT_SCOPE && event.messageId) this.buf.openTextFrames.add(event.messageId);
222
379
  this.startTextMessage(scope, event.messageId);
223
380
  this.emit();
224
381
  break;
@@ -226,11 +383,12 @@ var RunAggregator = class {
226
383
  case "TEXT_MESSAGE_CONTENT": {
227
384
  const incomingId = event.messageId;
228
385
  const scope = this.scopeOf(event);
229
- if (scope === ROOT_SCOPE) {
386
+ if (scope === ROOT_SCOPE && this.buf === this.current) {
230
387
  this.beginDistinctTextMessage(incomingId, true);
231
388
  this.reportServerMessageId(incomingId);
232
389
  if (incomingId) this.lastTextMessageId = incomingId;
233
390
  }
391
+ if (scope === ROOT_SCOPE && incomingId) this.buf.openTextFrames.add(incomingId);
234
392
  if (!event.delta) break;
235
393
  this.recordFirstToken();
236
394
  const id = this.resolveTextMessageId(scope, incomingId);
@@ -241,14 +399,17 @@ var RunAggregator = class {
241
399
  }
242
400
  case "TEXT_MESSAGE_END": {
243
401
  const scope = this.scopeOf(event);
244
- if (scope === ROOT_SCOPE) this.reportServerMessageId(event.messageId);
245
- if (event.messageId && this.activeTextMessageIdByScope.get(scope) === this.partKey(scope, event.messageId)) this.activeTextMessageIdByScope.delete(scope);
402
+ if (scope === ROOT_SCOPE && event.messageId) {
403
+ if (this.buf === this.current) this.reportServerMessageId(event.messageId);
404
+ this.buf.openTextFrames.delete(event.messageId);
405
+ }
406
+ if (event.messageId && this.buf.activeTextMessageIdByScope.get(scope) === this.partKey(scope, event.messageId)) this.buf.activeTextMessageIdByScope.delete(scope);
246
407
  this.emit();
247
408
  break;
248
409
  }
249
410
  case "CUSTOM":
250
- this.activeTextMessageIdByScope.delete(ROOT_SCOPE);
251
- this.partOrder.push({
411
+ this.buf.activeTextMessageIdByScope.delete(ROOT_SCOPE);
412
+ this.buf.partOrder.push({
252
413
  kind: "data",
253
414
  name: event.name,
254
415
  value: event.value
@@ -263,14 +424,14 @@ var RunAggregator = class {
263
424
  if (event.subtype === "message") {
264
425
  const scope = this.scopeOf(event);
265
426
  const entityKey = this.partKey(scope, event.entityId);
266
- const key = this.showThinking && this.reasoningParts.has(entityKey) ? entityKey : void 0;
427
+ const key = this.showThinking && this.buf.reasoningParts.has(entityKey) ? entityKey : void 0;
267
428
  if (key !== void 0) {
268
- this.reasoningSignatures.set(key, event.encryptedValue);
269
- this.reasoningSignatureIds.set(key, event.entityId);
429
+ this.buf.reasoningSignatures.set(key, event.encryptedValue);
430
+ this.buf.reasoningSignatureIds.set(key, event.entityId);
270
431
  this.emit();
271
- } else if (!this.showThinking && scope === ROOT_SCOPE && event.entityId.trim().length > 0 && event.encryptedValue.trim().length > 0 && this.hiddenReasoningIds.has(event.entityId)) {
272
- this.hiddenSignatures.set(event.entityId, event.encryptedValue);
273
- this.hiddenSignatureAnchors.set(event.entityId, this.hiddenBlockAnchors.get(event.entityId) ?? this.partOrder.length);
432
+ } else if (!this.showThinking && scope === ROOT_SCOPE && event.entityId.trim().length > 0 && event.encryptedValue.trim().length > 0 && this.buf.hiddenReasoningIds.has(event.entityId)) {
433
+ this.buf.hiddenSignatures.set(event.entityId, event.encryptedValue);
434
+ this.buf.hiddenSignatureAnchors.set(event.entityId, this.buf.hiddenBlockAnchors.get(event.entityId) ?? this.buf.partOrder.length);
274
435
  this.emit();
275
436
  }
276
437
  }
@@ -286,7 +447,7 @@ var RunAggregator = class {
286
447
  break;
287
448
  case "TOOL_CALL_START": {
288
449
  const scope = this.scopeOf(event);
289
- if (scope === ROOT_SCOPE) this.reportServerMessageId(event.parentMessageId);
450
+ if (scope === ROOT_SCOPE && this.buf === this.current) this.reportServerMessageId(event.parentMessageId);
290
451
  this.startToolCall(scope, event.toolCallId, event.toolCallName, event.parentMessageId);
291
452
  this.emit();
292
453
  break;
@@ -314,8 +475,8 @@ var RunAggregator = class {
314
475
  }
315
476
  const activityScope = this.scopeOf(event);
316
477
  const toolCallId = event.content.toolCallId;
317
- const fallbackId = this.lastResolvedToolCallIdByScope.get(activityScope);
318
- const entry = typeof toolCallId === "string" ? this.toolCalls.get(toolCallId) : fallbackId ? this.toolCalls.get(fallbackId) : void 0;
478
+ const fallbackId = this.buf.lastResolvedToolCallIdByScope.get(activityScope);
479
+ const entry = typeof toolCallId === "string" ? this.buf.toolCalls.get(toolCallId) : fallbackId ? this.buf.toolCalls.get(fallbackId) : void 0;
319
480
  const resourceUri = event.content.resourceUri;
320
481
  if (entry && typeof resourceUri === "string" && isMcpAppUri(resourceUri)) {
321
482
  entry.mcpAppResourceUri = resourceUri;
@@ -338,7 +499,7 @@ var RunAggregator = class {
338
499
  break;
339
500
  }
340
501
  case "SUBAGENT_STARTED": {
341
- const existing = this.subagentRuns.get(event.subagentRunId);
502
+ const existing = this.buf.subagentRuns.get(event.subagentRunId);
342
503
  if (existing) {
343
504
  existing.name = event.name;
344
505
  if (event.description !== void 0) existing.description = event.description;
@@ -346,8 +507,8 @@ var RunAggregator = class {
346
507
  this.emit();
347
508
  break;
348
509
  }
349
- this.nestingMemo = void 0;
350
- this.subagentRuns.set(event.subagentRunId, {
510
+ this.buf.nestingMemo = void 0;
511
+ this.buf.subagentRuns.set(event.subagentRunId, {
351
512
  subagentRunId: event.subagentRunId,
352
513
  name: event.name,
353
514
  ...event.description !== void 0 ? { description: event.description } : {},
@@ -361,7 +522,7 @@ var RunAggregator = class {
361
522
  break;
362
523
  }
363
524
  case "SUBAGENT_FINISHED": {
364
- const run = this.subagentRuns.get(event.subagentRunId);
525
+ const run = this.buf.subagentRuns.get(event.subagentRunId);
365
526
  if (run) {
366
527
  run.status = event.outcome?.type === "suspended" ? {
367
528
  type: "requires-action",
@@ -377,7 +538,7 @@ var RunAggregator = class {
377
538
  break;
378
539
  }
379
540
  case "SUBAGENT_ERROR": {
380
- const run = this.subagentRuns.get(event.subagentRunId);
541
+ const run = this.buf.subagentRuns.get(event.subagentRunId);
381
542
  if (run) {
382
543
  run.status = {
383
544
  type: "incomplete",
@@ -397,7 +558,7 @@ var RunAggregator = class {
397
558
  }
398
559
  handleActivityDelta(event) {
399
560
  const scope = this.scopeOf(event);
400
- const existing = this.activityParts.get(this.partKey(scope, `message:${event.messageId}`));
561
+ const existing = this.buf.activityParts.get(this.partKey(scope, `message:${event.messageId}`));
401
562
  if (!existing) {
402
563
  this.logger.debug?.("[agui] activity delta has no snapshot", event);
403
564
  return;
@@ -420,18 +581,18 @@ var RunAggregator = class {
420
581
  const operations = event.content["a2ui_operations"];
421
582
  if (!Array.isArray(operations)) return;
422
583
  const messageId = event.messageId ?? "a2ui:anonymous";
423
- if (event.replace === false && this.a2uiBuckets.has(messageId)) return;
584
+ if (event.replace === false && this.buf.a2uiBuckets.has(messageId)) return;
424
585
  const { state, warnings } = applyA2uiOperations(/* @__PURE__ */ new Map(), operations);
425
586
  for (const warning of warnings) this.logger.debug("[agui] a2ui operation warning", warning);
426
- this.a2uiBuckets.delete(messageId);
427
- this.a2uiBuckets.set(messageId, state);
587
+ this.buf.a2uiBuckets.delete(messageId);
588
+ this.buf.a2uiBuckets.set(messageId, state);
428
589
  this.synthesizeA2uiToolCalls();
429
590
  this.emit();
430
591
  }
431
592
  handleActivitySnapshot(event) {
432
593
  const scope = this.scopeOf(event);
433
594
  const key = this.partKey(scope, event.messageId !== void 0 ? `message:${event.messageId}` : `type:${event.activityType}`);
434
- const existing = this.activityParts.get(key);
595
+ const existing = this.buf.activityParts.get(key);
435
596
  if (existing) {
436
597
  if (event.replace === false) return;
437
598
  existing.name = `agui-activity/${event.activityType}`;
@@ -447,15 +608,15 @@ var RunAggregator = class {
447
608
  value: event.content,
448
609
  subagentRunId: scope
449
610
  };
450
- this.activityParts.set(key, part);
451
- this.partOrder.push(part);
452
- this.activeTextMessageIdByScope.delete(scope);
611
+ this.buf.activityParts.set(key, part);
612
+ this.buf.partOrder.push(part);
613
+ this.buf.activeTextMessageIdByScope.delete(scope);
453
614
  }
454
615
  this.emit();
455
616
  }
456
617
  synthesizeA2uiToolCalls() {
457
618
  const surfaces = /* @__PURE__ */ new Map();
458
- for (const bucket of this.a2uiBuckets.values()) for (const [surfaceId, surface] of bucket) surfaces.set(surfaceId, surface);
619
+ for (const bucket of this.buf.a2uiBuckets.values()) for (const [surfaceId, surface] of bucket) surfaces.set(surfaceId, surface);
459
620
  const activeToolCallIds = /* @__PURE__ */ new Set();
460
621
  for (const [surfaceId, surface] of surfaces) {
461
622
  const { toolCall, warnings } = surfaceToPresentToolCall(surfaceId, surface);
@@ -473,21 +634,21 @@ var RunAggregator = class {
473
634
  artifact: toolCall.artifact,
474
635
  snapshotResultApplied: false
475
636
  };
476
- if (!this.toolCalls.has(toolCallId)) this.partOrder.push({
637
+ if (!this.buf.toolCalls.has(toolCallId)) this.buf.partOrder.push({
477
638
  kind: "tool-call",
478
639
  toolCallId
479
640
  });
480
- this.nestingMemo = void 0;
481
- this.toolCalls.set(toolCallId, entry);
482
- this.a2uiToolCallIds.add(toolCallId);
641
+ this.buf.nestingMemo = void 0;
642
+ this.buf.toolCalls.set(toolCallId, entry);
643
+ this.buf.a2uiToolCallIds.add(toolCallId);
483
644
  }
484
- for (const toolCallId of this.a2uiToolCallIds) {
645
+ for (const toolCallId of this.buf.a2uiToolCallIds) {
485
646
  if (activeToolCallIds.has(toolCallId)) continue;
486
- this.nestingMemo = void 0;
487
- this.toolCalls.delete(toolCallId);
488
- const partIndex = this.partOrder.findIndex((part) => part.kind === "tool-call" && part.toolCallId === toolCallId);
489
- if (partIndex !== -1) this.partOrder.splice(partIndex, 1);
490
- this.a2uiToolCallIds.delete(toolCallId);
647
+ this.buf.nestingMemo = void 0;
648
+ this.buf.toolCalls.delete(toolCallId);
649
+ const partIndex = this.buf.partOrder.findIndex((part) => part.kind === "tool-call" && part.toolCallId === toolCallId);
650
+ if (partIndex !== -1) this.buf.partOrder.splice(partIndex, 1);
651
+ this.buf.a2uiToolCallIds.delete(toolCallId);
491
652
  }
492
653
  }
493
654
  reportServerMessageId(messageId) {
@@ -495,41 +656,19 @@ var RunAggregator = class {
495
656
  if (this.lastTextMessageId === void 0) this.lastTextMessageId = messageId;
496
657
  if (this.serverMessageIdReported) return;
497
658
  this.serverMessageIdReported = true;
498
- this.reportedServerMessageId = messageId;
659
+ this.buf.reportedServerMessageId = messageId;
499
660
  this.onServerMessageId?.(messageId);
500
661
  }
501
- clearTextParts() {
502
- this.textParts.clear();
503
- }
504
- resetMessageParts() {
505
- this.clearTextParts();
506
- this.pendingSnapshotText.clear();
507
- this.reasoningParts.clear();
508
- this.reasoningSignatures.clear();
509
- this.reasoningSignatureIds.clear();
510
- this.reasoningMessageIds.clear();
511
- this.hiddenSignatures.clear();
512
- this.hiddenSignatureAnchors.clear();
513
- this.hiddenBlockAnchors.clear();
514
- this.hiddenReasoningIds.clear();
515
- this.hiddenActiveReasoningId = void 0;
516
- this.loggedDroppedOpaqueIds.clear();
517
- this.activeReasoningKeyByScope.clear();
518
- this.nestingMemo = void 0;
519
- this.subagentRuns.clear();
520
- this.toolCalls.clear();
521
- this.a2uiBuckets.clear();
522
- this.a2uiToolCallIds.clear();
523
- this.activityParts.clear();
524
- this.lastResolvedToolCallIdByScope.clear();
525
- this.partOrder.length = 0;
526
- this.lastReasoningBeforeToolCall = void 0;
527
- this.textPartCounter = 0;
528
- this.activeTextMessageIdByScope.clear();
529
- this.reportedServerMessageId = void 0;
530
- }
531
662
  closeOpenSubagentRuns(status) {
532
- for (const run of this.subagentRuns.values()) if (run.status.type === "running") run.status = status;
663
+ for (const buffer of this.buffers()) {
664
+ let closed = false;
665
+ for (const run of buffer.subagentRuns.values()) {
666
+ if (run.status.type !== "running") continue;
667
+ run.status = status;
668
+ closed = true;
669
+ }
670
+ if (closed && buffer !== this.current) this.withBuffer(buffer, () => this.emit());
671
+ }
533
672
  }
534
673
  scopeOf(event) {
535
674
  return event.subagentRunId ?? ROOT_SCOPE;
@@ -541,38 +680,37 @@ var RunAggregator = class {
541
680
  if (this.joinStrategy === "concat-content" || !messageId || this.lastTextMessageId === void 0 || this.lastTextMessageId === messageId || !this.onTextMessageStart) return;
542
681
  const carried = this.takeTrailingReasoning();
543
682
  if (carried.parts.length > 0 || carried.hiddenSignatures.length > 0) this.emit();
544
- const openToolCallIds = Array.from(this.toolCalls.values()).filter((call) => call.result === void 0).map((call) => call.toolCallId);
545
- const snapshotText = preserveSnapshotText ? this.pendingSnapshotText.get(messageId) : void 0;
546
- this.resetMessageParts();
547
- if (snapshotText !== void 0) this.pendingSnapshotText.set(messageId, snapshotText);
548
- this.onTextMessageStart(messageId, openToolCallIds);
683
+ const openToolCallIds = Array.from(this.buf.toolCalls.values()).filter((call) => call.result === void 0).map((call) => call.toolCallId);
684
+ if (!preserveSnapshotText) this.pendingSnapshotText.delete(messageId);
685
+ const outgoingId = this.onTextMessageStart(messageId, openToolCallIds) || void 0;
686
+ this.openMessage(messageId, outgoingId);
549
687
  this.restoreReasoning(carried);
550
688
  }
551
689
  takeTrailingReasoning() {
552
690
  const parts = [];
553
- let last = this.partOrder.at(-1);
554
- while (last?.kind === "reasoning" && last.subagentRunId === void 0 && last !== this.lastReasoningBeforeToolCall) {
555
- this.partOrder.pop();
691
+ let last = this.buf.partOrder.at(-1);
692
+ while (last?.kind === "reasoning" && last.subagentRunId === void 0 && last !== this.buf.lastReasoningBeforeToolCall) {
693
+ this.buf.partOrder.pop();
556
694
  parts.unshift({
557
695
  key: last.key,
558
- entry: this.reasoningParts.get(last.key) ?? { buffer: "" },
559
- signature: this.reasoningSignatures.get(last.key),
560
- signatureId: this.reasoningSignatureIds.get(last.key),
561
- reasoningId: this.reasoningMessageIds.get(last.key)
696
+ entry: this.buf.reasoningParts.get(last.key) ?? { buffer: "" },
697
+ signature: this.buf.reasoningSignatures.get(last.key),
698
+ signatureId: this.buf.reasoningSignatureIds.get(last.key),
699
+ reasoningId: this.buf.reasoningMessageIds.get(last.key)
562
700
  });
563
- last = this.partOrder.at(-1);
701
+ last = this.buf.partOrder.at(-1);
564
702
  }
565
- const cut = this.partOrder.length;
566
- const hiddenSignatures = Array.from(this.hiddenSignatures).filter(([id]) => this.hiddenSignatureAnchors.get(id) >= cut);
703
+ const cut = this.buf.partOrder.length;
704
+ const hiddenSignatures = Array.from(this.buf.hiddenSignatures).filter(([id]) => this.buf.hiddenSignatureAnchors.get(id) >= cut);
567
705
  for (const [id] of hiddenSignatures) {
568
- this.hiddenSignatures.delete(id);
569
- this.hiddenSignatureAnchors.delete(id);
706
+ this.buf.hiddenSignatures.delete(id);
707
+ this.buf.hiddenSignatureAnchors.delete(id);
570
708
  }
571
- const hiddenBlockIds = Array.from(this.hiddenBlockAnchors).filter(([, anchor]) => anchor >= cut).map(([id]) => id);
572
- const hiddenActiveReasoningId = this.hiddenActiveReasoningId;
709
+ const hiddenBlockIds = Array.from(this.buf.hiddenBlockAnchors).filter(([, anchor]) => anchor >= cut).map(([id]) => id);
710
+ const hiddenActiveReasoningId = this.buf.hiddenActiveReasoningId;
573
711
  return {
574
712
  parts,
575
- activeKey: this.activeReasoningKeyByScope.get(ROOT_SCOPE),
713
+ activeKey: this.buf.activeReasoningKeyByScope.get(ROOT_SCOPE),
576
714
  hiddenSignatures,
577
715
  hiddenBlockIds,
578
716
  hiddenActiveReasoningId: hiddenActiveReasoningId !== void 0 && hiddenBlockIds.includes(hiddenActiveReasoningId) ? hiddenActiveReasoningId : void 0
@@ -580,57 +718,57 @@ var RunAggregator = class {
580
718
  }
581
719
  restoreReasoning(carried) {
582
720
  for (const part of carried.parts) {
583
- this.partOrder.push({
721
+ this.buf.partOrder.push({
584
722
  kind: "reasoning",
585
723
  key: part.key
586
724
  });
587
- this.reasoningParts.set(part.key, part.entry);
588
- if (part.signature !== void 0) this.reasoningSignatures.set(part.key, part.signature);
589
- if (part.signatureId !== void 0) this.reasoningSignatureIds.set(part.key, part.signatureId);
590
- if (part.reasoningId !== void 0) this.reasoningMessageIds.set(part.key, part.reasoningId);
725
+ this.buf.reasoningParts.set(part.key, part.entry);
726
+ if (part.signature !== void 0) this.buf.reasoningSignatures.set(part.key, part.signature);
727
+ if (part.signatureId !== void 0) this.buf.reasoningSignatureIds.set(part.key, part.signatureId);
728
+ if (part.reasoningId !== void 0) this.buf.reasoningMessageIds.set(part.key, part.reasoningId);
591
729
  }
592
- if (carried.activeKey !== void 0 && this.reasoningParts.has(carried.activeKey)) this.activeReasoningKeyByScope.set(ROOT_SCOPE, carried.activeKey);
730
+ if (carried.activeKey !== void 0 && this.buf.reasoningParts.has(carried.activeKey)) this.buf.activeReasoningKeyByScope.set(ROOT_SCOPE, carried.activeKey);
593
731
  for (const [id, encryptedValue] of carried.hiddenSignatures) {
594
- this.hiddenSignatures.set(id, encryptedValue);
595
- this.hiddenSignatureAnchors.set(id, 0);
732
+ this.buf.hiddenSignatures.set(id, encryptedValue);
733
+ this.buf.hiddenSignatureAnchors.set(id, 0);
596
734
  }
597
735
  for (const id of carried.hiddenBlockIds) {
598
- this.hiddenReasoningIds.add(id);
599
- this.hiddenBlockAnchors.set(id, 0);
736
+ this.buf.hiddenReasoningIds.add(id);
737
+ this.buf.hiddenBlockAnchors.set(id, 0);
600
738
  }
601
- this.hiddenActiveReasoningId = carried.hiddenActiveReasoningId;
739
+ this.buf.hiddenActiveReasoningId = carried.hiddenActiveReasoningId;
602
740
  }
603
741
  generateTextKey() {
604
- this.textPartCounter += 1;
605
- return `text-${this.textPartCounter}`;
742
+ this.buf.textPartCounter += 1;
743
+ return `text-${this.buf.textPartCounter}`;
606
744
  }
607
745
  startTextMessage(scope, messageId) {
608
746
  const id = messageId ?? this.generateTextKey();
609
747
  const key = this.ensureTextPart(scope, id);
610
748
  if (scope === ROOT_SCOPE && messageId) {
611
749
  const snapshotText = this.pendingSnapshotText.get(messageId);
612
- const part = this.textParts.get(key);
750
+ const part = this.buf.textParts.get(key);
613
751
  if (snapshotText !== void 0 && part) {
614
752
  part.buffer = snapshotText;
615
753
  this.pendingSnapshotText.delete(messageId);
616
754
  }
617
755
  }
618
- this.activeTextMessageIdByScope.set(scope, key);
756
+ this.buf.activeTextMessageIdByScope.set(scope, key);
619
757
  return key;
620
758
  }
621
759
  resolveTextMessageId(scope, messageId) {
622
760
  if (messageId) return this.startTextMessage(scope, messageId);
623
- const active = this.activeTextMessageIdByScope.get(scope);
761
+ const active = this.buf.activeTextMessageIdByScope.get(scope);
624
762
  if (active) return active;
625
763
  const key = this.ensureTextPart(scope, this.generateTextKey());
626
- this.activeTextMessageIdByScope.set(scope, key);
764
+ this.buf.activeTextMessageIdByScope.set(scope, key);
627
765
  return key;
628
766
  }
629
767
  ensureTextPart(scope, id) {
630
768
  const key = this.partKey(scope, id);
631
- if (!this.textParts.has(key)) {
632
- this.textParts.set(key, { buffer: "" });
633
- if (!this.partOrder.some((part) => part.kind === "text" && part.key === key)) this.partOrder.push(scope === ROOT_SCOPE ? {
769
+ if (!this.buf.textParts.has(key)) {
770
+ this.buf.textParts.set(key, { buffer: "" });
771
+ if (!this.buf.partOrder.some((part) => part.kind === "text" && part.key === key)) this.buf.partOrder.push(scope === ROOT_SCOPE ? {
634
772
  kind: "text",
635
773
  key
636
774
  } : {
@@ -642,15 +780,15 @@ var RunAggregator = class {
642
780
  return key;
643
781
  }
644
782
  appendText(id, delta) {
645
- const entry = this.textParts.get(id);
783
+ const entry = this.buf.textParts.get(id);
646
784
  if (!entry) return;
647
785
  entry.buffer += delta;
648
786
  }
649
787
  startToolCall(scope, id, name, parentMessageId) {
650
788
  if (!id) return;
651
- this.activeTextMessageIdByScope.delete(scope);
652
- if (!this.partOrder.some((part) => part.kind === "tool-call" && part.toolCallId === id)) {
653
- if (scope === ROOT_SCOPE) this.lastReasoningBeforeToolCall = this.partOrder.findLast((part) => part.kind === "reasoning" && part.subagentRunId === void 0);
789
+ this.buf.activeTextMessageIdByScope.delete(scope);
790
+ if (!this.buf.partOrder.some((part) => part.kind === "tool-call" && part.toolCallId === id)) {
791
+ if (scope === ROOT_SCOPE) this.buf.lastReasoningBeforeToolCall = this.buf.partOrder.findLast((part) => part.kind === "reasoning" && part.subagentRunId === void 0);
654
792
  this.insertToolPart(scope, id, parentMessageId);
655
793
  }
656
794
  const state = {
@@ -664,8 +802,8 @@ var RunAggregator = class {
664
802
  };
665
803
  if (parentMessageId) state.parentMessageId = parentMessageId;
666
804
  if (scope !== ROOT_SCOPE) state.subagentRunId = scope;
667
- this.nestingMemo = void 0;
668
- this.toolCalls.set(id, state);
805
+ this.buf.nestingMemo = void 0;
806
+ this.buf.toolCalls.set(id, state);
669
807
  }
670
808
  insertToolPart(scope, id, parentMessageId) {
671
809
  const entry = {
@@ -674,25 +812,25 @@ var RunAggregator = class {
674
812
  };
675
813
  if (parentMessageId) {
676
814
  const parentKey = this.partKey(scope, parentMessageId);
677
- const parentIndex = this.partOrder.findIndex((part) => part.kind === "text" && part.key === parentKey);
815
+ const parentIndex = this.buf.partOrder.findIndex((part) => part.kind === "text" && part.key === parentKey);
678
816
  if (parentIndex !== -1) {
679
817
  let insertAt = parentIndex + 1;
680
- while (insertAt < this.partOrder.length) {
681
- const part = this.partOrder[insertAt];
682
- if (part?.kind === "tool-call" && this.toolCalls.get(part.toolCallId)?.parentMessageId === parentMessageId) {
818
+ while (insertAt < this.buf.partOrder.length) {
819
+ const part = this.buf.partOrder[insertAt];
820
+ if (part?.kind === "tool-call" && this.buf.toolCalls.get(part.toolCallId)?.parentMessageId === parentMessageId) {
683
821
  insertAt += 1;
684
822
  continue;
685
823
  }
686
824
  break;
687
825
  }
688
- this.partOrder.splice(insertAt, 0, entry);
826
+ this.buf.partOrder.splice(insertAt, 0, entry);
689
827
  return;
690
828
  }
691
829
  }
692
- this.partOrder.push(entry);
830
+ this.buf.partOrder.push(entry);
693
831
  }
694
832
  appendToolArgs(id, delta) {
695
- const entry = id ? this.toolCalls.get(id) : void 0;
833
+ const entry = id ? this.buf.toolCalls.get(id) : void 0;
696
834
  if (!entry) return;
697
835
  if (!entry.argsTextScanner) entry.argsTextScanner = createJSONContainerScanner();
698
836
  entry.argsText += delta;
@@ -711,7 +849,7 @@ var RunAggregator = class {
711
849
  finishToolCall(scope, id, content, isError, toolMessageId, mcpResult, metadata) {
712
850
  if (!id) return;
713
851
  const modelText = toolContentText(content);
714
- let entry = this.toolCalls.get(id);
852
+ let entry = this.buf.toolCalls.get(id);
715
853
  if (!entry) {
716
854
  entry = {
717
855
  toolCallId: id,
@@ -723,10 +861,10 @@ var RunAggregator = class {
723
861
  snapshotResultApplied: false,
724
862
  ...scope !== ROOT_SCOPE ? { subagentRunId: scope } : {}
725
863
  };
726
- this.nestingMemo = void 0;
727
- this.toolCalls.set(id, entry);
864
+ this.buf.nestingMemo = void 0;
865
+ this.buf.toolCalls.set(id, entry);
728
866
  }
729
- if (!this.partOrder.some((part) => part.kind === "tool-call" && part.toolCallId === id)) this.partOrder.push({
867
+ if (!this.buf.partOrder.some((part) => part.kind === "tool-call" && part.toolCallId === id)) this.buf.partOrder.push({
730
868
  kind: "tool-call",
731
869
  toolCallId: id
732
870
  });
@@ -758,21 +896,21 @@ var RunAggregator = class {
758
896
  ...entry.resultMetadata,
759
897
  ...metadata
760
898
  };
761
- this.lastResolvedToolCallIdByScope.set(scope, id);
899
+ this.buf.lastResolvedToolCallIdByScope.set(scope, id);
762
900
  }
763
901
  nesting() {
764
- if (!this.nestingMemo) {
902
+ if (!this.buf.nestingMemo) {
765
903
  const subagentsByParentToolCallId = this.subagentsByParentToolCallId();
766
- this.nestingMemo = {
904
+ this.buf.nestingMemo = {
767
905
  subagentsByParentToolCallId,
768
906
  reachable: this.reachableSubagentRunIds(subagentsByParentToolCallId)
769
907
  };
770
908
  }
771
- return this.nestingMemo;
909
+ return this.buf.nestingMemo;
772
910
  }
773
911
  subagentsByParentToolCallId() {
774
912
  const map = /* @__PURE__ */ new Map();
775
- for (const run of this.subagentRuns.values()) {
913
+ for (const run of this.buf.subagentRuns.values()) {
776
914
  if (!run.parentToolCallId) continue;
777
915
  const list = map.get(run.parentToolCallId) ?? [];
778
916
  list.push(run.subagentRunId);
@@ -784,7 +922,7 @@ var RunAggregator = class {
784
922
  const reachable = /* @__PURE__ */ new Set();
785
923
  const walk = (scope, depth) => {
786
924
  if (depth > 16) return;
787
- for (const entry of this.toolCalls.values()) {
925
+ for (const entry of this.buf.toolCalls.values()) {
788
926
  if ((entry.subagentRunId ?? ROOT_SCOPE) !== scope) continue;
789
927
  for (const id of subagentsByParentToolCallId.get(entry.toolCallId) ?? []) {
790
928
  if (reachable.has(id)) continue;
@@ -797,7 +935,7 @@ var RunAggregator = class {
797
935
  return reachable;
798
936
  }
799
937
  materializeSubagentMessage(subagentRunId, depth, ctx) {
800
- const run = this.subagentRuns.get(subagentRunId);
938
+ const run = this.buf.subagentRuns.get(subagentRunId);
801
939
  if (!run || depth > 16) return void 0;
802
940
  const content = this.buildParts(subagentRunId, depth + 1, ctx);
803
941
  return {
@@ -830,7 +968,7 @@ var RunAggregator = class {
830
968
  if (isRoot && ctx.root) ctx.root.lastMaterializedIndex = index;
831
969
  };
832
970
  if (part.kind === "tool-call") {
833
- const entry = this.toolCalls.get(part.toolCallId);
971
+ const entry = this.buf.toolCalls.get(part.toolCallId);
834
972
  if (!entry) continue;
835
973
  const approval = ctx.approvals.get(entry.toolCallId);
836
974
  const nestedMessages = (ctx.subagentsByParentToolCallId.get(entry.toolCallId) ?? []).filter((id) => ctx.reachable.has(id)).map((id) => this.materializeSubagentMessage(id, depth, ctx)).filter((m) => m !== void 0);
@@ -861,12 +999,12 @@ var RunAggregator = class {
861
999
  const rawScope = "subagentRunId" in part ? part.subagentRunId ?? ROOT_SCOPE : ROOT_SCOPE;
862
1000
  if (part.kind === "reasoning") {
863
1001
  if (this.showThinking) {
864
- const entry = this.reasoningParts.get(part.key);
1002
+ const entry = this.buf.reasoningParts.get(part.key);
865
1003
  const buffer = entry?.buffer ?? "";
866
- const isActive = this.activeReasoningKeyByScope.get(rawScope) === part.key;
1004
+ const isActive = this.buf.activeReasoningKeyByScope.get(rawScope) === part.key;
867
1005
  if (buffer.length > 0 || isActive) {
868
- const encryptedValue = this.reasoningSignatures.get(part.key);
869
- const reasoningId = this.reasoningMessageIds.get(part.key);
1006
+ const encryptedValue = this.buf.reasoningSignatures.get(part.key);
1007
+ const reasoningId = this.buf.reasoningMessageIds.get(part.key);
870
1008
  const meta = {
871
1009
  ...reasoningId !== void 0 ? { reasoningId } : {},
872
1010
  ...encryptedValue !== void 0 ? { encryptedValue } : {}
@@ -879,8 +1017,8 @@ var RunAggregator = class {
879
1017
  });
880
1018
  materialized();
881
1019
  } else if (isRoot && ctx.root) {
882
- const encryptedValue = this.reasoningSignatures.get(part.key);
883
- const id = this.reasoningSignatureIds.get(part.key);
1020
+ const encryptedValue = this.buf.reasoningSignatures.get(part.key);
1021
+ const id = this.buf.reasoningSignatureIds.get(part.key);
884
1022
  if (id?.trim() && encryptedValue?.trim()) ctx.root.opaqueCandidates.push({
885
1023
  id,
886
1024
  encryptedValue,
@@ -891,7 +1029,7 @@ var RunAggregator = class {
891
1029
  continue;
892
1030
  }
893
1031
  if (part.kind === "text") {
894
- const entry = this.textParts.get(part.key);
1032
+ const entry = this.buf.textParts.get(part.key);
895
1033
  if (entry && entry.buffer.trim().length > 0) {
896
1034
  snapshot.push({
897
1035
  type: "text",
@@ -916,16 +1054,16 @@ var RunAggregator = class {
916
1054
  emit() {
917
1055
  const { subagentsByParentToolCallId, reachable } = this.nesting();
918
1056
  const root = {
919
- opaqueCandidates: Array.from(this.hiddenSignatures, ([id, encryptedValue]) => ({
1057
+ opaqueCandidates: Array.from(this.buf.hiddenSignatures, ([id, encryptedValue]) => ({
920
1058
  id,
921
1059
  encryptedValue,
922
- anchor: this.hiddenSignatureAnchors.get(id)
1060
+ anchor: this.buf.hiddenSignatureAnchors.get(id)
923
1061
  })),
924
1062
  lastMaterializedIndex: -1
925
1063
  };
926
1064
  const partsByScope = /* @__PURE__ */ new Map();
927
- for (const [index, part] of this.partOrder.entries()) {
928
- const rawScope = part.kind === "tool-call" ? this.toolCalls.get(part.toolCallId)?.subagentRunId ?? ROOT_SCOPE : "subagentRunId" in part ? part.subagentRunId ?? ROOT_SCOPE : ROOT_SCOPE;
1065
+ for (const [index, part] of this.buf.partOrder.entries()) {
1066
+ const rawScope = part.kind === "tool-call" ? this.buf.toolCalls.get(part.toolCallId)?.subagentRunId ?? ROOT_SCOPE : "subagentRunId" in part ? part.subagentRunId ?? ROOT_SCOPE : ROOT_SCOPE;
929
1067
  const bucketScope = rawScope === ROOT_SCOPE || reachable.has(rawScope) ? rawScope : ROOT_SCOPE;
930
1068
  const bucket = partsByScope.get(bucketScope);
931
1069
  if (bucket) bucket.push({
@@ -941,18 +1079,18 @@ var RunAggregator = class {
941
1079
  subagentsByParentToolCallId,
942
1080
  reachable,
943
1081
  partsByScope,
944
- approvals: projectAgUiToolApprovals(this.status?.type === "requires-action" ? this.interrupts : void 0, new Set(Array.from(this.toolCalls.values()).map((entry) => entry.toolCallId))),
1082
+ approvals: projectAgUiToolApprovals(this.buf === this.current && this.status?.type === "requires-action" ? this.interrupts : void 0, new Set(Array.from(this.buf.toolCalls.values()).map((entry) => entry.toolCallId))),
945
1083
  root
946
1084
  };
947
1085
  const snapshot = this.buildParts(ROOT_SCOPE, 0, ctx);
948
1086
  const { opaqueCandidates, lastMaterializedIndex } = root;
949
1087
  const toolMessageIds = /* @__PURE__ */ new Set();
950
- for (const call of this.toolCalls.values()) if (call.toolMessageId) toolMessageIds.add(call.toolMessageId);
1088
+ for (const call of this.buf.toolCalls.values()) if (call.toolMessageId) toolMessageIds.add(call.toolMessageId);
951
1089
  opaqueCandidates.sort((a, b) => a.anchor - b.anchor);
952
1090
  const publishableOpaqueReasoning = opaqueCandidates.filter((entry) => {
953
- const collides = this.textParts.has(entry.id) || entry.id === this.reportedServerMessageId || toolMessageIds.has(entry.id);
954
- if (collides && !this.loggedDroppedOpaqueIds.has(entry.id)) {
955
- this.loggedDroppedOpaqueIds.add(entry.id);
1091
+ const collides = this.buf.textParts.has(entry.id) || entry.id === this.buf.reportedServerMessageId || toolMessageIds.has(entry.id);
1092
+ if (collides && !this.buf.loggedDroppedOpaqueIds.has(entry.id)) {
1093
+ this.buf.loggedDroppedOpaqueIds.add(entry.id);
956
1094
  this.logger.debug?.("[agui] aggregator dropped opaque reasoning signature: id collides with a message id", entry.id);
957
1095
  }
958
1096
  return !collides;
@@ -960,8 +1098,15 @@ var RunAggregator = class {
960
1098
  ...entry,
961
1099
  after: true
962
1100
  } : entry);
963
- if (publishableOpaqueReasoning.length > 0) this.hasEmittedOpaqueReasoning = true;
964
- const includeOpaque = this.hasEmittedOpaqueReasoning;
1101
+ if (publishableOpaqueReasoning.length > 0) this.buf.hasEmittedOpaqueReasoning = true;
1102
+ const includeOpaque = this.buf.hasEmittedOpaqueReasoning;
1103
+ if (this.buf !== this.current) {
1104
+ this.onRetainedMessage?.({
1105
+ content: snapshot,
1106
+ ...includeOpaque ? { metadata: { custom: { [AG_UI_METADATA_NAMESPACE]: { opaqueReasoning: publishableOpaqueReasoning } } } } : {}
1107
+ }, this.retainedState(this.buf));
1108
+ return;
1109
+ }
965
1110
  const timing = this.getTiming();
966
1111
  const metadata = {
967
1112
  ...timing ? { timing } : {},
@@ -984,7 +1129,7 @@ var RunAggregator = class {
984
1129
  getTiming() {
985
1130
  if (this.streamStartTime === void 0) return void 0;
986
1131
  const totalStreamTime = Date.now() - this.streamStartTime;
987
- const tokenCount = this.totalChunks > 0 ? Math.ceil(Array.from(this.textParts.values()).reduce((sum, p) => sum + p.buffer.length, 0) / 4) : void 0;
1132
+ const tokenCount = this.totalChunks > 0 ? Math.ceil(Array.from(this.buf.textParts.values()).reduce((sum, p) => sum + p.buffer.length, 0) / 4) : void 0;
988
1133
  const tokensPerSecond = tokenCount && totalStreamTime > 0 ? tokenCount / totalStreamTime * 1e3 : void 0;
989
1134
  return {
990
1135
  streamStartTime: this.streamStartTime,
@@ -993,24 +1138,24 @@ var RunAggregator = class {
993
1138
  ...tokenCount !== void 0 ? { tokenCount } : {},
994
1139
  ...tokensPerSecond !== void 0 ? { tokensPerSecond } : {},
995
1140
  totalChunks: this.totalChunks,
996
- toolCallCount: this.toolCalls.size
1141
+ toolCallCount: this.buf.toolCalls.size
997
1142
  };
998
1143
  }
999
1144
  handleReasoningStart(scope, messageId, isMessageId = false, timestamp) {
1000
1145
  if (!this.showThinking) {
1001
1146
  if (scope !== ROOT_SCOPE) return;
1002
- const anchor = this.partOrder.length;
1003
- this.hiddenReasoningIds.add(messageId);
1004
- this.hiddenBlockAnchors.set(messageId, anchor);
1005
- this.hiddenActiveReasoningId = messageId;
1147
+ const anchor = this.buf.partOrder.length;
1148
+ this.buf.hiddenReasoningIds.add(messageId);
1149
+ this.buf.hiddenBlockAnchors.set(messageId, anchor);
1150
+ this.buf.hiddenActiveReasoningId = messageId;
1006
1151
  return;
1007
1152
  }
1008
- this.activeTextMessageIdByScope.delete(scope);
1153
+ this.buf.activeTextMessageIdByScope.delete(scope);
1009
1154
  const key = this.partKey(scope, messageId);
1010
- if (isMessageId) this.reasoningMessageIds.set(key, messageId);
1011
- if (!this.reasoningParts.has(key)) {
1012
- this.reasoningParts.set(key, { buffer: "" });
1013
- this.partOrder.push(scope === ROOT_SCOPE ? {
1155
+ if (isMessageId) this.buf.reasoningMessageIds.set(key, messageId);
1156
+ if (!this.buf.reasoningParts.has(key)) {
1157
+ this.buf.reasoningParts.set(key, { buffer: "" });
1158
+ this.buf.partOrder.push(scope === ROOT_SCOPE ? {
1014
1159
  kind: "reasoning",
1015
1160
  key
1016
1161
  } : {
@@ -1019,38 +1164,38 @@ var RunAggregator = class {
1019
1164
  subagentRunId: scope
1020
1165
  });
1021
1166
  }
1022
- const entry = this.reasoningParts.get(key);
1167
+ const entry = this.buf.reasoningParts.get(key);
1023
1168
  if (entry?.timing) entry.timing = { startedAt: entry.timing.startedAt };
1024
1169
  else if (entry && typeof timestamp === "number" && Number.isFinite(timestamp)) entry.timing = { startedAt: timestamp };
1025
- this.activeReasoningKeyByScope.set(scope, key);
1170
+ this.buf.activeReasoningKeyByScope.set(scope, key);
1026
1171
  this.emit();
1027
1172
  }
1028
1173
  handleReasoningContent(scope, delta, messageId, isMessageId = false) {
1029
1174
  if (!delta) return;
1030
1175
  if (!this.showThinking) {
1031
1176
  if (scope !== ROOT_SCOPE) return;
1032
- if (this.hiddenActiveReasoningId === void 0) this.handleReasoningStart(scope, messageId, isMessageId);
1177
+ if (this.buf.hiddenActiveReasoningId === void 0) this.handleReasoningStart(scope, messageId, isMessageId);
1033
1178
  return;
1034
1179
  }
1035
- if (!this.activeReasoningKeyByScope.has(scope)) this.handleReasoningStart(scope, messageId, isMessageId);
1036
- const key = this.activeReasoningKeyByScope.get(scope);
1180
+ if (!this.buf.activeReasoningKeyByScope.has(scope)) this.handleReasoningStart(scope, messageId, isMessageId);
1181
+ const key = this.buf.activeReasoningKeyByScope.get(scope);
1037
1182
  if (!key) return;
1038
- const entry = this.reasoningParts.get(key);
1183
+ const entry = this.buf.reasoningParts.get(key);
1039
1184
  if (entry) entry.buffer += delta;
1040
1185
  this.emit();
1041
1186
  }
1042
1187
  handleReasoningEnd(scope, timestamp) {
1043
1188
  if (!this.showThinking) {
1044
- if (scope === ROOT_SCOPE) this.hiddenActiveReasoningId = void 0;
1189
+ if (scope === ROOT_SCOPE) this.buf.hiddenActiveReasoningId = void 0;
1045
1190
  return;
1046
1191
  }
1047
- const key = this.activeReasoningKeyByScope.get(scope);
1048
- const entry = key ? this.reasoningParts.get(key) : void 0;
1192
+ const key = this.buf.activeReasoningKeyByScope.get(scope);
1193
+ const entry = key ? this.buf.reasoningParts.get(key) : void 0;
1049
1194
  if (entry?.timing && typeof timestamp === "number" && Number.isFinite(timestamp)) entry.timing = {
1050
1195
  ...entry.timing,
1051
1196
  completedAt: timestamp
1052
1197
  };
1053
- this.activeReasoningKeyByScope.delete(scope);
1198
+ this.buf.activeReasoningKeyByScope.delete(scope);
1054
1199
  this.emit();
1055
1200
  }
1056
1201
  };