@assistant-ui/react-ag-ui 0.0.31 → 0.0.33

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 (38) hide show
  1. package/dist/index.d.ts +3 -4
  2. package/dist/index.js +2 -2
  3. package/dist/runtime/AgUiThreadRuntimeCore.d.ts +85 -82
  4. package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +1 -1
  5. package/dist/runtime/AgUiThreadRuntimeCore.js +542 -628
  6. package/dist/runtime/AgUiThreadRuntimeCore.js.map +1 -1
  7. package/dist/runtime/adapter/conversions.d.ts +39 -36
  8. package/dist/runtime/adapter/conversions.d.ts.map +1 -1
  9. package/dist/runtime/adapter/conversions.js +294 -390
  10. package/dist/runtime/adapter/conversions.js.map +1 -1
  11. package/dist/runtime/adapter/run-aggregator.d.ts +54 -51
  12. package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
  13. package/dist/runtime/adapter/run-aggregator.js +367 -413
  14. package/dist/runtime/adapter/run-aggregator.js.map +1 -1
  15. package/dist/runtime/adapter/subscriber.d.ts +94 -91
  16. package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
  17. package/dist/runtime/adapter/subscriber.js +77 -86
  18. package/dist/runtime/adapter/subscriber.js.map +1 -1
  19. package/dist/runtime/event-parser.d.ts +9 -5
  20. package/dist/runtime/event-parser.d.ts.map +1 -1
  21. package/dist/runtime/event-parser.js +172 -177
  22. package/dist/runtime/event-parser.js.map +1 -1
  23. package/dist/runtime/logger.d.ts +7 -4
  24. package/dist/runtime/logger.d.ts.map +1 -1
  25. package/dist/runtime/logger.js +7 -3
  26. package/dist/runtime/logger.js.map +1 -1
  27. package/dist/runtime/types.d.ts +121 -117
  28. package/dist/runtime/types.d.ts.map +1 -1
  29. package/dist/runtime/types.js +0 -2
  30. package/dist/useAgUiRuntime.d.ts +10 -6
  31. package/dist/useAgUiRuntime.d.ts.map +1 -1
  32. package/dist/useAgUiRuntime.js +105 -142
  33. package/dist/useAgUiRuntime.js.map +1 -1
  34. package/package.json +7 -7
  35. package/src/useAgUiRuntime.ts +3 -47
  36. package/dist/index.d.ts.map +0 -1
  37. package/dist/index.js.map +0 -1
  38. package/dist/runtime/types.js.map +0 -1
@@ -1,416 +1,370 @@
1
1
  "use client";
2
- export const AG_UI_METADATA_NAMESPACE = "agui";
2
+ //#region src/runtime/adapter/run-aggregator.ts
3
+ const AG_UI_METADATA_NAMESPACE = "agui";
3
4
  /**
4
- * Collects AG-UI events into assistant-ui run snapshots that can be yielded from a ChatModelAdapter.
5
- *
6
- * The aggregator keeps a single assistant message worth of parts. Each incoming event updates the parts and
7
- * emits a fresh snapshot through the provided `emit` callback.
8
- */
9
- export class RunAggregator {
10
- emitUpdate;
11
- showThinking;
12
- logger;
13
- onServerMessageId;
14
- status;
15
- interrupts;
16
- textParts = new Map();
17
- activeTextMessageId;
18
- reasoningParts = new Map(); // key → buffer
19
- activeReasoningKey;
20
- reasoningPartCounter = 0;
21
- toolCalls = new Map();
22
- partOrder = [];
23
- textPartCounter = 0;
24
- serverMessageIdReported = false;
25
- streamStartTime;
26
- firstTokenTime;
27
- totalChunks = 0;
28
- constructor(options) {
29
- this.emitUpdate = options.emit;
30
- this.showThinking = options.showThinking;
31
- this.logger = options.logger;
32
- this.onServerMessageId = options.onServerMessageId;
33
- }
34
- handle(event) {
35
- switch (event.type) {
36
- case "RUN_STARTED": {
37
- this.clearTextParts();
38
- this.reasoningParts.clear();
39
- this.activeReasoningKey = undefined;
40
- this.reasoningPartCounter = 0;
41
- this.toolCalls.clear();
42
- this.partOrder.length = 0;
43
- this.textPartCounter = 0;
44
- this.activeTextMessageId = undefined;
45
- this.interrupts = undefined;
46
- this.serverMessageIdReported = false;
47
- this.streamStartTime = Date.now();
48
- this.firstTokenTime = undefined;
49
- this.totalChunks = 0;
50
- this.status = { type: "running" };
51
- this.emit();
52
- break;
53
- }
54
- case "RUN_FINISHED": {
55
- if (event.outcome?.type === "interrupt") {
56
- this.interrupts = event.outcome.interrupts;
57
- this.status = { type: "requires-action", reason: "interrupt" };
58
- this.emit();
59
- break;
60
- }
61
- this.interrupts = undefined;
62
- const hasUnresolvedToolCalls = Array.from(this.toolCalls.values()).some((tc) => tc.result === undefined);
63
- this.status =
64
- event.outcome?.type === "success" || !hasUnresolvedToolCalls
65
- ? { type: "complete", reason: "unknown" }
66
- : { type: "requires-action", reason: "tool-calls" };
67
- this.emit();
68
- break;
69
- }
70
- case "RUN_ERROR": {
71
- this.status = {
72
- type: "incomplete",
73
- reason: "error",
74
- ...(event.message !== undefined ? { error: event.message } : {}),
75
- };
76
- this.emit();
77
- break;
78
- }
79
- case "RUN_CANCELLED": {
80
- this.status = { type: "incomplete", reason: "cancelled" };
81
- this.emit();
82
- break;
83
- }
84
- case "TEXT_MESSAGE_START": {
85
- this.reportServerMessageId(event.messageId);
86
- const id = this.startTextMessage(event.messageId);
87
- if (id) {
88
- this.markTextPartTouched(id);
89
- }
90
- this.emit();
91
- break;
92
- }
93
- case "TEXT_MESSAGE_CONTENT":
94
- case "TEXT_MESSAGE_CHUNK": {
95
- const incomingId = "messageId" in event ? event.messageId : undefined;
96
- this.reportServerMessageId(incomingId);
97
- if (!event.delta)
98
- break;
99
- this.recordFirstToken();
100
- const id = this.resolveTextMessageId(incomingId);
101
- this.appendText(id, event.delta);
102
- this.totalChunks++;
103
- this.emit();
104
- break;
105
- }
106
- case "TEXT_MESSAGE_END": {
107
- this.reportServerMessageId(event.messageId);
108
- if (event.messageId && this.activeTextMessageId === event.messageId) {
109
- this.activeTextMessageId = undefined;
110
- }
111
- this.emit();
112
- break;
113
- }
114
- case "THINKING_START":
115
- case "THINKING_TEXT_MESSAGE_START":
116
- case "REASONING_START":
117
- case "REASONING_MESSAGE_START":
118
- this.handleReasoningStart("messageId" in event ? event.messageId : undefined);
119
- break;
120
- case "THINKING_TEXT_MESSAGE_CONTENT":
121
- this.handleReasoningContent(event.delta);
122
- this.totalChunks++;
123
- this.recordFirstToken();
124
- break;
125
- case "REASONING_MESSAGE_CONTENT":
126
- this.handleReasoningContent(event.delta, "messageId" in event ? event.messageId : undefined);
127
- this.totalChunks++;
128
- this.recordFirstToken();
129
- break;
130
- case "THINKING_TEXT_MESSAGE_END":
131
- case "THINKING_END":
132
- case "REASONING_MESSAGE_END":
133
- case "REASONING_END":
134
- this.handleReasoningEnd();
135
- break;
136
- case "TOOL_CALL_START": {
137
- this.reportServerMessageId(event.parentMessageId);
138
- this.startToolCall(event.toolCallId, event.toolCallName, event.parentMessageId);
139
- this.emit();
140
- break;
141
- }
142
- case "TOOL_CALL_ARGS":
143
- case "TOOL_CALL_CHUNK": {
144
- if (event.type === "TOOL_CALL_CHUNK") {
145
- this.reportServerMessageId(event.parentMessageId);
146
- }
147
- if (!event.delta)
148
- break;
149
- this.appendToolArgs(event.toolCallId, event.delta);
150
- this.emit();
151
- break;
152
- }
153
- case "TOOL_CALL_END": {
154
- this.emit();
155
- break;
156
- }
157
- case "TOOL_CALL_RESULT": {
158
- this.finishToolCall(event.toolCallId, event.content ?? "", event.role === "tool" ? false : undefined, event.messageId);
159
- this.emit();
160
- break;
161
- }
162
- default: {
163
- this.logger.debug?.("[agui] aggregator ignored event", event);
164
- }
165
- }
166
- }
167
- reportServerMessageId(messageId) {
168
- if (this.serverMessageIdReported || !messageId)
169
- return;
170
- this.serverMessageIdReported = true;
171
- this.onServerMessageId?.(messageId);
172
- }
173
- clearTextParts() {
174
- this.textParts.clear();
175
- }
176
- generateTextKey() {
177
- this.textPartCounter += 1;
178
- return `text-${this.textPartCounter}`;
179
- }
180
- startTextMessage(messageId) {
181
- const id = messageId ?? this.generateTextKey();
182
- this.ensureTextPart(id);
183
- this.activeTextMessageId = id;
184
- return id;
185
- }
186
- resolveTextMessageId(messageId) {
187
- if (messageId) {
188
- this.ensureTextPart(messageId);
189
- this.activeTextMessageId = messageId;
190
- return messageId;
191
- }
192
- if (this.activeTextMessageId) {
193
- return this.activeTextMessageId;
194
- }
195
- const generated = this.generateTextKey();
196
- this.ensureTextPart(generated);
197
- this.activeTextMessageId = generated;
198
- return generated;
199
- }
200
- ensureTextPart(id) {
201
- if (!this.textParts.has(id)) {
202
- this.textParts.set(id, { buffer: "", touched: false });
203
- if (!this.partOrder.some((part) => part.kind === "text" && part.key === id)) {
204
- this.partOrder.push({ kind: "text", key: id });
205
- }
206
- }
207
- }
208
- markTextPartTouched(id) {
209
- const entry = this.textParts.get(id);
210
- if (!entry)
211
- return;
212
- entry.touched = true;
213
- }
214
- appendText(id, delta) {
215
- this.ensureTextPart(id);
216
- const entry = this.textParts.get(id);
217
- if (!entry)
218
- return;
219
- entry.buffer += delta;
220
- entry.touched = true;
221
- }
222
- startToolCall(id, name, parentMessageId) {
223
- if (!id)
224
- return;
225
- // A new tool call acts as a boundary: any anonymous text that arrives
226
- // after it should be a new part, not appended to the pre-tool text.
227
- this.activeTextMessageId = undefined;
228
- if (!this.partOrder.some((part) => part.kind === "tool-call" && part.toolCallId === id)) {
229
- this.partOrder.push({ kind: "tool-call", toolCallId: id });
230
- }
231
- const state = {
232
- toolCallId: id,
233
- toolCallName: name ?? "tool",
234
- argsText: "",
235
- parsedArgs: undefined,
236
- result: undefined,
237
- isError: undefined,
238
- };
239
- if (parentMessageId) {
240
- state.parentMessageId = parentMessageId;
241
- }
242
- this.toolCalls.set(id, state);
243
- }
244
- appendToolArgs(id, delta) {
245
- const entry = id ? this.toolCalls.get(id) : undefined;
246
- if (!entry)
247
- return;
248
- entry.argsText += delta;
249
- try {
250
- const parsed = JSON.parse(entry.argsText);
251
- if (parsed && typeof parsed === "object") {
252
- entry.parsedArgs = parsed;
253
- }
254
- else {
255
- entry.parsedArgs = undefined;
256
- }
257
- }
258
- catch {
259
- entry.parsedArgs = undefined;
260
- }
261
- }
262
- finishToolCall(id, content, isError, toolMessageId) {
263
- if (!id)
264
- return;
265
- let entry = this.toolCalls.get(id);
266
- if (!entry) {
267
- entry = {
268
- toolCallId: id,
269
- toolCallName: "tool",
270
- argsText: "",
271
- parsedArgs: undefined,
272
- result: undefined,
273
- isError: undefined,
274
- };
275
- this.toolCalls.set(id, entry);
276
- }
277
- if (!this.partOrder.some((part) => part.kind === "tool-call" && part.toolCallId === id)) {
278
- this.partOrder.push({ kind: "tool-call", toolCallId: id });
279
- }
280
- entry.result = this.tryParseJSON(content);
281
- entry.isError = isError;
282
- if (toolMessageId) {
283
- entry.toolMessageId = toolMessageId;
284
- }
285
- }
286
- tryParseJSON(value) {
287
- if (!value)
288
- return value;
289
- try {
290
- return JSON.parse(value);
291
- }
292
- catch {
293
- return value;
294
- }
295
- }
296
- emit() {
297
- const snapshot = [];
298
- for (const part of this.partOrder) {
299
- if (part.kind === "reasoning") {
300
- if (this.showThinking) {
301
- const buffer = this.reasoningParts.get(part.key) ?? "";
302
- if (buffer.length > 0 || this.activeReasoningKey === part.key) {
303
- snapshot.push({ type: "reasoning", text: buffer });
304
- }
305
- }
306
- continue;
307
- }
308
- if (part.kind === "text") {
309
- const entry = this.textParts.get(part.key);
310
- if (entry?.touched) {
311
- snapshot.push({ type: "text", text: entry.buffer });
312
- }
313
- continue;
314
- }
315
- const entry = this.toolCalls.get(part.toolCallId);
316
- if (!entry)
317
- continue;
318
- const toolPart = {
319
- type: "tool-call",
320
- toolCallId: entry.toolCallId,
321
- toolName: entry.toolCallName,
322
- args: (entry.parsedArgs ?? {}),
323
- argsText: entry.argsText,
324
- ...(entry.result !== undefined ? { result: entry.result } : {}),
325
- ...(entry.isError !== undefined ? { isError: entry.isError } : {}),
326
- ...(entry.parentMessageId ? { parentId: entry.parentMessageId } : {}),
327
- ...(entry.toolMessageId
328
- ? { unstable_toolMessageId: entry.toolMessageId }
329
- : {}),
330
- };
331
- snapshot.push(toolPart);
332
- }
333
- const timing = this.getTiming();
334
- const metadata = {
335
- ...(timing ? { timing } : {}),
336
- ...(this.interrupts
337
- ? {
338
- custom: {
339
- [AG_UI_METADATA_NAMESPACE]: {
340
- interrupts: this.interrupts,
341
- },
342
- },
343
- }
344
- : {}),
345
- };
346
- const result = {
347
- content: snapshot,
348
- ...(this.status ? { status: this.status } : undefined),
349
- ...(Object.keys(metadata).length > 0 ? { metadata } : undefined),
350
- };
351
- this.emitUpdate(result);
352
- }
353
- recordFirstToken() {
354
- if (this.firstTokenTime === undefined &&
355
- this.streamStartTime !== undefined) {
356
- this.firstTokenTime = Date.now() - this.streamStartTime;
357
- }
358
- }
359
- getTiming() {
360
- if (this.streamStartTime === undefined)
361
- return undefined;
362
- const now = Date.now();
363
- const totalStreamTime = now - this.streamStartTime;
364
- const tokenCount = this.totalChunks > 0
365
- ? Math.ceil(Array.from(this.textParts.values()).reduce((sum, p) => sum + p.buffer.length, 0) / 4)
366
- : undefined;
367
- const tokensPerSecond = tokenCount && totalStreamTime > 0
368
- ? (tokenCount / totalStreamTime) * 1000
369
- : undefined;
370
- return {
371
- streamStartTime: this.streamStartTime,
372
- ...(this.firstTokenTime !== undefined
373
- ? { firstTokenTime: this.firstTokenTime }
374
- : {}),
375
- totalStreamTime,
376
- ...(tokenCount !== undefined ? { tokenCount } : {}),
377
- ...(tokensPerSecond !== undefined ? { tokensPerSecond } : {}),
378
- totalChunks: this.totalChunks,
379
- toolCallCount: this.toolCalls.size,
380
- };
381
- }
382
- handleReasoningStart(messageId) {
383
- if (!this.showThinking)
384
- return;
385
- // A reasoning block acts as a boundary: anonymous text arriving after it
386
- // should be a new part, not appended to any pre-reasoning text.
387
- this.activeTextMessageId = undefined;
388
- const key = messageId ?? `__auto-reasoning-${++this.reasoningPartCounter}`;
389
- if (!this.reasoningParts.has(key)) {
390
- this.reasoningParts.set(key, "");
391
- this.partOrder.push({ kind: "reasoning", key });
392
- }
393
- this.activeReasoningKey = key;
394
- this.emit();
395
- }
396
- handleReasoningContent(delta, messageId) {
397
- if (!this.showThinking || !delta)
398
- return;
399
- if (!this.activeReasoningKey) {
400
- // Content arrived without a preceding START — create the slot lazily.
401
- this.handleReasoningStart(messageId);
402
- }
403
- const key = this.activeReasoningKey;
404
- if (!key)
405
- return;
406
- this.reasoningParts.set(key, (this.reasoningParts.get(key) ?? "") + delta);
407
- this.emit();
408
- }
409
- handleReasoningEnd() {
410
- if (!this.showThinking)
411
- return;
412
- this.activeReasoningKey = undefined;
413
- this.emit();
414
- }
415
- }
5
+ * Collects AG-UI events into assistant-ui run snapshots that can be yielded from a ChatModelAdapter.
6
+ *
7
+ * The aggregator keeps a single assistant message worth of parts. Each incoming event updates the parts and
8
+ * emits a fresh snapshot through the provided `emit` callback.
9
+ */
10
+ var RunAggregator = class {
11
+ emitUpdate;
12
+ showThinking;
13
+ logger;
14
+ onServerMessageId;
15
+ status;
16
+ interrupts;
17
+ textParts = /* @__PURE__ */ new Map();
18
+ activeTextMessageId;
19
+ reasoningParts = /* @__PURE__ */ new Map();
20
+ activeReasoningKey;
21
+ reasoningPartCounter = 0;
22
+ toolCalls = /* @__PURE__ */ new Map();
23
+ partOrder = [];
24
+ textPartCounter = 0;
25
+ serverMessageIdReported = false;
26
+ streamStartTime;
27
+ firstTokenTime;
28
+ totalChunks = 0;
29
+ constructor(options) {
30
+ this.emitUpdate = options.emit;
31
+ this.showThinking = options.showThinking;
32
+ this.logger = options.logger;
33
+ this.onServerMessageId = options.onServerMessageId;
34
+ }
35
+ handle(event) {
36
+ switch (event.type) {
37
+ case "RUN_STARTED":
38
+ this.clearTextParts();
39
+ this.reasoningParts.clear();
40
+ this.activeReasoningKey = void 0;
41
+ this.reasoningPartCounter = 0;
42
+ this.toolCalls.clear();
43
+ this.partOrder.length = 0;
44
+ this.textPartCounter = 0;
45
+ this.activeTextMessageId = void 0;
46
+ this.interrupts = void 0;
47
+ this.serverMessageIdReported = false;
48
+ this.streamStartTime = Date.now();
49
+ this.firstTokenTime = void 0;
50
+ this.totalChunks = 0;
51
+ this.status = { type: "running" };
52
+ this.emit();
53
+ break;
54
+ case "RUN_FINISHED": {
55
+ if (event.outcome?.type === "interrupt") {
56
+ this.interrupts = event.outcome.interrupts;
57
+ this.status = {
58
+ type: "requires-action",
59
+ reason: "interrupt"
60
+ };
61
+ this.emit();
62
+ break;
63
+ }
64
+ this.interrupts = void 0;
65
+ const hasUnresolvedToolCalls = Array.from(this.toolCalls.values()).some((tc) => tc.result === void 0);
66
+ this.status = event.outcome?.type === "success" || !hasUnresolvedToolCalls ? {
67
+ type: "complete",
68
+ reason: "unknown"
69
+ } : {
70
+ type: "requires-action",
71
+ reason: "tool-calls"
72
+ };
73
+ this.emit();
74
+ break;
75
+ }
76
+ case "RUN_ERROR":
77
+ this.status = {
78
+ type: "incomplete",
79
+ reason: "error",
80
+ ...event.message !== void 0 ? { error: event.message } : {}
81
+ };
82
+ this.emit();
83
+ break;
84
+ case "RUN_CANCELLED":
85
+ this.status = {
86
+ type: "incomplete",
87
+ reason: "cancelled"
88
+ };
89
+ this.emit();
90
+ break;
91
+ case "TEXT_MESSAGE_START": {
92
+ this.reportServerMessageId(event.messageId);
93
+ const id = this.startTextMessage(event.messageId);
94
+ if (id) this.markTextPartTouched(id);
95
+ this.emit();
96
+ break;
97
+ }
98
+ case "TEXT_MESSAGE_CONTENT":
99
+ case "TEXT_MESSAGE_CHUNK": {
100
+ const incomingId = "messageId" in event ? event.messageId : void 0;
101
+ this.reportServerMessageId(incomingId);
102
+ if (!event.delta) break;
103
+ this.recordFirstToken();
104
+ const id = this.resolveTextMessageId(incomingId);
105
+ this.appendText(id, event.delta);
106
+ this.totalChunks++;
107
+ this.emit();
108
+ break;
109
+ }
110
+ case "TEXT_MESSAGE_END":
111
+ this.reportServerMessageId(event.messageId);
112
+ if (event.messageId && this.activeTextMessageId === event.messageId) this.activeTextMessageId = void 0;
113
+ this.emit();
114
+ break;
115
+ case "THINKING_START":
116
+ case "THINKING_TEXT_MESSAGE_START":
117
+ case "REASONING_START":
118
+ case "REASONING_MESSAGE_START":
119
+ this.handleReasoningStart("messageId" in event ? event.messageId : void 0);
120
+ break;
121
+ case "THINKING_TEXT_MESSAGE_CONTENT":
122
+ this.handleReasoningContent(event.delta);
123
+ this.totalChunks++;
124
+ this.recordFirstToken();
125
+ break;
126
+ case "REASONING_MESSAGE_CONTENT":
127
+ this.handleReasoningContent(event.delta, "messageId" in event ? event.messageId : void 0);
128
+ this.totalChunks++;
129
+ this.recordFirstToken();
130
+ break;
131
+ case "THINKING_TEXT_MESSAGE_END":
132
+ case "THINKING_END":
133
+ case "REASONING_MESSAGE_END":
134
+ case "REASONING_END":
135
+ this.handleReasoningEnd();
136
+ break;
137
+ case "TOOL_CALL_START":
138
+ this.reportServerMessageId(event.parentMessageId);
139
+ this.startToolCall(event.toolCallId, event.toolCallName, event.parentMessageId);
140
+ this.emit();
141
+ break;
142
+ case "TOOL_CALL_ARGS":
143
+ case "TOOL_CALL_CHUNK":
144
+ if (event.type === "TOOL_CALL_CHUNK") this.reportServerMessageId(event.parentMessageId);
145
+ if (!event.delta) break;
146
+ this.appendToolArgs(event.toolCallId, event.delta);
147
+ this.emit();
148
+ break;
149
+ case "TOOL_CALL_END":
150
+ this.emit();
151
+ break;
152
+ case "TOOL_CALL_RESULT":
153
+ this.finishToolCall(event.toolCallId, event.content ?? "", event.role === "tool" ? false : void 0, event.messageId);
154
+ this.emit();
155
+ break;
156
+ default: this.logger.debug?.("[agui] aggregator ignored event", event);
157
+ }
158
+ }
159
+ reportServerMessageId(messageId) {
160
+ if (this.serverMessageIdReported || !messageId) return;
161
+ this.serverMessageIdReported = true;
162
+ this.onServerMessageId?.(messageId);
163
+ }
164
+ clearTextParts() {
165
+ this.textParts.clear();
166
+ }
167
+ generateTextKey() {
168
+ this.textPartCounter += 1;
169
+ return `text-${this.textPartCounter}`;
170
+ }
171
+ startTextMessage(messageId) {
172
+ const id = messageId ?? this.generateTextKey();
173
+ this.ensureTextPart(id);
174
+ this.activeTextMessageId = id;
175
+ return id;
176
+ }
177
+ resolveTextMessageId(messageId) {
178
+ if (messageId) {
179
+ this.ensureTextPart(messageId);
180
+ this.activeTextMessageId = messageId;
181
+ return messageId;
182
+ }
183
+ if (this.activeTextMessageId) return this.activeTextMessageId;
184
+ const generated = this.generateTextKey();
185
+ this.ensureTextPart(generated);
186
+ this.activeTextMessageId = generated;
187
+ return generated;
188
+ }
189
+ ensureTextPart(id) {
190
+ if (!this.textParts.has(id)) {
191
+ this.textParts.set(id, {
192
+ buffer: "",
193
+ touched: false
194
+ });
195
+ if (!this.partOrder.some((part) => part.kind === "text" && part.key === id)) this.partOrder.push({
196
+ kind: "text",
197
+ key: id
198
+ });
199
+ }
200
+ }
201
+ markTextPartTouched(id) {
202
+ const entry = this.textParts.get(id);
203
+ if (!entry) return;
204
+ entry.touched = true;
205
+ }
206
+ appendText(id, delta) {
207
+ this.ensureTextPart(id);
208
+ const entry = this.textParts.get(id);
209
+ if (!entry) return;
210
+ entry.buffer += delta;
211
+ entry.touched = true;
212
+ }
213
+ startToolCall(id, name, parentMessageId) {
214
+ if (!id) return;
215
+ this.activeTextMessageId = void 0;
216
+ if (!this.partOrder.some((part) => part.kind === "tool-call" && part.toolCallId === id)) this.partOrder.push({
217
+ kind: "tool-call",
218
+ toolCallId: id
219
+ });
220
+ const state = {
221
+ toolCallId: id,
222
+ toolCallName: name ?? "tool",
223
+ argsText: "",
224
+ parsedArgs: void 0,
225
+ result: void 0,
226
+ isError: void 0
227
+ };
228
+ if (parentMessageId) state.parentMessageId = parentMessageId;
229
+ this.toolCalls.set(id, state);
230
+ }
231
+ appendToolArgs(id, delta) {
232
+ const entry = id ? this.toolCalls.get(id) : void 0;
233
+ if (!entry) return;
234
+ entry.argsText += delta;
235
+ try {
236
+ const parsed = JSON.parse(entry.argsText);
237
+ if (parsed && typeof parsed === "object") entry.parsedArgs = parsed;
238
+ else entry.parsedArgs = void 0;
239
+ } catch {
240
+ entry.parsedArgs = void 0;
241
+ }
242
+ }
243
+ finishToolCall(id, content, isError, toolMessageId) {
244
+ if (!id) return;
245
+ let entry = this.toolCalls.get(id);
246
+ if (!entry) {
247
+ entry = {
248
+ toolCallId: id,
249
+ toolCallName: "tool",
250
+ argsText: "",
251
+ parsedArgs: void 0,
252
+ result: void 0,
253
+ isError: void 0
254
+ };
255
+ this.toolCalls.set(id, entry);
256
+ }
257
+ if (!this.partOrder.some((part) => part.kind === "tool-call" && part.toolCallId === id)) this.partOrder.push({
258
+ kind: "tool-call",
259
+ toolCallId: id
260
+ });
261
+ entry.result = this.tryParseJSON(content);
262
+ entry.isError = isError;
263
+ if (toolMessageId) entry.toolMessageId = toolMessageId;
264
+ }
265
+ tryParseJSON(value) {
266
+ if (!value) return value;
267
+ try {
268
+ return JSON.parse(value);
269
+ } catch {
270
+ return value;
271
+ }
272
+ }
273
+ emit() {
274
+ const snapshot = [];
275
+ for (const part of this.partOrder) {
276
+ if (part.kind === "reasoning") {
277
+ if (this.showThinking) {
278
+ const buffer = this.reasoningParts.get(part.key) ?? "";
279
+ if (buffer.length > 0 || this.activeReasoningKey === part.key) snapshot.push({
280
+ type: "reasoning",
281
+ text: buffer
282
+ });
283
+ }
284
+ continue;
285
+ }
286
+ if (part.kind === "text") {
287
+ const entry = this.textParts.get(part.key);
288
+ if (entry?.touched) snapshot.push({
289
+ type: "text",
290
+ text: entry.buffer
291
+ });
292
+ continue;
293
+ }
294
+ const entry = this.toolCalls.get(part.toolCallId);
295
+ if (!entry) continue;
296
+ const toolPart = {
297
+ type: "tool-call",
298
+ toolCallId: entry.toolCallId,
299
+ toolName: entry.toolCallName,
300
+ args: entry.parsedArgs ?? {},
301
+ argsText: entry.argsText,
302
+ ...entry.result !== void 0 ? { result: entry.result } : {},
303
+ ...entry.isError !== void 0 ? { isError: entry.isError } : {},
304
+ ...entry.parentMessageId ? { parentId: entry.parentMessageId } : {},
305
+ ...entry.toolMessageId ? { unstable_toolMessageId: entry.toolMessageId } : {}
306
+ };
307
+ snapshot.push(toolPart);
308
+ }
309
+ const timing = this.getTiming();
310
+ const metadata = {
311
+ ...timing ? { timing } : {},
312
+ ...this.interrupts ? { custom: { [AG_UI_METADATA_NAMESPACE]: { interrupts: this.interrupts } } } : {}
313
+ };
314
+ const result = {
315
+ content: snapshot,
316
+ ...this.status ? { status: this.status } : void 0,
317
+ ...Object.keys(metadata).length > 0 ? { metadata } : void 0
318
+ };
319
+ this.emitUpdate(result);
320
+ }
321
+ recordFirstToken() {
322
+ if (this.firstTokenTime === void 0 && this.streamStartTime !== void 0) this.firstTokenTime = Date.now() - this.streamStartTime;
323
+ }
324
+ getTiming() {
325
+ if (this.streamStartTime === void 0) return void 0;
326
+ const totalStreamTime = Date.now() - this.streamStartTime;
327
+ const tokenCount = this.totalChunks > 0 ? Math.ceil(Array.from(this.textParts.values()).reduce((sum, p) => sum + p.buffer.length, 0) / 4) : void 0;
328
+ const tokensPerSecond = tokenCount && totalStreamTime > 0 ? tokenCount / totalStreamTime * 1e3 : void 0;
329
+ return {
330
+ streamStartTime: this.streamStartTime,
331
+ ...this.firstTokenTime !== void 0 ? { firstTokenTime: this.firstTokenTime } : {},
332
+ totalStreamTime,
333
+ ...tokenCount !== void 0 ? { tokenCount } : {},
334
+ ...tokensPerSecond !== void 0 ? { tokensPerSecond } : {},
335
+ totalChunks: this.totalChunks,
336
+ toolCallCount: this.toolCalls.size
337
+ };
338
+ }
339
+ handleReasoningStart(messageId) {
340
+ if (!this.showThinking) return;
341
+ this.activeTextMessageId = void 0;
342
+ const key = messageId ?? `__auto-reasoning-${++this.reasoningPartCounter}`;
343
+ if (!this.reasoningParts.has(key)) {
344
+ this.reasoningParts.set(key, "");
345
+ this.partOrder.push({
346
+ kind: "reasoning",
347
+ key
348
+ });
349
+ }
350
+ this.activeReasoningKey = key;
351
+ this.emit();
352
+ }
353
+ handleReasoningContent(delta, messageId) {
354
+ if (!this.showThinking || !delta) return;
355
+ if (!this.activeReasoningKey) this.handleReasoningStart(messageId);
356
+ const key = this.activeReasoningKey;
357
+ if (!key) return;
358
+ this.reasoningParts.set(key, (this.reasoningParts.get(key) ?? "") + delta);
359
+ this.emit();
360
+ }
361
+ handleReasoningEnd() {
362
+ if (!this.showThinking) return;
363
+ this.activeReasoningKey = void 0;
364
+ this.emit();
365
+ }
366
+ };
367
+ //#endregion
368
+ export { AG_UI_METADATA_NAMESPACE, RunAggregator };
369
+
416
370
  //# sourceMappingURL=run-aggregator.js.map