@assistant-ui/react-ag-ui 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/agUiExtras.js +1 -2
- package/dist/hooks.js +1 -2
- package/dist/runtime/AgUiThreadController.d.ts +11 -0
- package/dist/runtime/AgUiThreadController.d.ts.map +1 -1
- package/dist/runtime/AgUiThreadController.js +565 -89
- package/dist/runtime/AgUiThreadController.js.map +1 -1
- package/dist/runtime/adapter/conversions.d.ts +9 -1
- package/dist/runtime/adapter/conversions.d.ts.map +1 -1
- package/dist/runtime/adapter/conversions.js +194 -12
- package/dist/runtime/adapter/conversions.js.map +1 -1
- package/dist/runtime/adapter/replay-content.d.ts +9 -0
- package/dist/runtime/adapter/replay-content.d.ts.map +1 -0
- package/dist/runtime/adapter/replay-content.js +185 -0
- package/dist/runtime/adapter/replay-content.js.map +1 -0
- package/dist/runtime/adapter/run-aggregator.d.ts +1 -0
- package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
- package/dist/runtime/adapter/run-aggregator.js +35 -14
- package/dist/runtime/adapter/run-aggregator.js.map +1 -1
- package/dist/runtime/adapter/subscriber.d.ts +0 -1
- package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
- package/dist/runtime/adapter/subscriber.js +2 -10
- package/dist/runtime/adapter/subscriber.js.map +1 -1
- package/dist/runtime/adapter/tool-approval.d.ts +2 -0
- package/dist/runtime/adapter/tool-approval.d.ts.map +1 -1
- package/dist/runtime/adapter/tool-approval.js +23 -6
- package/dist/runtime/adapter/tool-approval.js.map +1 -1
- package/dist/runtime/agUiThreadState.d.ts +1 -1
- package/dist/runtime/agUiThreadState.d.ts.map +1 -1
- package/dist/runtime/agUiThreadState.js +3 -4
- package/dist/runtime/agUiThreadState.js.map +1 -1
- package/dist/runtime/event-parser.d.ts.map +1 -1
- package/dist/runtime/event-parser.js +2 -2
- package/dist/runtime/event-parser.js.map +1 -1
- package/dist/runtime/interrupt-internals.js +1 -2
- package/dist/runtime/logger.js +1 -2
- package/dist/runtime/mcp-tool-result.js +1 -2
- package/dist/runtime/types.d.ts +2 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js +3 -3
- package/dist/runtime/types.js.map +1 -1
- package/dist/useAgUiRuntime.js +1 -2
- package/package.json +6 -6
- package/src/runtime/AgUiThreadController.deferred-results.test.ts +548 -0
- package/src/runtime/AgUiThreadController.disconnect.test.ts +914 -0
- package/src/runtime/AgUiThreadController.interrupt-history.test.ts +392 -0
- package/src/runtime/AgUiThreadController.replay.test.ts +3939 -0
- package/src/runtime/AgUiThreadController.snapshot-head-move.test.ts +167 -0
- package/src/runtime/AgUiThreadController.subagent-snapshot.test.ts +854 -0
- package/src/runtime/AgUiThreadController.test.ts +119 -0
- package/src/runtime/AgUiThreadController.ts +1184 -136
- package/src/runtime/adapter/conversions.content.test.ts +242 -1
- package/src/runtime/adapter/conversions.subagent-messages.test.ts +550 -0
- package/src/runtime/adapter/conversions.ts +349 -28
- package/src/runtime/adapter/replay-content.test.ts +235 -0
- package/src/runtime/adapter/replay-content.ts +306 -0
- package/src/runtime/adapter/run-aggregator.metadata.test.ts +151 -0
- package/src/runtime/adapter/run-aggregator.ts +52 -9
- package/src/runtime/adapter/subscriber.test.ts +1 -1
- package/src/runtime/adapter/subscriber.ts +1 -6
- package/src/runtime/adapter/tool-approval.ts +41 -3
- package/src/runtime/agUiThreadState.test.ts +21 -2
- package/src/runtime/agUiThreadState.ts +12 -3
- package/src/runtime/event-parser.ts +3 -0
- package/src/runtime/types.ts +5 -0
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import jsonpatch, { type Operation } from "fast-json-patch";
|
|
23
23
|
import { readMcpAppResourceUri, toolContentText } from "../mcp-tool-result";
|
|
24
24
|
import { projectAgUiToolApprovals } from "./tool-approval";
|
|
25
|
+
import { withAgUiPartMessageMetadata } from "./conversions";
|
|
25
26
|
import {
|
|
26
27
|
AG_UI_METADATA_NAMESPACE,
|
|
27
28
|
A2UI_SURFACE_ACTIVITY_TYPE,
|
|
@@ -30,15 +31,12 @@ import {
|
|
|
30
31
|
type AgUiEvent,
|
|
31
32
|
type AgUiInterrupt,
|
|
32
33
|
type AgUiOpaqueReasoning,
|
|
34
|
+
MAX_SUBAGENT_DEPTH,
|
|
33
35
|
} from "../types";
|
|
34
36
|
import type { Logger } from "../logger";
|
|
35
37
|
|
|
36
38
|
const ROOT_SCOPE = "";
|
|
37
39
|
|
|
38
|
-
// Defensive guard against a malformed or cyclic parentToolCallId /
|
|
39
|
-
// parentSubagentRunId chain, not a realistic nesting depth.
|
|
40
|
-
const MAX_SUBAGENT_DEPTH = 16;
|
|
41
|
-
|
|
42
40
|
type SubagentRunState = {
|
|
43
41
|
subagentRunId: string;
|
|
44
42
|
name: string;
|
|
@@ -93,6 +91,7 @@ type ToolCallState = {
|
|
|
93
91
|
mcpAppServerId?: string;
|
|
94
92
|
artifact?: unknown;
|
|
95
93
|
modelContent?: ToolModelContentPart[];
|
|
94
|
+
resultMetadata?: Record<string, unknown>;
|
|
96
95
|
snapshotResultApplied: boolean;
|
|
97
96
|
subagentRunId?: string;
|
|
98
97
|
};
|
|
@@ -209,6 +208,7 @@ export class RunAggregator {
|
|
|
209
208
|
private interrupts: AgUiInterrupt[] | undefined;
|
|
210
209
|
private pendingToolCallIds: string[] | undefined;
|
|
211
210
|
private readonly textParts = new Map<string, { buffer: string }>();
|
|
211
|
+
private readonly pendingSnapshotText = new Map<string, string>();
|
|
212
212
|
private readonly activeTextMessageIdByScope = new Map<string, string>();
|
|
213
213
|
private readonly reasoningParts = new Map<
|
|
214
214
|
string,
|
|
@@ -282,6 +282,20 @@ export class RunAggregator {
|
|
|
282
282
|
this.emit();
|
|
283
283
|
break;
|
|
284
284
|
}
|
|
285
|
+
case "MESSAGES_SNAPSHOT": {
|
|
286
|
+
this.pendingSnapshotText.clear();
|
|
287
|
+
for (const message of event.messages) {
|
|
288
|
+
if (
|
|
289
|
+
message.role !== "assistant" ||
|
|
290
|
+
typeof message.content !== "string"
|
|
291
|
+
)
|
|
292
|
+
continue;
|
|
293
|
+
const part = this.textParts.get(message.id);
|
|
294
|
+
if (part) part.buffer = message.content;
|
|
295
|
+
else this.pendingSnapshotText.set(message.id, message.content);
|
|
296
|
+
}
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
285
299
|
case "RUN_FINISHED": {
|
|
286
300
|
if (event.outcome?.type === "cancelled") {
|
|
287
301
|
this.interrupts = undefined;
|
|
@@ -368,7 +382,7 @@ export class RunAggregator {
|
|
|
368
382
|
const incomingId = event.messageId;
|
|
369
383
|
const scope = this.scopeOf(event);
|
|
370
384
|
if (scope === ROOT_SCOPE) {
|
|
371
|
-
this.beginDistinctTextMessage(incomingId);
|
|
385
|
+
this.beginDistinctTextMessage(incomingId, true);
|
|
372
386
|
this.reportServerMessageId(incomingId);
|
|
373
387
|
if (incomingId) this.lastTextMessageId = incomingId;
|
|
374
388
|
}
|
|
@@ -494,6 +508,7 @@ export class RunAggregator {
|
|
|
494
508
|
: undefined,
|
|
495
509
|
event.messageId,
|
|
496
510
|
event.mcpResult,
|
|
511
|
+
event.metadata,
|
|
497
512
|
);
|
|
498
513
|
this.emit();
|
|
499
514
|
break;
|
|
@@ -793,6 +808,7 @@ export class RunAggregator {
|
|
|
793
808
|
|
|
794
809
|
private resetMessageParts(): void {
|
|
795
810
|
this.clearTextParts();
|
|
811
|
+
this.pendingSnapshotText.clear();
|
|
796
812
|
this.reasoningParts.clear();
|
|
797
813
|
this.reasoningSignatures.clear();
|
|
798
814
|
this.reasoningSignatureIds.clear();
|
|
@@ -837,7 +853,10 @@ export class RunAggregator {
|
|
|
837
853
|
return scope === ROOT_SCOPE ? id : `${scope}\u0000${id}`;
|
|
838
854
|
}
|
|
839
855
|
|
|
840
|
-
private beginDistinctTextMessage(
|
|
856
|
+
private beginDistinctTextMessage(
|
|
857
|
+
messageId: string | undefined,
|
|
858
|
+
preserveSnapshotText = false,
|
|
859
|
+
): void {
|
|
841
860
|
if (
|
|
842
861
|
this.joinStrategy === "concat-content" ||
|
|
843
862
|
!messageId ||
|
|
@@ -854,7 +873,13 @@ export class RunAggregator {
|
|
|
854
873
|
const openToolCallIds = Array.from(this.toolCalls.values())
|
|
855
874
|
.filter((call) => call.result === undefined)
|
|
856
875
|
.map((call) => call.toolCallId);
|
|
876
|
+
const snapshotText = preserveSnapshotText
|
|
877
|
+
? this.pendingSnapshotText.get(messageId)
|
|
878
|
+
: undefined;
|
|
857
879
|
this.resetMessageParts();
|
|
880
|
+
if (snapshotText !== undefined) {
|
|
881
|
+
this.pendingSnapshotText.set(messageId, snapshotText);
|
|
882
|
+
}
|
|
858
883
|
this.onTextMessageStart(messageId, openToolCallIds);
|
|
859
884
|
this.restoreReasoning(carried);
|
|
860
885
|
}
|
|
@@ -946,15 +971,21 @@ export class RunAggregator {
|
|
|
946
971
|
private startTextMessage(scope: string, messageId?: string): string {
|
|
947
972
|
const id = messageId ?? this.generateTextKey();
|
|
948
973
|
const key = this.ensureTextPart(scope, id);
|
|
974
|
+
if (scope === ROOT_SCOPE && messageId) {
|
|
975
|
+
const snapshotText = this.pendingSnapshotText.get(messageId);
|
|
976
|
+
const part = this.textParts.get(key);
|
|
977
|
+
if (snapshotText !== undefined && part) {
|
|
978
|
+
part.buffer = snapshotText;
|
|
979
|
+
this.pendingSnapshotText.delete(messageId);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
949
982
|
this.activeTextMessageIdByScope.set(scope, key);
|
|
950
983
|
return key;
|
|
951
984
|
}
|
|
952
985
|
|
|
953
986
|
private resolveTextMessageId(scope: string, messageId?: string): string {
|
|
954
987
|
if (messageId) {
|
|
955
|
-
|
|
956
|
-
this.activeTextMessageIdByScope.set(scope, key);
|
|
957
|
-
return key;
|
|
988
|
+
return this.startTextMessage(scope, messageId);
|
|
958
989
|
}
|
|
959
990
|
|
|
960
991
|
const active = this.activeTextMessageIdByScope.get(scope);
|
|
@@ -1098,6 +1129,7 @@ export class RunAggregator {
|
|
|
1098
1129
|
isError?: boolean,
|
|
1099
1130
|
toolMessageId?: string,
|
|
1100
1131
|
mcpResult?: Record<string, unknown>,
|
|
1132
|
+
metadata?: Record<string, unknown>,
|
|
1101
1133
|
) {
|
|
1102
1134
|
if (!id) return;
|
|
1103
1135
|
const modelText = toolContentText(content);
|
|
@@ -1149,6 +1181,9 @@ export class RunAggregator {
|
|
|
1149
1181
|
if (toolMessageId) {
|
|
1150
1182
|
entry.toolMessageId = toolMessageId;
|
|
1151
1183
|
}
|
|
1184
|
+
if (metadata !== undefined) {
|
|
1185
|
+
entry.resultMetadata = { ...entry.resultMetadata, ...metadata };
|
|
1186
|
+
}
|
|
1152
1187
|
this.lastResolvedToolCallIdByScope.set(scope, id);
|
|
1153
1188
|
}
|
|
1154
1189
|
|
|
@@ -1299,6 +1334,14 @@ export class RunAggregator {
|
|
|
1299
1334
|
...(entry.toolMessageId
|
|
1300
1335
|
? { unstable_toolMessageId: entry.toolMessageId }
|
|
1301
1336
|
: {}),
|
|
1337
|
+
...(entry.resultMetadata !== undefined
|
|
1338
|
+
? {
|
|
1339
|
+
providerMetadata: withAgUiPartMessageMetadata(
|
|
1340
|
+
undefined,
|
|
1341
|
+
entry.resultMetadata,
|
|
1342
|
+
),
|
|
1343
|
+
}
|
|
1344
|
+
: {}),
|
|
1302
1345
|
...(nestedMessages.length > 0 ? { messages: nestedMessages } : {}),
|
|
1303
1346
|
} as ToolCallMessagePart & { unstable_toolMessageId?: string };
|
|
1304
1347
|
snapshot.push(toolPart);
|
|
@@ -4,7 +4,7 @@ import { createAgUiSubscriber } from "./subscriber";
|
|
|
4
4
|
const createSubscriber = () => {
|
|
5
5
|
const dispatch = vi.fn();
|
|
6
6
|
const logger = { debug: vi.fn(), error: vi.fn() };
|
|
7
|
-
const subscriber = createAgUiSubscriber({ dispatch,
|
|
7
|
+
const subscriber = createAgUiSubscriber({ dispatch, logger });
|
|
8
8
|
return { subscriber, dispatch, logger };
|
|
9
9
|
};
|
|
10
10
|
|
|
@@ -60,7 +60,6 @@ const ensureEvent = (
|
|
|
60
60
|
|
|
61
61
|
type SubscriberOptions = {
|
|
62
62
|
dispatch: Dispatch;
|
|
63
|
-
runId: string;
|
|
64
63
|
logger?: Logger;
|
|
65
64
|
onRunFailed?: (error: Error) => void;
|
|
66
65
|
};
|
|
@@ -68,7 +67,7 @@ type SubscriberOptions = {
|
|
|
68
67
|
export const createAgUiSubscriber = (
|
|
69
68
|
options: SubscriberOptions,
|
|
70
69
|
): Subscriber => {
|
|
71
|
-
const { dispatch,
|
|
70
|
+
const { dispatch, logger, onRunFailed } = options;
|
|
72
71
|
let runFinishedDispatched = false;
|
|
73
72
|
const dispatchIfValid = (raw: unknown, type: AgUiEvent["type"]) => {
|
|
74
73
|
const event = ensureEvent(raw, type, logger);
|
|
@@ -140,10 +139,6 @@ export const createAgUiSubscriber = (
|
|
|
140
139
|
logger?.debug?.("[agui] step boundary ignored", event),
|
|
141
140
|
onStepFinishedEvent: ({ event }) =>
|
|
142
141
|
logger?.debug?.("[agui] step boundary ignored", event),
|
|
143
|
-
onRunFinalized: () => {
|
|
144
|
-
if (runFinishedDispatched) return;
|
|
145
|
-
dispatch({ type: "RUN_FINISHED", runId });
|
|
146
|
-
},
|
|
147
142
|
onRunFailed: ({ error }) => {
|
|
148
143
|
if (runFinishedDispatched) return;
|
|
149
144
|
runFinishedDispatched = true;
|
|
@@ -228,6 +228,40 @@ export const projectAgUiToolApprovals = (
|
|
|
228
228
|
);
|
|
229
229
|
};
|
|
230
230
|
|
|
231
|
+
/** A recorded decision authorizes one exact call and gate, not a reused id. */
|
|
232
|
+
export const preserveToolApprovalDecision = (
|
|
233
|
+
previous: ToolCallMessagePart | undefined,
|
|
234
|
+
next: ToolCallMessagePart,
|
|
235
|
+
): ToolApproval | undefined => {
|
|
236
|
+
const prior = previous?.approval;
|
|
237
|
+
let approval = next.approval;
|
|
238
|
+
if (!approval && prior) {
|
|
239
|
+
const {
|
|
240
|
+
approved: _approved,
|
|
241
|
+
reason: _reason,
|
|
242
|
+
resolution: _resolution,
|
|
243
|
+
...pending
|
|
244
|
+
} = prior;
|
|
245
|
+
approval = pending;
|
|
246
|
+
}
|
|
247
|
+
if (!approval) return undefined;
|
|
248
|
+
// AG-UI may omit argument events for a no-argument call; both forms mean {}.
|
|
249
|
+
if (
|
|
250
|
+
!previous ||
|
|
251
|
+
prior?.id !== approval.id ||
|
|
252
|
+
previous.toolCallId !== next.toolCallId ||
|
|
253
|
+
previous.toolName !== next.toolName ||
|
|
254
|
+
(previous.argsText || "{}") !== (next.argsText || "{}")
|
|
255
|
+
)
|
|
256
|
+
return approval;
|
|
257
|
+
return {
|
|
258
|
+
...approval,
|
|
259
|
+
...(prior.approved !== undefined ? { approved: prior.approved } : {}),
|
|
260
|
+
...(prior.reason !== undefined ? { reason: prior.reason } : {}),
|
|
261
|
+
...(prior.resolution !== undefined ? { resolution: prior.resolution } : {}),
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
|
|
231
265
|
/**
|
|
232
266
|
* Records a decision on the gated tool call. The decision is held on the part
|
|
233
267
|
* until every gate in the batch is answered, because AG-UI resumes a run with
|
|
@@ -266,16 +300,20 @@ export const buildToolApprovalResume = (
|
|
|
266
300
|
content: readonly ThreadAssistantMessagePart[],
|
|
267
301
|
interrupts: readonly AgUiInterrupt[],
|
|
268
302
|
): AgUiResumeEntry[] | null => {
|
|
269
|
-
const
|
|
303
|
+
const calls = Array.from(iterateToolCallParts(content));
|
|
304
|
+
const gates = projectAgUiToolApprovals(
|
|
305
|
+
interrupts,
|
|
306
|
+
new Set(calls.map((part) => part.toolCallId)),
|
|
307
|
+
);
|
|
270
308
|
// Decisions accumulate in a `Map` rather than in an object literal, because an
|
|
271
309
|
// interrupt id of `__proto__` would otherwise write to the prototype chain
|
|
272
310
|
// instead of recording a response, and `constructor` or `toString` would
|
|
273
311
|
// report themselves as answered while undecided.
|
|
274
312
|
const responses = new Map<string, { status: "resolved"; payload: unknown }>();
|
|
275
|
-
for (const part of
|
|
313
|
+
for (const part of calls) {
|
|
276
314
|
const approval = part.approval;
|
|
277
315
|
if (!approval || approval.approved === undefined) continue;
|
|
278
|
-
if (
|
|
316
|
+
if (gates.get(part.toolCallId)?.id !== approval.id) continue;
|
|
279
317
|
responses.set(approval.id, {
|
|
280
318
|
status: "resolved",
|
|
281
319
|
payload: {
|
|
@@ -115,7 +115,10 @@ describe("reduceAgUiThreadState", () => {
|
|
|
115
115
|
owner,
|
|
116
116
|
messageId: "assistant",
|
|
117
117
|
});
|
|
118
|
-
expect(withResume.pendingResume).toEqual({
|
|
118
|
+
expect(withResume.pendingResume).toEqual({
|
|
119
|
+
owner,
|
|
120
|
+
messageIds: ["assistant"],
|
|
121
|
+
});
|
|
119
122
|
expect(
|
|
120
123
|
reduceAgUiThreadState(withResume, {
|
|
121
124
|
type: "resumeDeferred",
|
|
@@ -123,6 +126,22 @@ describe("reduceAgUiThreadState", () => {
|
|
|
123
126
|
messageId: "assistant",
|
|
124
127
|
}),
|
|
125
128
|
).toBe(withResume);
|
|
129
|
+
const withTwoOwners = reduceAgUiThreadState(withResume, {
|
|
130
|
+
type: "resumeDeferred",
|
|
131
|
+
owner,
|
|
132
|
+
messageId: "assistant-next",
|
|
133
|
+
});
|
|
134
|
+
expect(withTwoOwners.pendingResume).toEqual({
|
|
135
|
+
owner,
|
|
136
|
+
messageIds: ["assistant", "assistant-next"],
|
|
137
|
+
});
|
|
138
|
+
expect(
|
|
139
|
+
reduceAgUiThreadState(withTwoOwners, {
|
|
140
|
+
type: "resumeDeferred",
|
|
141
|
+
owner: new AbortController(),
|
|
142
|
+
messageId: "new-run",
|
|
143
|
+
}).pendingResume?.messageIds,
|
|
144
|
+
).toEqual(["new-run"]);
|
|
126
145
|
const withOwner = reduceAgUiThreadState(withResume, {
|
|
127
146
|
type: "a2uiResumeDeferred",
|
|
128
147
|
owner,
|
|
@@ -189,7 +208,7 @@ describe("reduceAgUiThreadState", () => {
|
|
|
189
208
|
});
|
|
190
209
|
expect(cleared.pendingA2uiResumeOwner).toBeNull();
|
|
191
210
|
expect(cleared.pendingA2uiAction).toBeUndefined();
|
|
192
|
-
expect(cleared.pendingResume).toEqual({ owner,
|
|
211
|
+
expect(cleared.pendingResume).toEqual({ owner, messageIds: ["assistant"] });
|
|
193
212
|
expect(
|
|
194
213
|
reduceAgUiThreadState(cleared, { type: "a2uiContinuationsCleared" }),
|
|
195
214
|
).toBe(cleared);
|
|
@@ -8,7 +8,10 @@ export type AgUiThreadState = {
|
|
|
8
8
|
historyMutationGeneration: number;
|
|
9
9
|
stateSnapshot: ReadonlyJSONValue | undefined;
|
|
10
10
|
lastRunConfig: RunConfig | undefined;
|
|
11
|
-
pendingResume: {
|
|
11
|
+
pendingResume: {
|
|
12
|
+
owner: AbortController;
|
|
13
|
+
messageIds: readonly string[];
|
|
14
|
+
} | null;
|
|
12
15
|
pendingA2uiResumeOwner: AbortController | null;
|
|
13
16
|
pendingA2uiAction: Record<string, unknown> | undefined;
|
|
14
17
|
};
|
|
@@ -78,11 +81,17 @@ export const reduceAgUiThreadState = (
|
|
|
78
81
|
: { ...state, lastRunConfig: action.runConfig };
|
|
79
82
|
case "resumeDeferred":
|
|
80
83
|
return state.pendingResume?.owner === action.owner &&
|
|
81
|
-
state.pendingResume.
|
|
84
|
+
state.pendingResume.messageIds.includes(action.messageId)
|
|
82
85
|
? state
|
|
83
86
|
: {
|
|
84
87
|
...state,
|
|
85
|
-
pendingResume: {
|
|
88
|
+
pendingResume: {
|
|
89
|
+
owner: action.owner,
|
|
90
|
+
messageIds:
|
|
91
|
+
state.pendingResume?.owner === action.owner
|
|
92
|
+
? [...state.pendingResume.messageIds, action.messageId]
|
|
93
|
+
: [action.messageId],
|
|
94
|
+
},
|
|
86
95
|
};
|
|
87
96
|
case "resumeCleared":
|
|
88
97
|
return state.pendingResume === null
|
|
@@ -344,6 +344,9 @@ export const parseAgUiEvent = (
|
|
|
344
344
|
messageId: getString("messageId"),
|
|
345
345
|
role: payload.role === "tool" ? "tool" : undefined,
|
|
346
346
|
mcpResult: parseMcpToolCallResult(payload, content),
|
|
347
|
+
metadata: isPlainObject(payload.metadata)
|
|
348
|
+
? payload.metadata
|
|
349
|
+
: undefined,
|
|
347
350
|
subagentRunId: getString("subagentRunId"),
|
|
348
351
|
},
|
|
349
352
|
);
|
package/src/runtime/types.ts
CHANGED
|
@@ -26,6 +26,10 @@ export const AG_UI_METADATA_NAMESPACE = "agui";
|
|
|
26
26
|
export const MCP_APPS_ACTIVITY_TYPE = "mcp-apps";
|
|
27
27
|
export const A2UI_SURFACE_ACTIVITY_TYPE = "a2ui-surface";
|
|
28
28
|
|
|
29
|
+
// Defensive guard against a malformed or cyclic parentToolCallId /
|
|
30
|
+
// parentSubagentRunId chain, not a realistic nesting depth.
|
|
31
|
+
export const MAX_SUBAGENT_DEPTH = 16;
|
|
32
|
+
|
|
29
33
|
export type AgUiOpaqueReasoning = {
|
|
30
34
|
id: string;
|
|
31
35
|
encryptedValue: string;
|
|
@@ -278,6 +282,7 @@ export type AgUiEvent =
|
|
|
278
282
|
content: string | ContentPart[];
|
|
279
283
|
role?: "tool";
|
|
280
284
|
mcpResult?: McpToolCallResult;
|
|
285
|
+
metadata?: Record<string, unknown>;
|
|
281
286
|
subagentRunId?: string;
|
|
282
287
|
}
|
|
283
288
|
| {
|