@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
package/README.md
CHANGED
|
@@ -37,6 +37,8 @@ export function Provider({ children }: { children: React.ReactNode }) {
|
|
|
37
37
|
|
|
38
38
|
An AG-UI backend that runs subagents (the agents-as-tools pattern) emits `SUBAGENT_STARTED` / `SUBAGENT_FINISHED` / `SUBAGENT_ERROR` plus a `subagentRunId` on the events each subagent produces. This adapter groups that activity into one nested assistant message per subagent run and attaches it to the spawning tool call as `ToolCallMessagePart.messages`, joined on `SUBAGENT_STARTED.parentToolCallId`. `PartPrimitive.Messages` renders it without extra wiring, and the subagent's name, description, result, and error code ride on that message's `agui` metadata.
|
|
39
39
|
|
|
40
|
+
A `MESSAGES_SNAPSHOT` whose messages carry `subagentRunId` adds to the same nested messages instead of rendering them a second time in the parent thread: a run's messages attach to the tool call its `SUBAGENT_STARTED.parentToolCallId` named in this session, merged with what streamed live. A run that was never announced this session, and its messages, stay in the parent thread. The live grouping only reaches spawning calls of the current run, so a subagent resumed in a later run still streams into the parent thread, and after that run's snapshot can show both nested and in the parent thread.
|
|
41
|
+
|
|
40
42
|
A subagent that names no reachable spawning call (`parentToolCallId` is optional, may name a call this run never saw, or two runs may name each other) has nowhere to nest, so its output renders in the parent thread instead of being dropped. This matches the downgrade the protocol's own pre-subagent compatibility middleware performs.
|
|
41
43
|
|
|
42
44
|
A subagent's frontend-executed tool calls work the same way the root agent's do: a call nested on `ToolCallMessagePart.messages` is reachable by `getPendingToolCalls()`, resolves through `addToolResult`, and its result rides the resume as a `tool` record on the spawning assistant record, the same flattened wire shape these calls had before subagent attribution.
|
|
@@ -44,6 +44,7 @@ export declare class AgUiThreadController {
|
|
|
44
44
|
private activeRunAgent;
|
|
45
45
|
private history;
|
|
46
46
|
private readonly assistantHistoryParents;
|
|
47
|
+
private readonly subagentParentToolCallIds;
|
|
47
48
|
private readonly snapshotHistoryIds;
|
|
48
49
|
private readonly persistedHistoryIds;
|
|
49
50
|
private readonly historyWrites;
|
|
@@ -156,6 +157,15 @@ export declare class AgUiThreadController {
|
|
|
156
157
|
private preserveToolInteractions;
|
|
157
158
|
private mergeSnapshotAssistantContent;
|
|
158
159
|
private reconcileSnapshotNestedContent;
|
|
160
|
+
/**
|
|
161
|
+
* Merges one tool call's nested transcripts message by message. The incoming
|
|
162
|
+
* copy is the base for tool calls and reasoning; resolved results and calls
|
|
163
|
+
* only the prior copy has carry over, and the prior text wins when it extends
|
|
164
|
+
* the incoming text. Importing a snapshot keeps prior messages it lacks, a
|
|
165
|
+
* prior status that is running or incomplete, and the prior status when the
|
|
166
|
+
* snapshot waits on calls the merged content has already resolved.
|
|
167
|
+
*/
|
|
168
|
+
private mergeNestedTranscripts;
|
|
159
169
|
private mergeAssistantMetadata;
|
|
160
170
|
private handleEvent;
|
|
161
171
|
private updateActiveAssistantState;
|
|
@@ -167,6 +177,7 @@ export declare class AgUiThreadController {
|
|
|
167
177
|
private isPersistableStatus;
|
|
168
178
|
private recordHistoryEntry;
|
|
169
179
|
private markPendingAssistantHistory;
|
|
180
|
+
private persistSettledAssistant;
|
|
170
181
|
private persistAssistantHistory;
|
|
171
182
|
private appendHistoryItem;
|
|
172
183
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgUiThreadController.d.ts","sourceRoot":"","sources":["../../src/runtime/AgUiThreadController.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,yBAAyB,EAEzB,4BAA4B,EAC5B,SAAS,EAET,oBAAoB,EACpB,aAAa,EAEb,wBAAwB,EACzB,MAAM,oBAAoB,CAAC;AAS5B,OAAO,KAAK,EACV,aAAa,EAKd,MAAM,eAAe,CAAC;AAQvB,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAiB;AAEvC,OAAO,KAAK,EAGV,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,qBAAqB,EACtB,mBAAgB;AAEjB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"AgUiThreadController.d.ts","sourceRoot":"","sources":["../../src/runtime/AgUiThreadController.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,8 +3,8 @@ import { createAgUiThreadState, reduceAgUiThreadState } from "./agUiThreadState.
|
|
|
3
3
|
import { readMcpAppResourceUri, toolContentText } from "./mcp-tool-result.js";
|
|
4
4
|
import { AG_UI_METADATA_NAMESPACE } from "./types.js";
|
|
5
5
|
import { buildToolApprovalResume, preserveToolApprovalDecision, projectAgUiToolApprovals, withSettledToolApprovals, withToolApprovalDecision } from "./adapter/tool-approval.js";
|
|
6
|
+
import { fromAgUiSnapshotMessages, toAgUiMessages, toAgUiTools, withAgUiPartMessageMetadata } from "./adapter/conversions.js";
|
|
6
7
|
import { RunAggregator, isPlainObject, tryParseJSON } from "./adapter/run-aggregator.js";
|
|
7
|
-
import { fromAgUiMessages, toAgUiMessages, toAgUiTools } from "./adapter/conversions.js";
|
|
8
8
|
import { createAgUiSubscriber } from "./adapter/subscriber.js";
|
|
9
9
|
import { createReplayContentMerger, groupReplayToolContent, indexReplayToolOwners, preserveToolResult } from "./adapter/replay-content.js";
|
|
10
10
|
import { fromThreadMessageLike, generateId, isMcpAppUri } from "@assistant-ui/core";
|
|
@@ -57,6 +57,42 @@ const FALLBACK_USER_STATUS = {
|
|
|
57
57
|
type: "complete",
|
|
58
58
|
reason: "unknown"
|
|
59
59
|
};
|
|
60
|
+
const textOf = (content) => content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
|
|
61
|
+
/**
|
|
62
|
+
* Swaps `content`'s text parts, in order, for `source`'s. A text part `source`
|
|
63
|
+
* has beyond `content`'s text slots goes after the nearest earlier part of
|
|
64
|
+
* `source` that `content` also has: a tool call by id, another part by its
|
|
65
|
+
* type and position among parts of that type. With none, it goes first.
|
|
66
|
+
*/
|
|
67
|
+
const replaceTextParts = (content, source) => {
|
|
68
|
+
const texts = source.filter((part) => part.type === "text");
|
|
69
|
+
const result = [];
|
|
70
|
+
let used = 0;
|
|
71
|
+
for (const part of content) if (part.type !== "text") result.push(part);
|
|
72
|
+
else if (used < texts.length) result.push(texts[used++]);
|
|
73
|
+
const positionIn = (parts, part) => parts.filter((other) => other.type === part.type).indexOf(part);
|
|
74
|
+
for (const text of texts.slice(used)) {
|
|
75
|
+
let anchor = -1;
|
|
76
|
+
for (let index = source.indexOf(text) - 1; index >= 0 && anchor < 0; index--) {
|
|
77
|
+
const before = source[index];
|
|
78
|
+
if (before.type === "tool-call") anchor = result.findIndex((part) => part.type === "tool-call" && part.toolCallId === before.toolCallId);
|
|
79
|
+
else if (before.type === "text") anchor = result.indexOf(before);
|
|
80
|
+
else {
|
|
81
|
+
const match = result.filter((part) => part.type === before.type)[positionIn(source, before)];
|
|
82
|
+
anchor = match ? result.indexOf(match) : -1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
result.splice(anchor + 1, 0, text);
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
};
|
|
89
|
+
const settleNestedContent = (content, status) => mapToolCallPartsDeep(content, (part) => part.messages ? {
|
|
90
|
+
...part,
|
|
91
|
+
messages: part.messages.map((message) => message.role === "assistant" && message.status?.type === "running" ? {
|
|
92
|
+
...message,
|
|
93
|
+
status
|
|
94
|
+
} : message)
|
|
95
|
+
} : part).content;
|
|
60
96
|
const invokeRuntimeCallback = (name, callback, ...args) => {
|
|
61
97
|
invokeUserCallback("react-ag-ui", name, callback, ...args);
|
|
62
98
|
};
|
|
@@ -107,6 +143,7 @@ var AgUiThreadController = class {
|
|
|
107
143
|
activeRunAgent = null;
|
|
108
144
|
history;
|
|
109
145
|
assistantHistoryParents = /* @__PURE__ */ new Map();
|
|
146
|
+
subagentParentToolCallIds = /* @__PURE__ */ new Map();
|
|
110
147
|
snapshotHistoryIds = /* @__PURE__ */ new Set();
|
|
111
148
|
persistedHistoryIds = /* @__PURE__ */ new Set();
|
|
112
149
|
historyWrites = createHistoryWriteChain();
|
|
@@ -188,6 +225,7 @@ var AgUiThreadController = class {
|
|
|
188
225
|
if (!repo) return;
|
|
189
226
|
this.session.applyExternalMessageRepository(repo);
|
|
190
227
|
this.assistantHistoryParents.clear();
|
|
228
|
+
this.subagentParentToolCallIds.clear();
|
|
191
229
|
this.snapshotHistoryIds.clear();
|
|
192
230
|
this.persistedHistoryIds.clear();
|
|
193
231
|
for (const { message } of repo.messages) this.persistedHistoryIds.add(message.id);
|
|
@@ -555,7 +593,10 @@ var AgUiThreadController = class {
|
|
|
555
593
|
custom: newCustom
|
|
556
594
|
}
|
|
557
595
|
};
|
|
558
|
-
}))
|
|
596
|
+
})) {
|
|
597
|
+
this.publishUpdate();
|
|
598
|
+
this.persistSettledAssistant(messageId);
|
|
599
|
+
}
|
|
559
600
|
}
|
|
560
601
|
findMessageIdForToolCall(toolCallId) {
|
|
561
602
|
let fallbackMessageId;
|
|
@@ -609,9 +650,31 @@ var AgUiThreadController = class {
|
|
|
609
650
|
};
|
|
610
651
|
});
|
|
611
652
|
if (!matchedToolCall) return message;
|
|
653
|
+
const settled = mapToolCallPartsDeep(content, (part) => {
|
|
654
|
+
if (!part.messages) return part;
|
|
655
|
+
let changed = false;
|
|
656
|
+
const messages = part.messages.map((nested) => {
|
|
657
|
+
if (nested.role !== "assistant" || nested.status.type !== "requires-action" || nested.status.reason !== "tool-calls") return nested;
|
|
658
|
+
const listed = listedPendingToolCallIds(nested);
|
|
659
|
+
if (Array.from(iterateToolCallParts(nested.content)).some((call) => !isResolvedToolCall(call) && (!listed || listed.has(call.toolCallId)))) return nested;
|
|
660
|
+
changed = true;
|
|
661
|
+
return {
|
|
662
|
+
...nested,
|
|
663
|
+
status: {
|
|
664
|
+
type: "complete",
|
|
665
|
+
reason: "unknown"
|
|
666
|
+
},
|
|
667
|
+
metadata: withoutPendingToolCallIds(nested.metadata)
|
|
668
|
+
};
|
|
669
|
+
});
|
|
670
|
+
return changed ? {
|
|
671
|
+
...part,
|
|
672
|
+
messages
|
|
673
|
+
} : part;
|
|
674
|
+
}).content;
|
|
612
675
|
return {
|
|
613
676
|
...assistant,
|
|
614
|
-
content
|
|
677
|
+
content: settled
|
|
615
678
|
};
|
|
616
679
|
})) return;
|
|
617
680
|
this.publishUpdate();
|
|
@@ -724,7 +787,8 @@ var AgUiThreadController = class {
|
|
|
724
787
|
metadata: withoutPendingToolCallIds(current.metadata)
|
|
725
788
|
} : current)) return false;
|
|
726
789
|
this.publishUpdate();
|
|
727
|
-
this.
|
|
790
|
+
if (this.history?.update) this.persistSettledAssistant(messageId);
|
|
791
|
+
else this.persistAssistantHistory(messageId);
|
|
728
792
|
return true;
|
|
729
793
|
}
|
|
730
794
|
startResumeRun(messageId) {
|
|
@@ -827,6 +891,7 @@ var AgUiThreadController = class {
|
|
|
827
891
|
const controller = this.abortController;
|
|
828
892
|
const activeRunAgent = this.activeRunAgent;
|
|
829
893
|
this.dispatch({ type: "resetThread" });
|
|
894
|
+
this.subagentParentToolCallIds.clear();
|
|
830
895
|
if (controller) {
|
|
831
896
|
this.abortController = null;
|
|
832
897
|
this.activeRunAgent = null;
|
|
@@ -892,6 +957,8 @@ var AgUiThreadController = class {
|
|
|
892
957
|
let frameOwnerIds = /* @__PURE__ */ new Set();
|
|
893
958
|
let restrictedPendingToolCallIds;
|
|
894
959
|
let settlingRun = false;
|
|
960
|
+
let latestSnapshot;
|
|
961
|
+
let snapshotToolCallIds = /* @__PURE__ */ new Set();
|
|
895
962
|
const ensureAssistant = (allowRecreate = false) => {
|
|
896
963
|
const cached = assistantMessageId;
|
|
897
964
|
if (cached !== void 0 && this.session.tryGetMessage(cached)) return cached;
|
|
@@ -1097,7 +1164,45 @@ var AgUiThreadController = class {
|
|
|
1097
1164
|
showThinking: this.showThinking,
|
|
1098
1165
|
joinStrategy,
|
|
1099
1166
|
logger: this.logger,
|
|
1100
|
-
emit:
|
|
1167
|
+
emit: (update) => {
|
|
1168
|
+
const currentSnapshotAssistant = assistantMessageId === void 0 ? void 0 : this.session.tryGetMessage(assistantMessageId)?.message;
|
|
1169
|
+
const listedSnapshotToolCallIds = currentSnapshotAssistant?.role === "assistant" ? listedPendingToolCallIds(currentSnapshotAssistant) : void 0;
|
|
1170
|
+
const hasPendingSnapshotToolCall = latestSnapshot?.id === assistantMessageId && currentSnapshotAssistant?.role === "assistant" && Array.from(iterateToolCallParts(currentSnapshotAssistant.content)).some((part) => !isResolvedToolCall(part) && (!listedSnapshotToolCallIds || listedSnapshotToolCallIds.has(part.toolCallId)) && (!restrictedPendingToolCallIds || restrictedPendingToolCallIds.has(part.toolCallId)));
|
|
1171
|
+
const reconciledUpdate = settlingRun && update.status?.type === "complete" && hasPendingSnapshotToolCall ? {
|
|
1172
|
+
...update,
|
|
1173
|
+
status: {
|
|
1174
|
+
type: "requires-action",
|
|
1175
|
+
reason: "tool-calls"
|
|
1176
|
+
},
|
|
1177
|
+
...restrictedPendingToolCallIds ? { metadata: {
|
|
1178
|
+
...update.metadata,
|
|
1179
|
+
custom: {
|
|
1180
|
+
...update.metadata?.custom,
|
|
1181
|
+
[AG_UI_METADATA_NAMESPACE]: {
|
|
1182
|
+
...update.metadata?.custom?.[AG_UI_METADATA_NAMESPACE],
|
|
1183
|
+
pendingToolCallIds: [...restrictedPendingToolCallIds]
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
} } : {}
|
|
1187
|
+
} : update;
|
|
1188
|
+
if (settlingRun && latestSnapshot && latestSnapshot.id === assistantMessageId && reconciledUpdate.status && reconciledUpdate.status.type !== "running" && reconciledUpdate.status.type !== "requires-action") {
|
|
1189
|
+
const status = reconciledUpdate.status;
|
|
1190
|
+
const current = this.session.tryGetMessage(assistantMessageId)?.message;
|
|
1191
|
+
const content = settleNestedContent(current?.role === "assistant" ? current.content : latestSnapshot.content, status);
|
|
1192
|
+
applyUpdate({
|
|
1193
|
+
...reconciledUpdate,
|
|
1194
|
+
content
|
|
1195
|
+
});
|
|
1196
|
+
} else if (latestSnapshot && latestSnapshot.id === assistantMessageId && reconciledUpdate.content !== void 0) {
|
|
1197
|
+
const content = this.mergeSnapshotAssistantContent(latestSnapshot.content, reconciledUpdate.content, reconciledUpdate.content.some((part) => part.type === "reasoning"), reconciledUpdate.content.some(isActivityPart), snapshotToolCallIds);
|
|
1198
|
+
applyUpdate({
|
|
1199
|
+
...reconciledUpdate,
|
|
1200
|
+
content
|
|
1201
|
+
});
|
|
1202
|
+
} else applyUpdate(reconciledUpdate);
|
|
1203
|
+
const current = assistantMessageId ? this.session.tryGetMessage(assistantMessageId)?.message : void 0;
|
|
1204
|
+
latestSnapshot = !settlingRun && current?.role === "assistant" && current.id === latestSnapshot?.id ? current : void 0;
|
|
1205
|
+
},
|
|
1101
1206
|
onServerMessageId: (serverId) => {
|
|
1102
1207
|
adoptServerMessageId(serverId, false);
|
|
1103
1208
|
},
|
|
@@ -1147,6 +1252,10 @@ var AgUiThreadController = class {
|
|
|
1147
1252
|
}
|
|
1148
1253
|
}
|
|
1149
1254
|
assistantMessageId = nextAssistantMessageId;
|
|
1255
|
+
const imported = assistantMessageId ? this.session.tryGetMessage(assistantMessageId)?.message : void 0;
|
|
1256
|
+
const source = event.messages.find((message) => message?.role === "assistant" && message.id === imported?.id);
|
|
1257
|
+
latestSnapshot = imported?.role === "assistant" && source ? imported : void 0;
|
|
1258
|
+
snapshotToolCallIds = new Set(Array.isArray(source?.toolCalls) ? source.toolCalls.map((call) => call.id).filter((id) => typeof id === "string") : []);
|
|
1150
1259
|
if (!replayEnded && assistantMessageId === void 0) {
|
|
1151
1260
|
const tail = this.session.getMessages().at(-1);
|
|
1152
1261
|
snapshotAssistantId = tail?.role === "assistant" ? tail.id : void 0;
|
|
@@ -1194,7 +1303,6 @@ var AgUiThreadController = class {
|
|
|
1194
1303
|
const input = this.buildRunInput(runId, normalizedRunConfig, historicalMessages, resume);
|
|
1195
1304
|
const subscriber = createAgUiSubscriber({
|
|
1196
1305
|
dispatch,
|
|
1197
|
-
runId,
|
|
1198
1306
|
logger: this.logger,
|
|
1199
1307
|
onRunFailed: (error) => {
|
|
1200
1308
|
if (abortSignal.aborted) return;
|
|
@@ -1205,7 +1313,7 @@ var AgUiThreadController = class {
|
|
|
1205
1313
|
try {
|
|
1206
1314
|
runAgentInstance.messages = input.messages;
|
|
1207
1315
|
runAgentInstance.threadId = input.threadId;
|
|
1208
|
-
runAgentInstance.state = input.state
|
|
1316
|
+
runAgentInstance.state = input.state;
|
|
1209
1317
|
} catch {}
|
|
1210
1318
|
await runAgentInstance.runAgent.bind(runAgentInstance)(input, subscriber, { signal: abortSignal });
|
|
1211
1319
|
}
|
|
@@ -1220,6 +1328,19 @@ var AgUiThreadController = class {
|
|
|
1220
1328
|
pendingError = err;
|
|
1221
1329
|
}
|
|
1222
1330
|
} finally {
|
|
1331
|
+
const current = assistantMessageId ? this.session.tryGetMessage(assistantMessageId)?.message : void 0;
|
|
1332
|
+
if (this.abortController === abortController && current?.role === "assistant" && current.status?.type === "running") {
|
|
1333
|
+
const status = {
|
|
1334
|
+
type: "incomplete",
|
|
1335
|
+
reason: "error",
|
|
1336
|
+
error: "The connection ended before the answer finished."
|
|
1337
|
+
};
|
|
1338
|
+
settleHeldMessages(status);
|
|
1339
|
+
applyUpdate({
|
|
1340
|
+
status,
|
|
1341
|
+
content: settleNestedContent(current.content, status)
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1223
1344
|
this.finishRun(abortController);
|
|
1224
1345
|
}
|
|
1225
1346
|
if (pendingError) {
|
|
@@ -1342,7 +1463,7 @@ var AgUiThreadController = class {
|
|
|
1342
1463
|
return {
|
|
1343
1464
|
threadId,
|
|
1344
1465
|
runId,
|
|
1345
|
-
state: this.state.stateSnapshot
|
|
1466
|
+
state: this.state.stateSnapshot === void 0 ? {} : this.state.stateSnapshot,
|
|
1346
1467
|
messages,
|
|
1347
1468
|
tools: toAgUiTools(context?.tools),
|
|
1348
1469
|
context: [...context?.system ? [{
|
|
@@ -1535,10 +1656,10 @@ var AgUiThreadController = class {
|
|
|
1535
1656
|
});
|
|
1536
1657
|
return changed ? content : next;
|
|
1537
1658
|
}
|
|
1538
|
-
mergeSnapshotAssistantContent(previous, next, snapshotHasReasoning, snapshotHasActivity) {
|
|
1539
|
-
const shouldKeep = (part) => part.type === "reasoning" ? this.showThinking && !snapshotHasReasoning : isActivityPart(part) && !snapshotHasActivity;
|
|
1659
|
+
mergeSnapshotAssistantContent(previous, next, snapshotHasReasoning, snapshotHasActivity, keepMissingToolCalls = /* @__PURE__ */ new Set(), keepPriorTranscripts = false) {
|
|
1660
|
+
const shouldKeep = (part) => part.type === "tool-call" ? keepMissingToolCalls.has(part.toolCallId) && !next.some((incoming) => incoming.type === "tool-call" && incoming.toolCallId === part.toolCallId) : part.type === "reasoning" ? this.showThinking && !snapshotHasReasoning : part.type === "data" && !isActivityPart(part) ? !next.some((incoming) => incoming.type === "data" && incoming.name === part.name) : isActivityPart(part) && !snapshotHasActivity;
|
|
1540
1661
|
const kept = previous.filter(shouldKeep);
|
|
1541
|
-
const merged = this.reconcileSnapshotNestedContent(previous, this.preserveToolInteractions(previous, next));
|
|
1662
|
+
const merged = this.reconcileSnapshotNestedContent(previous, this.preserveToolInteractions(previous, next), keepPriorTranscripts);
|
|
1542
1663
|
if (kept.length === 0) return merged;
|
|
1543
1664
|
const mergedByType = /* @__PURE__ */ new Map();
|
|
1544
1665
|
const mergedTools = /* @__PURE__ */ new Map();
|
|
@@ -1570,7 +1691,7 @@ var AgUiThreadController = class {
|
|
|
1570
1691
|
result.push(...insertions[merged.length]);
|
|
1571
1692
|
return result;
|
|
1572
1693
|
}
|
|
1573
|
-
reconcileSnapshotNestedContent(previous, next) {
|
|
1694
|
+
reconcileSnapshotNestedContent(previous, next, keepPriorTranscripts) {
|
|
1574
1695
|
const nestedIds = /* @__PURE__ */ new Set();
|
|
1575
1696
|
for (const part of previous) {
|
|
1576
1697
|
if (part.type !== "tool-call" || !part.messages) continue;
|
|
@@ -1600,11 +1721,49 @@ var AgUiThreadController = class {
|
|
|
1600
1721
|
if (part.type === "tool-call" && flattened.has(part.toolCallId)) return [];
|
|
1601
1722
|
if (part.type !== "tool-call") return [part];
|
|
1602
1723
|
const prior = previousCalls.get(part.toolCallId);
|
|
1603
|
-
|
|
1724
|
+
if (prior?.messages === void 0) return [part];
|
|
1725
|
+
if (part.messages === void 0) return [{
|
|
1604
1726
|
...part,
|
|
1605
1727
|
messages: prior.messages
|
|
1606
1728
|
}];
|
|
1729
|
+
if (part.messages.length === 0) return [part];
|
|
1730
|
+
return [{
|
|
1731
|
+
...part,
|
|
1732
|
+
messages: this.mergeNestedTranscripts(prior.messages, part.messages, keepPriorTranscripts)
|
|
1733
|
+
}];
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
/**
|
|
1737
|
+
* Merges one tool call's nested transcripts message by message. The incoming
|
|
1738
|
+
* copy is the base for tool calls and reasoning; resolved results and calls
|
|
1739
|
+
* only the prior copy has carry over, and the prior text wins when it extends
|
|
1740
|
+
* the incoming text. Importing a snapshot keeps prior messages it lacks, a
|
|
1741
|
+
* prior status that is running or incomplete, and the prior status when the
|
|
1742
|
+
* snapshot waits on calls the merged content has already resolved.
|
|
1743
|
+
*/
|
|
1744
|
+
mergeNestedTranscripts(prior, next, importingSnapshot) {
|
|
1745
|
+
const priorById = new Map(prior.map((message) => [message.id, message]));
|
|
1746
|
+
const merged = next.map((message) => {
|
|
1747
|
+
const previous = priorById.get(message.id);
|
|
1748
|
+
if (previous?.role !== "assistant" || message.role !== "assistant") return message;
|
|
1749
|
+
let content = this.mergeSnapshotAssistantContent(previous.content, this.preserveToolResults(previous.content, message.content), message.content.some((part) => part.type === "reasoning"), message.content.some(isActivityPart), new Set(Array.from(iterateToolCallParts(previous.content), (call) => call.toolCallId)), importingSnapshot);
|
|
1750
|
+
const priorText = textOf(previous.content);
|
|
1751
|
+
const incomingText = textOf(message.content);
|
|
1752
|
+
if (priorText.length > incomingText.length && priorText.startsWith(incomingText)) content = replaceTextParts(content, previous.content);
|
|
1753
|
+
const status = !importingSnapshot ? message.status : previous.status.type === "running" || previous.status.type === "incomplete" || message.status.type === "requires-action" && message.status.reason === "tool-calls" && !Array.from(iterateToolCallParts(content)).some((call) => !isResolvedToolCall(call)) ? previous.status : message.status;
|
|
1754
|
+
return {
|
|
1755
|
+
...message,
|
|
1756
|
+
status,
|
|
1757
|
+
content,
|
|
1758
|
+
metadata: {
|
|
1759
|
+
...message.metadata,
|
|
1760
|
+
custom: mergeAgUiNamespace(previous.metadata.custom, message.metadata.custom)
|
|
1761
|
+
}
|
|
1762
|
+
};
|
|
1607
1763
|
});
|
|
1764
|
+
if (!importingSnapshot) return merged;
|
|
1765
|
+
const mergedById = new Map(merged.map((message) => [message.id, message]));
|
|
1766
|
+
return [...prior.map((message) => mergedById.get(message.id) ?? message), ...merged.filter((message) => !priorById.has(message.id))];
|
|
1608
1767
|
}
|
|
1609
1768
|
mergeAssistantMetadata(current, incoming) {
|
|
1610
1769
|
const annotations = incoming.unstable_annotations ? [...current.unstable_annotations, ...incoming.unstable_annotations] : current.unstable_annotations;
|
|
@@ -1621,6 +1780,7 @@ var AgUiThreadController = class {
|
|
|
1621
1780
|
};
|
|
1622
1781
|
}
|
|
1623
1782
|
handleEvent(aggregator, event, activeAssistantId, joinStrategy, runStartMessageIds, hasRunUserBoundary, reconcileSnapshot) {
|
|
1783
|
+
if (event.type === "SUBAGENT_STARTED" && !this.subagentParentToolCallIds.has(event.subagentRunId)) this.subagentParentToolCallIds.set(event.subagentRunId, event.parentToolCallId);
|
|
1624
1784
|
switch (event.type) {
|
|
1625
1785
|
case "STATE_SNAPSHOT":
|
|
1626
1786
|
this.dispatch({
|
|
@@ -1643,12 +1803,26 @@ var AgUiThreadController = class {
|
|
|
1643
1803
|
}
|
|
1644
1804
|
break;
|
|
1645
1805
|
case "MESSAGES_SNAPSHOT": {
|
|
1806
|
+
aggregator.handle(event);
|
|
1646
1807
|
const previousHeadId = this.session.headId;
|
|
1647
1808
|
const activeAssistant = activeAssistantId === void 0 ? void 0 : this.session.tryGetMessage(activeAssistantId)?.message;
|
|
1648
1809
|
const hasActiveText = activeAssistant?.role === "assistant" && activeAssistant.content.some((part) => part.type === "text");
|
|
1649
1810
|
this.importMessagesSnapshot(event.messages, activeAssistantId, joinStrategy, runStartMessageIds, hasRunUserBoundary, reconcileSnapshot);
|
|
1650
1811
|
const headId = this.session.headId;
|
|
1651
|
-
|
|
1812
|
+
const head = headId === null ? void 0 : this.session.tryGetMessage(headId)?.message;
|
|
1813
|
+
const hasNewPendingToolCallAnswer = head?.role === "assistant" && !runStartMessageIds.has(head.id) && head.status?.type === "requires-action" && head.status.reason === "tool-calls";
|
|
1814
|
+
if (headId !== previousHeadId && (hasActiveText || hasNewPendingToolCallAnswer)) {
|
|
1815
|
+
const nextActiveAssistantId = head?.role === "assistant" ? headId ?? void 0 : void 0;
|
|
1816
|
+
if (activeAssistantId !== nextActiveAssistantId && activeAssistantId) {
|
|
1817
|
+
const previous = this.session.tryGetMessage(activeAssistantId)?.message;
|
|
1818
|
+
if (previous?.role === "assistant" && previous.status?.type === "running") this.updateAssistantMessage(activeAssistantId, {
|
|
1819
|
+
status: FALLBACK_USER_STATUS,
|
|
1820
|
+
content: settleNestedContent(previous.content, FALLBACK_USER_STATUS)
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
activeAssistantId = nextActiveAssistantId;
|
|
1824
|
+
if (hasNewPendingToolCallAnswer && head) this.updateAssistantMessage(head.id, { status: { type: "running" } });
|
|
1825
|
+
}
|
|
1652
1826
|
this.updateActiveAssistantState(activeAssistantId);
|
|
1653
1827
|
break;
|
|
1654
1828
|
}
|
|
@@ -1710,7 +1884,8 @@ var AgUiThreadController = class {
|
|
|
1710
1884
|
text: modelText
|
|
1711
1885
|
}] } : {},
|
|
1712
1886
|
...part.isError === void 0 ? typeof event.mcpResult?.isError === "boolean" ? { isError: event.mcpResult.isError } : event.role === "tool" ? { isError: false } : {} : {},
|
|
1713
|
-
...event.messageId ? { unstable_toolMessageId: event.messageId } : {}
|
|
1887
|
+
...event.messageId ? { unstable_toolMessageId: event.messageId } : {},
|
|
1888
|
+
...event.metadata !== void 0 ? { providerMetadata: withAgUiPartMessageMetadata(part.providerMetadata, event.metadata) } : {}
|
|
1714
1889
|
};
|
|
1715
1890
|
const { modelContent: _modelContent, ...partWithoutModelContent } = part;
|
|
1716
1891
|
return {
|
|
@@ -1722,7 +1897,8 @@ var AgUiThreadController = class {
|
|
|
1722
1897
|
}] } : {},
|
|
1723
1898
|
...typeof event.mcpResult?.isError === "boolean" ? { isError: event.mcpResult.isError } : event.role === "tool" ? { isError: false } : {},
|
|
1724
1899
|
...part.mcp === void 0 && mcpAppUri !== void 0 ? { mcp: { app: { resourceUri: mcpAppUri } } } : {},
|
|
1725
|
-
...event.messageId ? { unstable_toolMessageId: event.messageId } : {}
|
|
1900
|
+
...event.messageId ? { unstable_toolMessageId: event.messageId } : {},
|
|
1901
|
+
...event.metadata !== void 0 ? { providerMetadata: withAgUiPartMessageMetadata(part.providerMetadata, event.metadata) } : {}
|
|
1726
1902
|
};
|
|
1727
1903
|
});
|
|
1728
1904
|
if (!matchedToolCall) return message;
|
|
@@ -1732,7 +1908,8 @@ var AgUiThreadController = class {
|
|
|
1732
1908
|
};
|
|
1733
1909
|
})) return;
|
|
1734
1910
|
this.publishUpdate();
|
|
1735
|
-
this.maybeCompleteAfterToolResults(messageId);
|
|
1911
|
+
if (this.maybeCompleteAfterToolResults(messageId)) return;
|
|
1912
|
+
this.persistSettledAssistant(messageId);
|
|
1736
1913
|
}
|
|
1737
1914
|
applyCrossRunActivitySnapshot(messageId, toolCallId, event) {
|
|
1738
1915
|
const resourceUri = event.content["resourceUri"];
|
|
@@ -1771,7 +1948,8 @@ var AgUiThreadController = class {
|
|
|
1771
1948
|
};
|
|
1772
1949
|
})) return;
|
|
1773
1950
|
this.publishUpdate();
|
|
1774
|
-
this.maybeCompleteAfterToolResults(messageId);
|
|
1951
|
+
if (this.maybeCompleteAfterToolResults(messageId)) return;
|
|
1952
|
+
this.persistSettledAssistant(messageId);
|
|
1775
1953
|
}
|
|
1776
1954
|
importMessagesSnapshot(rawMessages, activeAssistantId, joinStrategy, runStartMessageIds, hasRunUserBoundary, reconcileSnapshot) {
|
|
1777
1955
|
try {
|
|
@@ -1779,10 +1957,12 @@ var AgUiThreadController = class {
|
|
|
1779
1957
|
const activeMessage = activeAssistantItem?.message;
|
|
1780
1958
|
const activeAssistantParentId = activeAssistantItem?.parentId;
|
|
1781
1959
|
const activeAssistant = activeMessage?.role === "assistant" ? activeMessage : void 0;
|
|
1782
|
-
const normalized =
|
|
1960
|
+
const normalized = fromAgUiSnapshotMessages(rawMessages, { showThinking: this.showThinking }, this.subagentParentToolCallIds);
|
|
1783
1961
|
const converted = [];
|
|
1784
1962
|
for (const message of normalized) try {
|
|
1785
|
-
|
|
1963
|
+
const previous = message.id ? this.session.tryGetMessage(message.id)?.message : void 0;
|
|
1964
|
+
const fallbackStatus = previous?.role === "assistant" && previous.status?.type === "running" ? previous.status : FALLBACK_USER_STATUS;
|
|
1965
|
+
converted.push(fromThreadMessageLike(message, generateId(), fallbackStatus));
|
|
1786
1966
|
} catch (error) {
|
|
1787
1967
|
this.logger.error?.("[agui] failed to import message from snapshot", error);
|
|
1788
1968
|
}
|
|
@@ -1794,9 +1974,11 @@ var AgUiThreadController = class {
|
|
|
1794
1974
|
if (convertedMessage.role === "assistant") {
|
|
1795
1975
|
const previous = existing?.role === "assistant" ? existing : void 0;
|
|
1796
1976
|
const snapshot = reconcileSnapshot?.(convertedMessage, previous) ?? convertedMessage;
|
|
1977
|
+
const preservesRunningStatus = convertedMessage.id === activeAssistantId && previous?.status?.type === "running" && snapshot.status?.type === "requires-action" && snapshot.status.reason === "tool-calls";
|
|
1797
1978
|
converted[index] = previous ? {
|
|
1798
1979
|
...snapshot,
|
|
1799
|
-
|
|
1980
|
+
...preservesRunningStatus ? { status: previous.status } : {},
|
|
1981
|
+
content: this.mergeSnapshotAssistantContent(previous.content, snapshot.content, snapshotHasReasoning, snapshotHasActivity, /* @__PURE__ */ new Set(), true)
|
|
1800
1982
|
} : snapshot;
|
|
1801
1983
|
}
|
|
1802
1984
|
}
|
|
@@ -1881,6 +2063,14 @@ var AgUiThreadController = class {
|
|
|
1881
2063
|
if (!this.history) return;
|
|
1882
2064
|
this.assistantHistoryParents.set(messageId, parentId);
|
|
1883
2065
|
}
|
|
2066
|
+
persistSettledAssistant(messageId) {
|
|
2067
|
+
const item = this.session.tryGetMessage(messageId);
|
|
2068
|
+
if (!this.history?.update || !item) return;
|
|
2069
|
+
if (!this.isTerminalStatus(item.message.status)) return;
|
|
2070
|
+
if (!this.persistedHistoryIds.has(messageId) && this.historyWrites.pending(messageId) === void 0) return;
|
|
2071
|
+
this.markPendingAssistantHistory(messageId, item.parentId);
|
|
2072
|
+
this.persistAssistantHistory(messageId);
|
|
2073
|
+
}
|
|
1884
2074
|
persistAssistantHistory(messageId) {
|
|
1885
2075
|
const history = this.history;
|
|
1886
2076
|
if (!history) return;
|
|
@@ -1891,7 +2081,7 @@ var AgUiThreadController = class {
|
|
|
1891
2081
|
if (!this.isPersistableStatus(message.status)) return;
|
|
1892
2082
|
const wasPersisted = this.persistedHistoryIds.has(messageId);
|
|
1893
2083
|
const update = history.update;
|
|
1894
|
-
if (update !== void 0 && (wasPersisted || this.snapshotHistoryIds.has(messageId))) {
|
|
2084
|
+
if (update !== void 0 && (wasPersisted || this.snapshotHistoryIds.has(messageId) || this.historyWrites.pending(messageId) !== void 0)) {
|
|
1895
2085
|
const write = this.historyWrites.chain(messageId, () => update.call(history, {
|
|
1896
2086
|
parentId,
|
|
1897
2087
|
message
|