@assistant-ui/react-ag-ui 0.1.1 → 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/runtime/AgUiThreadController.d.ts +11 -0
- package/dist/runtime/AgUiThreadController.d.ts.map +1 -1
- package/dist/runtime/AgUiThreadController.js +212 -22
- 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 +184 -9
- package/dist/runtime/adapter/conversions.js.map +1 -1
- 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 +34 -12
- 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 +1 -8
- package/dist/runtime/adapter/subscriber.js.map +1 -1
- package/dist/runtime/event-parser.d.ts.map +1 -1
- package/dist/runtime/event-parser.js +1 -0
- package/dist/runtime/event-parser.js.map +1 -1
- package/dist/runtime/types.d.ts +2 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js +2 -1
- package/dist/runtime/types.js.map +1 -1
- package/package.json +5 -5
- 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 +8 -1
- 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 +40 -0
- package/src/runtime/AgUiThreadController.ts +459 -27
- package/src/runtime/adapter/conversions.content.test.ts +129 -0
- package/src/runtime/adapter/conversions.subagent-messages.test.ts +550 -0
- package/src/runtime/adapter/conversions.ts +331 -20
- 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/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;
|
|
@@ -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
|
| {
|