@assistant-ui/react-ag-ui 0.1.1 → 0.1.3
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 +242 -25
- 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 +2 -0
- package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
- package/dist/runtime/adapter/run-aggregator.js +42 -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 +6 -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 +346 -0
- package/src/runtime/AgUiThreadController.subagent-snapshot.test.ts +854 -0
- package/src/runtime/AgUiThreadController.test.ts +173 -1
- package/src/runtime/AgUiThreadController.ts +528 -33
- 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 +63 -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/src/useAgUiRuntime.cloud-run-leases.test.tsx +384 -0
- package/src/useAgUiRuntime.state.test.tsx +87 -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,14 +3,22 @@ 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";
|
|
11
11
|
import { appendToolInteraction, createHistoryWriteChain, createMessageRepositorySession, invokeUserCallback, iterateToolCallParts, mapToolCallPartsDeep } from "@assistant-ui/core/internal";
|
|
12
12
|
import jsonpatch from "fast-json-patch";
|
|
13
13
|
// src/runtime/AgUiThreadController.ts
|
|
14
|
+
const agentInitialStates = /* @__PURE__ */ new WeakMap();
|
|
15
|
+
const getAgentInitialState = (agent) => {
|
|
16
|
+
if (!agentInitialStates.has(agent)) {
|
|
17
|
+
const state = agent.state;
|
|
18
|
+
agentInitialStates.set(agent, state === void 0 || isPlainObject(state) && Object.keys(state).length === 0 ? void 0 : structuredClone(state));
|
|
19
|
+
}
|
|
20
|
+
return agentInitialStates.get(agent);
|
|
21
|
+
};
|
|
14
22
|
const optimisticPrefix = "__optimistic__";
|
|
15
23
|
const generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
|
|
16
24
|
const isOptimisticId = (id) => id.startsWith(optimisticPrefix);
|
|
@@ -57,6 +65,42 @@ const FALLBACK_USER_STATUS = {
|
|
|
57
65
|
type: "complete",
|
|
58
66
|
reason: "unknown"
|
|
59
67
|
};
|
|
68
|
+
const textOf = (content) => content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
|
|
69
|
+
/**
|
|
70
|
+
* Swaps `content`'s text parts, in order, for `source`'s. A text part `source`
|
|
71
|
+
* has beyond `content`'s text slots goes after the nearest earlier part of
|
|
72
|
+
* `source` that `content` also has: a tool call by id, another part by its
|
|
73
|
+
* type and position among parts of that type. With none, it goes first.
|
|
74
|
+
*/
|
|
75
|
+
const replaceTextParts = (content, source) => {
|
|
76
|
+
const texts = source.filter((part) => part.type === "text");
|
|
77
|
+
const result = [];
|
|
78
|
+
let used = 0;
|
|
79
|
+
for (const part of content) if (part.type !== "text") result.push(part);
|
|
80
|
+
else if (used < texts.length) result.push(texts[used++]);
|
|
81
|
+
const positionIn = (parts, part) => parts.filter((other) => other.type === part.type).indexOf(part);
|
|
82
|
+
for (const text of texts.slice(used)) {
|
|
83
|
+
let anchor = -1;
|
|
84
|
+
for (let index = source.indexOf(text) - 1; index >= 0 && anchor < 0; index--) {
|
|
85
|
+
const before = source[index];
|
|
86
|
+
if (before.type === "tool-call") anchor = result.findIndex((part) => part.type === "tool-call" && part.toolCallId === before.toolCallId);
|
|
87
|
+
else if (before.type === "text") anchor = result.indexOf(before);
|
|
88
|
+
else {
|
|
89
|
+
const match = result.filter((part) => part.type === before.type)[positionIn(source, before)];
|
|
90
|
+
anchor = match ? result.indexOf(match) : -1;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
result.splice(anchor + 1, 0, text);
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
};
|
|
97
|
+
const settleNestedContent = (content, status) => mapToolCallPartsDeep(content, (part) => part.messages ? {
|
|
98
|
+
...part,
|
|
99
|
+
messages: part.messages.map((message) => message.role === "assistant" && message.status?.type === "running" ? {
|
|
100
|
+
...message,
|
|
101
|
+
status
|
|
102
|
+
} : message)
|
|
103
|
+
} : part).content;
|
|
60
104
|
const invokeRuntimeCallback = (name, callback, ...args) => {
|
|
61
105
|
invokeUserCallback("react-ag-ui", name, callback, ...args);
|
|
62
106
|
};
|
|
@@ -107,6 +151,7 @@ var AgUiThreadController = class {
|
|
|
107
151
|
activeRunAgent = null;
|
|
108
152
|
history;
|
|
109
153
|
assistantHistoryParents = /* @__PURE__ */ new Map();
|
|
154
|
+
subagentParentToolCallIds = /* @__PURE__ */ new Map();
|
|
110
155
|
snapshotHistoryIds = /* @__PURE__ */ new Set();
|
|
111
156
|
persistedHistoryIds = /* @__PURE__ */ new Set();
|
|
112
157
|
historyWrites = createHistoryWriteChain();
|
|
@@ -114,6 +159,11 @@ var AgUiThreadController = class {
|
|
|
114
159
|
_loadPromise;
|
|
115
160
|
constructor(options) {
|
|
116
161
|
this.agent = options.agent;
|
|
162
|
+
const initialState = getAgentInitialState(this.agent);
|
|
163
|
+
if (initialState !== void 0) this.dispatch({
|
|
164
|
+
type: "stateSnapshot",
|
|
165
|
+
snapshot: initialState
|
|
166
|
+
});
|
|
117
167
|
this.logger = options.logger;
|
|
118
168
|
this.showThinking = options.showThinking;
|
|
119
169
|
this.joinStrategy = options.joinStrategy;
|
|
@@ -143,7 +193,18 @@ var AgUiThreadController = class {
|
|
|
143
193
|
for (const listener of this.listeners) listener();
|
|
144
194
|
}
|
|
145
195
|
updateOptions(options) {
|
|
196
|
+
const agentChanged = this.agent !== options.agent;
|
|
146
197
|
this.agent = options.agent;
|
|
198
|
+
if (agentChanged) {
|
|
199
|
+
const initialState = getAgentInitialState(this.agent);
|
|
200
|
+
if (this.state.stateSnapshot === void 0 && initialState !== void 0) {
|
|
201
|
+
this.dispatch({
|
|
202
|
+
type: "stateSnapshot",
|
|
203
|
+
snapshot: initialState
|
|
204
|
+
});
|
|
205
|
+
this.publishUpdate();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
147
208
|
this.logger = options.logger;
|
|
148
209
|
this.showThinking = options.showThinking;
|
|
149
210
|
this.joinStrategy = options.joinStrategy;
|
|
@@ -188,6 +249,7 @@ var AgUiThreadController = class {
|
|
|
188
249
|
if (!repo) return;
|
|
189
250
|
this.session.applyExternalMessageRepository(repo);
|
|
190
251
|
this.assistantHistoryParents.clear();
|
|
252
|
+
this.subagentParentToolCallIds.clear();
|
|
191
253
|
this.snapshotHistoryIds.clear();
|
|
192
254
|
this.persistedHistoryIds.clear();
|
|
193
255
|
for (const { message } of repo.messages) this.persistedHistoryIds.add(message.id);
|
|
@@ -555,7 +617,10 @@ var AgUiThreadController = class {
|
|
|
555
617
|
custom: newCustom
|
|
556
618
|
}
|
|
557
619
|
};
|
|
558
|
-
}))
|
|
620
|
+
})) {
|
|
621
|
+
this.publishUpdate();
|
|
622
|
+
this.persistSettledAssistant(messageId);
|
|
623
|
+
}
|
|
559
624
|
}
|
|
560
625
|
findMessageIdForToolCall(toolCallId) {
|
|
561
626
|
let fallbackMessageId;
|
|
@@ -609,9 +674,31 @@ var AgUiThreadController = class {
|
|
|
609
674
|
};
|
|
610
675
|
});
|
|
611
676
|
if (!matchedToolCall) return message;
|
|
677
|
+
const settled = mapToolCallPartsDeep(content, (part) => {
|
|
678
|
+
if (!part.messages) return part;
|
|
679
|
+
let changed = false;
|
|
680
|
+
const messages = part.messages.map((nested) => {
|
|
681
|
+
if (nested.role !== "assistant" || nested.status.type !== "requires-action" || nested.status.reason !== "tool-calls") return nested;
|
|
682
|
+
const listed = listedPendingToolCallIds(nested);
|
|
683
|
+
if (Array.from(iterateToolCallParts(nested.content)).some((call) => !isResolvedToolCall(call) && (!listed || listed.has(call.toolCallId)))) return nested;
|
|
684
|
+
changed = true;
|
|
685
|
+
return {
|
|
686
|
+
...nested,
|
|
687
|
+
status: {
|
|
688
|
+
type: "complete",
|
|
689
|
+
reason: "unknown"
|
|
690
|
+
},
|
|
691
|
+
metadata: withoutPendingToolCallIds(nested.metadata)
|
|
692
|
+
};
|
|
693
|
+
});
|
|
694
|
+
return changed ? {
|
|
695
|
+
...part,
|
|
696
|
+
messages
|
|
697
|
+
} : part;
|
|
698
|
+
}).content;
|
|
612
699
|
return {
|
|
613
700
|
...assistant,
|
|
614
|
-
content
|
|
701
|
+
content: settled
|
|
615
702
|
};
|
|
616
703
|
})) return;
|
|
617
704
|
this.publishUpdate();
|
|
@@ -724,7 +811,8 @@ var AgUiThreadController = class {
|
|
|
724
811
|
metadata: withoutPendingToolCallIds(current.metadata)
|
|
725
812
|
} : current)) return false;
|
|
726
813
|
this.publishUpdate();
|
|
727
|
-
this.
|
|
814
|
+
if (this.history?.update) this.persistSettledAssistant(messageId);
|
|
815
|
+
else this.persistAssistantHistory(messageId);
|
|
728
816
|
return true;
|
|
729
817
|
}
|
|
730
818
|
startResumeRun(messageId) {
|
|
@@ -827,6 +915,7 @@ var AgUiThreadController = class {
|
|
|
827
915
|
const controller = this.abortController;
|
|
828
916
|
const activeRunAgent = this.activeRunAgent;
|
|
829
917
|
this.dispatch({ type: "resetThread" });
|
|
918
|
+
this.subagentParentToolCallIds.clear();
|
|
830
919
|
if (controller) {
|
|
831
920
|
this.abortController = null;
|
|
832
921
|
this.activeRunAgent = null;
|
|
@@ -892,6 +981,8 @@ var AgUiThreadController = class {
|
|
|
892
981
|
let frameOwnerIds = /* @__PURE__ */ new Set();
|
|
893
982
|
let restrictedPendingToolCallIds;
|
|
894
983
|
let settlingRun = false;
|
|
984
|
+
let latestSnapshot;
|
|
985
|
+
let snapshotToolCallIds = /* @__PURE__ */ new Set();
|
|
895
986
|
const ensureAssistant = (allowRecreate = false) => {
|
|
896
987
|
const cached = assistantMessageId;
|
|
897
988
|
if (cached !== void 0 && this.session.tryGetMessage(cached)) return cached;
|
|
@@ -1097,7 +1188,45 @@ var AgUiThreadController = class {
|
|
|
1097
1188
|
showThinking: this.showThinking,
|
|
1098
1189
|
joinStrategy,
|
|
1099
1190
|
logger: this.logger,
|
|
1100
|
-
emit:
|
|
1191
|
+
emit: (update) => {
|
|
1192
|
+
const currentSnapshotAssistant = assistantMessageId === void 0 ? void 0 : this.session.tryGetMessage(assistantMessageId)?.message;
|
|
1193
|
+
const listedSnapshotToolCallIds = currentSnapshotAssistant?.role === "assistant" ? listedPendingToolCallIds(currentSnapshotAssistant) : void 0;
|
|
1194
|
+
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)));
|
|
1195
|
+
const reconciledUpdate = settlingRun && update.status?.type === "complete" && hasPendingSnapshotToolCall ? {
|
|
1196
|
+
...update,
|
|
1197
|
+
status: {
|
|
1198
|
+
type: "requires-action",
|
|
1199
|
+
reason: "tool-calls"
|
|
1200
|
+
},
|
|
1201
|
+
...restrictedPendingToolCallIds ? { metadata: {
|
|
1202
|
+
...update.metadata,
|
|
1203
|
+
custom: {
|
|
1204
|
+
...update.metadata?.custom,
|
|
1205
|
+
[AG_UI_METADATA_NAMESPACE]: {
|
|
1206
|
+
...update.metadata?.custom?.[AG_UI_METADATA_NAMESPACE],
|
|
1207
|
+
pendingToolCallIds: [...restrictedPendingToolCallIds]
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
} } : {}
|
|
1211
|
+
} : update;
|
|
1212
|
+
if (settlingRun && latestSnapshot && latestSnapshot.id === assistantMessageId && reconciledUpdate.status && reconciledUpdate.status.type !== "running" && reconciledUpdate.status.type !== "requires-action") {
|
|
1213
|
+
const status = reconciledUpdate.status;
|
|
1214
|
+
const current = this.session.tryGetMessage(assistantMessageId)?.message;
|
|
1215
|
+
const content = settleNestedContent(current?.role === "assistant" ? current.content : latestSnapshot.content, status);
|
|
1216
|
+
applyUpdate({
|
|
1217
|
+
...reconciledUpdate,
|
|
1218
|
+
content
|
|
1219
|
+
});
|
|
1220
|
+
} else if (latestSnapshot && latestSnapshot.id === assistantMessageId && reconciledUpdate.content !== void 0) {
|
|
1221
|
+
const content = this.mergeSnapshotAssistantContent(latestSnapshot.content, reconciledUpdate.content, reconciledUpdate.content.some((part) => part.type === "reasoning"), reconciledUpdate.content.some(isActivityPart), snapshotToolCallIds);
|
|
1222
|
+
applyUpdate({
|
|
1223
|
+
...reconciledUpdate,
|
|
1224
|
+
content
|
|
1225
|
+
});
|
|
1226
|
+
} else applyUpdate(reconciledUpdate);
|
|
1227
|
+
const current = assistantMessageId ? this.session.tryGetMessage(assistantMessageId)?.message : void 0;
|
|
1228
|
+
latestSnapshot = !settlingRun && current?.role === "assistant" && current.id === latestSnapshot?.id ? current : void 0;
|
|
1229
|
+
},
|
|
1101
1230
|
onServerMessageId: (serverId) => {
|
|
1102
1231
|
adoptServerMessageId(serverId, false);
|
|
1103
1232
|
},
|
|
@@ -1147,6 +1276,10 @@ var AgUiThreadController = class {
|
|
|
1147
1276
|
}
|
|
1148
1277
|
}
|
|
1149
1278
|
assistantMessageId = nextAssistantMessageId;
|
|
1279
|
+
const imported = assistantMessageId ? this.session.tryGetMessage(assistantMessageId)?.message : void 0;
|
|
1280
|
+
const source = event.messages.find((message) => message?.role === "assistant" && message.id === imported?.id);
|
|
1281
|
+
latestSnapshot = imported?.role === "assistant" && source ? imported : void 0;
|
|
1282
|
+
snapshotToolCallIds = new Set(Array.isArray(source?.toolCalls) ? source.toolCalls.map((call) => call.id).filter((id) => typeof id === "string") : []);
|
|
1150
1283
|
if (!replayEnded && assistantMessageId === void 0) {
|
|
1151
1284
|
const tail = this.session.getMessages().at(-1);
|
|
1152
1285
|
snapshotAssistantId = tail?.role === "assistant" ? tail.id : void 0;
|
|
@@ -1194,7 +1327,6 @@ var AgUiThreadController = class {
|
|
|
1194
1327
|
const input = this.buildRunInput(runId, normalizedRunConfig, historicalMessages, resume);
|
|
1195
1328
|
const subscriber = createAgUiSubscriber({
|
|
1196
1329
|
dispatch,
|
|
1197
|
-
runId,
|
|
1198
1330
|
logger: this.logger,
|
|
1199
1331
|
onRunFailed: (error) => {
|
|
1200
1332
|
if (abortSignal.aborted) return;
|
|
@@ -1205,7 +1337,7 @@ var AgUiThreadController = class {
|
|
|
1205
1337
|
try {
|
|
1206
1338
|
runAgentInstance.messages = input.messages;
|
|
1207
1339
|
runAgentInstance.threadId = input.threadId;
|
|
1208
|
-
runAgentInstance.state = input.state
|
|
1340
|
+
runAgentInstance.state = input.state;
|
|
1209
1341
|
} catch {}
|
|
1210
1342
|
await runAgentInstance.runAgent.bind(runAgentInstance)(input, subscriber, { signal: abortSignal });
|
|
1211
1343
|
}
|
|
@@ -1220,6 +1352,19 @@ var AgUiThreadController = class {
|
|
|
1220
1352
|
pendingError = err;
|
|
1221
1353
|
}
|
|
1222
1354
|
} finally {
|
|
1355
|
+
const current = assistantMessageId ? this.session.tryGetMessage(assistantMessageId)?.message : void 0;
|
|
1356
|
+
if (this.abortController === abortController && current?.role === "assistant" && current.status?.type === "running") {
|
|
1357
|
+
const status = {
|
|
1358
|
+
type: "incomplete",
|
|
1359
|
+
reason: "error",
|
|
1360
|
+
error: "The connection ended before the answer finished."
|
|
1361
|
+
};
|
|
1362
|
+
settleHeldMessages(status);
|
|
1363
|
+
applyUpdate({
|
|
1364
|
+
status,
|
|
1365
|
+
content: settleNestedContent(current.content, status)
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1223
1368
|
this.finishRun(abortController);
|
|
1224
1369
|
}
|
|
1225
1370
|
if (pendingError) {
|
|
@@ -1342,7 +1487,7 @@ var AgUiThreadController = class {
|
|
|
1342
1487
|
return {
|
|
1343
1488
|
threadId,
|
|
1344
1489
|
runId,
|
|
1345
|
-
state: this.state.stateSnapshot
|
|
1490
|
+
state: this.state.stateSnapshot === void 0 ? {} : this.state.stateSnapshot,
|
|
1346
1491
|
messages,
|
|
1347
1492
|
tools: toAgUiTools(context?.tools),
|
|
1348
1493
|
context: [...context?.system ? [{
|
|
@@ -1452,13 +1597,13 @@ var AgUiThreadController = class {
|
|
|
1452
1597
|
const mergedMetadata = update.metadata ? this.mergeAssistantMetadata(assistant.metadata, update.metadata) : assistant.metadata;
|
|
1453
1598
|
latestStatus = update.status ?? assistant.status;
|
|
1454
1599
|
let content = update.content !== void 0 ? replay ? replay.merge(assistant.content, update.content) : this.preserveToolResults(assistant.content, update.content) : assistant.content;
|
|
1455
|
-
const calls =
|
|
1600
|
+
const calls = Array.from(iterateToolCallParts(content));
|
|
1456
1601
|
const pending = pendingToolCallIds ?? (replay ? /* @__PURE__ */ new Set([...replay.admittedToolCallIds, ...Array.from(iterateToolCallParts(update.content ?? [])).filter((part) => !isResolvedToolCall(part)).map((part) => part.toolCallId)]) : void 0);
|
|
1457
1602
|
if (replay && latestStatus?.type !== "running" && pending) replay.admittedToolCallIds = pending;
|
|
1458
|
-
if (replay) {
|
|
1603
|
+
if (replay || latestStatus?.type === "requires-action" && latestStatus.reason === "interrupt") {
|
|
1459
1604
|
const interrupts = latestStatus?.type === "requires-action" && latestStatus.reason === "interrupt" ? mergedMetadata.custom["agui"]?.interrupts : void 0;
|
|
1460
1605
|
const approvals = projectAgUiToolApprovals(interrupts, new Set(calls.map((part) => part.toolCallId)));
|
|
1461
|
-
const originalCalls = new Map(Array.from(iterateToolCallParts(replay.
|
|
1606
|
+
const originalCalls = new Map(Array.from(iterateToolCallParts(replay?.originalContent ?? assistant.content), (part) => [part.toolCallId, part]));
|
|
1462
1607
|
content = mapToolCallPartsDeep(content, (part) => {
|
|
1463
1608
|
const approval = approvals.get(part.toolCallId);
|
|
1464
1609
|
if (approval) return {
|
|
@@ -1535,10 +1680,10 @@ var AgUiThreadController = class {
|
|
|
1535
1680
|
});
|
|
1536
1681
|
return changed ? content : next;
|
|
1537
1682
|
}
|
|
1538
|
-
mergeSnapshotAssistantContent(previous, next, snapshotHasReasoning, snapshotHasActivity) {
|
|
1539
|
-
const shouldKeep = (part) => part.type === "reasoning" ? this.showThinking && !snapshotHasReasoning : isActivityPart(part) && !snapshotHasActivity;
|
|
1683
|
+
mergeSnapshotAssistantContent(previous, next, snapshotHasReasoning, snapshotHasActivity, keepMissingToolCalls = /* @__PURE__ */ new Set(), keepPriorTranscripts = false) {
|
|
1684
|
+
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
1685
|
const kept = previous.filter(shouldKeep);
|
|
1541
|
-
const merged = this.reconcileSnapshotNestedContent(previous, this.preserveToolInteractions(previous, next));
|
|
1686
|
+
const merged = this.reconcileSnapshotNestedContent(previous, this.preserveToolInteractions(previous, next), keepPriorTranscripts);
|
|
1542
1687
|
if (kept.length === 0) return merged;
|
|
1543
1688
|
const mergedByType = /* @__PURE__ */ new Map();
|
|
1544
1689
|
const mergedTools = /* @__PURE__ */ new Map();
|
|
@@ -1570,7 +1715,7 @@ var AgUiThreadController = class {
|
|
|
1570
1715
|
result.push(...insertions[merged.length]);
|
|
1571
1716
|
return result;
|
|
1572
1717
|
}
|
|
1573
|
-
reconcileSnapshotNestedContent(previous, next) {
|
|
1718
|
+
reconcileSnapshotNestedContent(previous, next, keepPriorTranscripts) {
|
|
1574
1719
|
const nestedIds = /* @__PURE__ */ new Set();
|
|
1575
1720
|
for (const part of previous) {
|
|
1576
1721
|
if (part.type !== "tool-call" || !part.messages) continue;
|
|
@@ -1600,11 +1745,49 @@ var AgUiThreadController = class {
|
|
|
1600
1745
|
if (part.type === "tool-call" && flattened.has(part.toolCallId)) return [];
|
|
1601
1746
|
if (part.type !== "tool-call") return [part];
|
|
1602
1747
|
const prior = previousCalls.get(part.toolCallId);
|
|
1603
|
-
|
|
1748
|
+
if (prior?.messages === void 0) return [part];
|
|
1749
|
+
if (part.messages === void 0) return [{
|
|
1604
1750
|
...part,
|
|
1605
1751
|
messages: prior.messages
|
|
1606
1752
|
}];
|
|
1753
|
+
if (part.messages.length === 0) return [part];
|
|
1754
|
+
return [{
|
|
1755
|
+
...part,
|
|
1756
|
+
messages: this.mergeNestedTranscripts(prior.messages, part.messages, keepPriorTranscripts)
|
|
1757
|
+
}];
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1760
|
+
/**
|
|
1761
|
+
* Merges one tool call's nested transcripts message by message. The incoming
|
|
1762
|
+
* copy is the base for tool calls and reasoning; resolved results and calls
|
|
1763
|
+
* only the prior copy has carry over, and the prior text wins when it extends
|
|
1764
|
+
* the incoming text. Importing a snapshot keeps prior messages it lacks, a
|
|
1765
|
+
* prior status that is running or incomplete, and the prior status when the
|
|
1766
|
+
* snapshot waits on calls the merged content has already resolved.
|
|
1767
|
+
*/
|
|
1768
|
+
mergeNestedTranscripts(prior, next, importingSnapshot) {
|
|
1769
|
+
const priorById = new Map(prior.map((message) => [message.id, message]));
|
|
1770
|
+
const merged = next.map((message) => {
|
|
1771
|
+
const previous = priorById.get(message.id);
|
|
1772
|
+
if (previous?.role !== "assistant" || message.role !== "assistant") return message;
|
|
1773
|
+
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);
|
|
1774
|
+
const priorText = textOf(previous.content);
|
|
1775
|
+
const incomingText = textOf(message.content);
|
|
1776
|
+
if (priorText.length > incomingText.length && priorText.startsWith(incomingText)) content = replaceTextParts(content, previous.content);
|
|
1777
|
+
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;
|
|
1778
|
+
return {
|
|
1779
|
+
...message,
|
|
1780
|
+
status,
|
|
1781
|
+
content,
|
|
1782
|
+
metadata: {
|
|
1783
|
+
...message.metadata,
|
|
1784
|
+
custom: mergeAgUiNamespace(previous.metadata.custom, message.metadata.custom)
|
|
1785
|
+
}
|
|
1786
|
+
};
|
|
1607
1787
|
});
|
|
1788
|
+
if (!importingSnapshot) return merged;
|
|
1789
|
+
const mergedById = new Map(merged.map((message) => [message.id, message]));
|
|
1790
|
+
return [...prior.map((message) => mergedById.get(message.id) ?? message), ...merged.filter((message) => !priorById.has(message.id))];
|
|
1608
1791
|
}
|
|
1609
1792
|
mergeAssistantMetadata(current, incoming) {
|
|
1610
1793
|
const annotations = incoming.unstable_annotations ? [...current.unstable_annotations, ...incoming.unstable_annotations] : current.unstable_annotations;
|
|
@@ -1621,6 +1804,7 @@ var AgUiThreadController = class {
|
|
|
1621
1804
|
};
|
|
1622
1805
|
}
|
|
1623
1806
|
handleEvent(aggregator, event, activeAssistantId, joinStrategy, runStartMessageIds, hasRunUserBoundary, reconcileSnapshot) {
|
|
1807
|
+
if (event.type === "SUBAGENT_STARTED" && !this.subagentParentToolCallIds.has(event.subagentRunId)) this.subagentParentToolCallIds.set(event.subagentRunId, event.parentToolCallId);
|
|
1624
1808
|
switch (event.type) {
|
|
1625
1809
|
case "STATE_SNAPSHOT":
|
|
1626
1810
|
this.dispatch({
|
|
@@ -1643,12 +1827,27 @@ var AgUiThreadController = class {
|
|
|
1643
1827
|
}
|
|
1644
1828
|
break;
|
|
1645
1829
|
case "MESSAGES_SNAPSHOT": {
|
|
1830
|
+
aggregator.handle(event);
|
|
1646
1831
|
const previousHeadId = this.session.headId;
|
|
1647
1832
|
const activeAssistant = activeAssistantId === void 0 ? void 0 : this.session.tryGetMessage(activeAssistantId)?.message;
|
|
1648
1833
|
const hasActiveText = activeAssistant?.role === "assistant" && activeAssistant.content.some((part) => part.type === "text");
|
|
1649
1834
|
this.importMessagesSnapshot(event.messages, activeAssistantId, joinStrategy, runStartMessageIds, hasRunUserBoundary, reconcileSnapshot);
|
|
1650
1835
|
const headId = this.session.headId;
|
|
1651
|
-
|
|
1836
|
+
const head = headId === null ? void 0 : this.session.tryGetMessage(headId)?.message;
|
|
1837
|
+
const hasNewPendingToolCallAnswer = head?.role === "assistant" && !runStartMessageIds.has(head.id) && head.status?.type === "requires-action" && head.status.reason === "tool-calls";
|
|
1838
|
+
if (headId !== previousHeadId && (hasActiveText || hasNewPendingToolCallAnswer)) {
|
|
1839
|
+
const nextActiveAssistantId = head?.role === "assistant" ? headId ?? void 0 : void 0;
|
|
1840
|
+
if (activeAssistantId !== nextActiveAssistantId && activeAssistantId) {
|
|
1841
|
+
const previous = this.session.tryGetMessage(activeAssistantId)?.message;
|
|
1842
|
+
if (previous?.role === "assistant" && previous.status?.type === "running") this.updateAssistantMessage(activeAssistantId, {
|
|
1843
|
+
status: FALLBACK_USER_STATUS,
|
|
1844
|
+
content: settleNestedContent(previous.content, FALLBACK_USER_STATUS)
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1847
|
+
activeAssistantId = nextActiveAssistantId;
|
|
1848
|
+
if (activeAssistantId && joinStrategy !== "concat-content") aggregator.rebaseMessage(activeAssistantId);
|
|
1849
|
+
if (hasNewPendingToolCallAnswer && head) this.updateAssistantMessage(head.id, { status: { type: "running" } });
|
|
1850
|
+
}
|
|
1652
1851
|
this.updateActiveAssistantState(activeAssistantId);
|
|
1653
1852
|
break;
|
|
1654
1853
|
}
|
|
@@ -1710,7 +1909,8 @@ var AgUiThreadController = class {
|
|
|
1710
1909
|
text: modelText
|
|
1711
1910
|
}] } : {},
|
|
1712
1911
|
...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 } : {}
|
|
1912
|
+
...event.messageId ? { unstable_toolMessageId: event.messageId } : {},
|
|
1913
|
+
...event.metadata !== void 0 ? { providerMetadata: withAgUiPartMessageMetadata(part.providerMetadata, event.metadata) } : {}
|
|
1714
1914
|
};
|
|
1715
1915
|
const { modelContent: _modelContent, ...partWithoutModelContent } = part;
|
|
1716
1916
|
return {
|
|
@@ -1722,7 +1922,8 @@ var AgUiThreadController = class {
|
|
|
1722
1922
|
}] } : {},
|
|
1723
1923
|
...typeof event.mcpResult?.isError === "boolean" ? { isError: event.mcpResult.isError } : event.role === "tool" ? { isError: false } : {},
|
|
1724
1924
|
...part.mcp === void 0 && mcpAppUri !== void 0 ? { mcp: { app: { resourceUri: mcpAppUri } } } : {},
|
|
1725
|
-
...event.messageId ? { unstable_toolMessageId: event.messageId } : {}
|
|
1925
|
+
...event.messageId ? { unstable_toolMessageId: event.messageId } : {},
|
|
1926
|
+
...event.metadata !== void 0 ? { providerMetadata: withAgUiPartMessageMetadata(part.providerMetadata, event.metadata) } : {}
|
|
1726
1927
|
};
|
|
1727
1928
|
});
|
|
1728
1929
|
if (!matchedToolCall) return message;
|
|
@@ -1732,7 +1933,8 @@ var AgUiThreadController = class {
|
|
|
1732
1933
|
};
|
|
1733
1934
|
})) return;
|
|
1734
1935
|
this.publishUpdate();
|
|
1735
|
-
this.maybeCompleteAfterToolResults(messageId);
|
|
1936
|
+
if (this.maybeCompleteAfterToolResults(messageId)) return;
|
|
1937
|
+
this.persistSettledAssistant(messageId);
|
|
1736
1938
|
}
|
|
1737
1939
|
applyCrossRunActivitySnapshot(messageId, toolCallId, event) {
|
|
1738
1940
|
const resourceUri = event.content["resourceUri"];
|
|
@@ -1771,7 +1973,8 @@ var AgUiThreadController = class {
|
|
|
1771
1973
|
};
|
|
1772
1974
|
})) return;
|
|
1773
1975
|
this.publishUpdate();
|
|
1774
|
-
this.maybeCompleteAfterToolResults(messageId);
|
|
1976
|
+
if (this.maybeCompleteAfterToolResults(messageId)) return;
|
|
1977
|
+
this.persistSettledAssistant(messageId);
|
|
1775
1978
|
}
|
|
1776
1979
|
importMessagesSnapshot(rawMessages, activeAssistantId, joinStrategy, runStartMessageIds, hasRunUserBoundary, reconcileSnapshot) {
|
|
1777
1980
|
try {
|
|
@@ -1779,10 +1982,12 @@ var AgUiThreadController = class {
|
|
|
1779
1982
|
const activeMessage = activeAssistantItem?.message;
|
|
1780
1983
|
const activeAssistantParentId = activeAssistantItem?.parentId;
|
|
1781
1984
|
const activeAssistant = activeMessage?.role === "assistant" ? activeMessage : void 0;
|
|
1782
|
-
const normalized =
|
|
1985
|
+
const normalized = fromAgUiSnapshotMessages(rawMessages, { showThinking: this.showThinking }, this.subagentParentToolCallIds);
|
|
1783
1986
|
const converted = [];
|
|
1784
1987
|
for (const message of normalized) try {
|
|
1785
|
-
|
|
1988
|
+
const previous = message.id ? this.session.tryGetMessage(message.id)?.message : void 0;
|
|
1989
|
+
const fallbackStatus = previous?.role === "assistant" && previous.status?.type === "running" ? previous.status : FALLBACK_USER_STATUS;
|
|
1990
|
+
converted.push(fromThreadMessageLike(message, generateId(), fallbackStatus));
|
|
1786
1991
|
} catch (error) {
|
|
1787
1992
|
this.logger.error?.("[agui] failed to import message from snapshot", error);
|
|
1788
1993
|
}
|
|
@@ -1794,9 +1999,13 @@ var AgUiThreadController = class {
|
|
|
1794
1999
|
if (convertedMessage.role === "assistant") {
|
|
1795
2000
|
const previous = existing?.role === "assistant" ? existing : void 0;
|
|
1796
2001
|
const snapshot = reconcileSnapshot?.(convertedMessage, previous) ?? convertedMessage;
|
|
2002
|
+
const preservesRunningStatus = convertedMessage.id === activeAssistantId && previous?.status?.type === "running" && snapshot.status?.type === "requires-action" && snapshot.status.reason === "tool-calls";
|
|
2003
|
+
const previousToolCallIds = new Set(Array.from(iterateToolCallParts(previous?.content ?? []), (call) => call.toolCallId));
|
|
2004
|
+
const preservesSettledStatus = convertedMessage.id !== activeAssistantId && this.isTerminalStatus(previous?.status) && snapshot.status?.type === "requires-action" && snapshot.status.reason === "tool-calls" && Array.from(iterateToolCallParts(snapshot.content)).every((call) => isResolvedToolCall(call) || previousToolCallIds.has(call.toolCallId));
|
|
1797
2005
|
converted[index] = previous ? {
|
|
1798
2006
|
...snapshot,
|
|
1799
|
-
|
|
2007
|
+
...preservesRunningStatus || preservesSettledStatus ? { status: previous.status } : {},
|
|
2008
|
+
content: this.mergeSnapshotAssistantContent(previous.content, snapshot.content, snapshotHasReasoning, snapshotHasActivity, /* @__PURE__ */ new Set(), true)
|
|
1800
2009
|
} : snapshot;
|
|
1801
2010
|
}
|
|
1802
2011
|
}
|
|
@@ -1881,6 +2090,14 @@ var AgUiThreadController = class {
|
|
|
1881
2090
|
if (!this.history) return;
|
|
1882
2091
|
this.assistantHistoryParents.set(messageId, parentId);
|
|
1883
2092
|
}
|
|
2093
|
+
persistSettledAssistant(messageId) {
|
|
2094
|
+
const item = this.session.tryGetMessage(messageId);
|
|
2095
|
+
if (!this.history?.update || !item) return;
|
|
2096
|
+
if (!this.isTerminalStatus(item.message.status)) return;
|
|
2097
|
+
if (!this.persistedHistoryIds.has(messageId) && this.historyWrites.pending(messageId) === void 0) return;
|
|
2098
|
+
this.markPendingAssistantHistory(messageId, item.parentId);
|
|
2099
|
+
this.persistAssistantHistory(messageId);
|
|
2100
|
+
}
|
|
1884
2101
|
persistAssistantHistory(messageId) {
|
|
1885
2102
|
const history = this.history;
|
|
1886
2103
|
if (!history) return;
|
|
@@ -1891,7 +2108,7 @@ var AgUiThreadController = class {
|
|
|
1891
2108
|
if (!this.isPersistableStatus(message.status)) return;
|
|
1892
2109
|
const wasPersisted = this.persistedHistoryIds.has(messageId);
|
|
1893
2110
|
const update = history.update;
|
|
1894
|
-
if (update !== void 0 && (wasPersisted || this.snapshotHistoryIds.has(messageId))) {
|
|
2111
|
+
if (update !== void 0 && (wasPersisted || this.snapshotHistoryIds.has(messageId) || this.historyWrites.pending(messageId) !== void 0)) {
|
|
1895
2112
|
const write = this.historyWrites.chain(messageId, () => update.call(history, {
|
|
1896
2113
|
parentId,
|
|
1897
2114
|
message
|