@aparte/runner-ag-ui 1.6.0 → 1.7.0
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/dist/index.js +279 -24
- package/dist/index.js.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/state.d.ts +6 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/subagent-history.d.ts +56 -0
- package/dist/subagent-history.d.ts.map +1 -0
- package/dist/wire.d.ts +12 -3
- package/dist/wire.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -210,23 +210,138 @@ function step(doc, op) {
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
function applyStateDelta(state, delta, warn) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
213
|
+
return applyPatch(state, delta, warn, { event: "STATE_DELTA", field: "delta", what: "state" });
|
|
214
|
+
}
|
|
215
|
+
function applyActivityDelta(content, patch, warn) {
|
|
216
|
+
return applyPatch(content, patch, warn, { event: "ACTIVITY_DELTA", field: "patch", what: "activity" });
|
|
217
|
+
}
|
|
218
|
+
function applyPatch(doc, patch, warn, names) {
|
|
219
|
+
const { event, field, what } = names;
|
|
220
|
+
if (!Array.isArray(patch)) {
|
|
221
|
+
warn(`Failed to apply ${what} patch: ${event} carried no list of operations; the ${what} is left as it was.`);
|
|
222
|
+
return doc;
|
|
216
223
|
}
|
|
217
224
|
const ops = [];
|
|
218
|
-
|
|
225
|
+
patch.forEach((op, i) => {
|
|
219
226
|
if (isContainer(op) && !Array.isArray(op) && OPS.has(String(op["op"]))) ops.push(op);
|
|
220
|
-
else warn(`dropped
|
|
227
|
+
else warn(`dropped /${field}/${i}: ${JSON.stringify(op)} is not a JSON Patch operation.`);
|
|
221
228
|
});
|
|
222
229
|
try {
|
|
223
|
-
let
|
|
224
|
-
for (const op of ops)
|
|
225
|
-
return
|
|
230
|
+
let next = structuredClone(doc);
|
|
231
|
+
for (const op of ops) next = step(next, op);
|
|
232
|
+
return next;
|
|
226
233
|
} catch (error) {
|
|
227
|
-
warn(`Failed to apply
|
|
228
|
-
return
|
|
234
|
+
warn(`Failed to apply ${what} patch: ${error.message}; the ${what} is left as it was.`);
|
|
235
|
+
return doc;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function createSubagentHistory(from = []) {
|
|
239
|
+
const records = new Map(from.map((r) => [r.id, JSON.parse(JSON.stringify(r))]));
|
|
240
|
+
const record = (id) => {
|
|
241
|
+
let at = records.get(id);
|
|
242
|
+
if (!at) {
|
|
243
|
+
at = { id, name: "subagent", status: "running", messages: [] };
|
|
244
|
+
records.set(id, at);
|
|
245
|
+
}
|
|
246
|
+
return at;
|
|
247
|
+
};
|
|
248
|
+
const assistantCall = (owner, toolCallId) => {
|
|
249
|
+
for (const m of record(owner).messages) {
|
|
250
|
+
if (m.role !== "assistant") continue;
|
|
251
|
+
const call = m.toolCalls?.find((c) => c.id === toolCallId);
|
|
252
|
+
if (call) return call;
|
|
253
|
+
}
|
|
254
|
+
return void 0;
|
|
255
|
+
};
|
|
256
|
+
return {
|
|
257
|
+
/** Announced (again, by a resumed run: the same invocation going on). */
|
|
258
|
+
start(id, info) {
|
|
259
|
+
const at = record(id);
|
|
260
|
+
Object.assign(at, info);
|
|
261
|
+
at.status = "running";
|
|
262
|
+
delete at.interruptIds;
|
|
263
|
+
},
|
|
264
|
+
end(id, status, interruptIds) {
|
|
265
|
+
const at = record(id);
|
|
266
|
+
at.status = status;
|
|
267
|
+
if (interruptIds?.length) at.interruptIds = interruptIds;
|
|
268
|
+
else delete at.interruptIds;
|
|
269
|
+
},
|
|
270
|
+
text(owner, messageId, delta) {
|
|
271
|
+
const messages = record(owner).messages;
|
|
272
|
+
let at = messages.find((m) => m.role === "assistant" && m.id === messageId);
|
|
273
|
+
if (!at) {
|
|
274
|
+
at = { id: messageId, role: "assistant", content: "" };
|
|
275
|
+
messages.push(at);
|
|
276
|
+
}
|
|
277
|
+
at.content = (at.content ?? "") + delta;
|
|
278
|
+
},
|
|
279
|
+
/** A call joins the assistant message that ends the transcript, as one model step writes text then calls. */
|
|
280
|
+
call(owner, toolCallId, name) {
|
|
281
|
+
const known = assistantCall(owner, toolCallId);
|
|
282
|
+
if (known) {
|
|
283
|
+
if (name) known.function.name = name;
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const messages = record(owner).messages;
|
|
287
|
+
const last = messages.at(-1);
|
|
288
|
+
let host;
|
|
289
|
+
if (last?.role === "assistant") {
|
|
290
|
+
host = last;
|
|
291
|
+
} else {
|
|
292
|
+
host = { id: `${toolCallId}:call`, role: "assistant" };
|
|
293
|
+
messages.push(host);
|
|
294
|
+
}
|
|
295
|
+
host.toolCalls = [...host.toolCalls ?? [], { id: toolCallId, type: "function", function: { name, arguments: "" } }];
|
|
296
|
+
},
|
|
297
|
+
args(owner, toolCallId, delta) {
|
|
298
|
+
const call = assistantCall(owner, toolCallId);
|
|
299
|
+
if (call) call.function.arguments += delta;
|
|
300
|
+
},
|
|
301
|
+
result(owner, messageId, toolCallId, content) {
|
|
302
|
+
record(owner).messages.push({ id: messageId ?? `${toolCallId}:result`, role: "tool", toolCallId, content });
|
|
303
|
+
},
|
|
304
|
+
/** The run's sub-agents, `tanstack` set on each when the run was TanStack AI's. */
|
|
305
|
+
records(tanstack = false) {
|
|
306
|
+
return [...records.values()].map((r) => tanstack ? { ...r, tanstack: true } : r);
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function subagentMessages(records, answers = /* @__PURE__ */ new Map()) {
|
|
311
|
+
return records.filter((r) => r.tanstack).flatMap((r) => withAnswers(r.messages, answers).map((m, i) => {
|
|
312
|
+
if (m.role !== "assistant" && m.role !== "tool") return m;
|
|
313
|
+
const card = i === 0 ? {
|
|
314
|
+
metadata: {
|
|
315
|
+
tanstack: {
|
|
316
|
+
subagent: {
|
|
317
|
+
name: r.name,
|
|
318
|
+
status: r.status,
|
|
319
|
+
...r.description !== void 0 ? { description: r.description } : {},
|
|
320
|
+
...r.parentToolCallId !== void 0 ? { parentToolCallId: r.parentToolCallId } : {},
|
|
321
|
+
...r.parentSubagentRunId !== void 0 ? { parentSubagentRunId: r.parentSubagentRunId } : {},
|
|
322
|
+
...r.interruptIds ? { interruptIds: r.interruptIds } : {},
|
|
323
|
+
...r.metadata ? { metadata: r.metadata } : {}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} : {};
|
|
328
|
+
return { ...m, subagentRunId: r.id, ...card };
|
|
329
|
+
}));
|
|
330
|
+
}
|
|
331
|
+
function withAnswers(messages, answers) {
|
|
332
|
+
if (answers.size === 0) return [...messages];
|
|
333
|
+
const out = [];
|
|
334
|
+
for (let i = 0; i < messages.length; i++) {
|
|
335
|
+
const m = messages[i];
|
|
336
|
+
out.push(m);
|
|
337
|
+
if (m.role !== "assistant" || !m.toolCalls?.length) continue;
|
|
338
|
+
while (messages[i + 1]?.role === "tool") out.push(messages[++i]);
|
|
339
|
+
for (const call of m.toolCalls) {
|
|
340
|
+
const answer = answers.get(call.id);
|
|
341
|
+
if (answer) out.push(answer);
|
|
342
|
+
}
|
|
229
343
|
}
|
|
344
|
+
return out;
|
|
230
345
|
}
|
|
231
346
|
function compare(a, b) {
|
|
232
347
|
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
@@ -577,14 +692,21 @@ const PARENT = "";
|
|
|
577
692
|
function by(owner) {
|
|
578
693
|
return owner ? { agentId: owner } : {};
|
|
579
694
|
}
|
|
695
|
+
function routerPlanOf(event) {
|
|
696
|
+
const tanstack = event["metadata"]?.tanstack;
|
|
697
|
+
const steps = tanstack?.subagentPlan?.steps;
|
|
698
|
+
if (!Array.isArray(steps)) return void 0;
|
|
699
|
+
const names = steps.map((step2) => Array.isArray(step2?.names) ? step2.names.filter((n) => typeof n === "string" && n !== "") : []).filter((step2) => step2.length > 0);
|
|
700
|
+
return names.length ? names : void 0;
|
|
701
|
+
}
|
|
580
702
|
function interruptKind(interrupt) {
|
|
581
703
|
if (interrupt.reason === "tool_call" || interrupt.reason === "confirmation" || interrupt.reason === "approval_required") return "approval";
|
|
582
704
|
if (/client_tool/.test(interrupt.reason)) return "external";
|
|
583
705
|
return void 0;
|
|
584
706
|
}
|
|
585
|
-
function readUsage(value) {
|
|
707
|
+
function readUsage(value, pick = "last") {
|
|
586
708
|
const entries = (Array.isArray(value) ? value : value && typeof value === "object" ? [value] : []).filter((u) => Boolean(u) && typeof u === "object");
|
|
587
|
-
const last = entries.at(-1);
|
|
709
|
+
const last = pick === "first" ? entries[0] : entries.at(-1);
|
|
588
710
|
if (!last) return void 0;
|
|
589
711
|
const num = (key) => typeof last[key] === "number" ? last[key] : 0;
|
|
590
712
|
const cached = num("cachedInputTokens");
|
|
@@ -662,16 +784,23 @@ function createAgUiRunner(options) {
|
|
|
662
784
|
const runId = `aparte-run-${uuid()}`;
|
|
663
785
|
const interrupted = new Set((ticket?.interrupts ?? []).map((i) => i.toolCallId));
|
|
664
786
|
const messages = toAgUiMessages(request.messages, flatten, warn);
|
|
787
|
+
const pausedAgents = Array.isArray(ticket?.subagents) ? ticket.subagents : [];
|
|
788
|
+
const sentBack = new Set(pausedAgents.filter((r) => r.tanstack).map((r) => r.id));
|
|
789
|
+
const answers = /* @__PURE__ */ new Map();
|
|
665
790
|
for (const [toolCallId, outcome] of Object.entries(outcomes)) {
|
|
666
791
|
if (interrupted.has(toolCallId)) continue;
|
|
667
792
|
if (outcome.kind === "decision" && outcome.approved) continue;
|
|
668
|
-
|
|
793
|
+
const answer = {
|
|
669
794
|
id: `aparte-result-${toolCallId}`,
|
|
670
795
|
role: "tool",
|
|
671
796
|
toolCallId,
|
|
672
797
|
content: outcome.kind === "failed" ? `Error: ${outcome.error}` : outcome.kind === "decision" ? rejectionText(outcome) : outcome.content
|
|
673
|
-
}
|
|
798
|
+
};
|
|
799
|
+
const owner = ticket?.agents?.[toolCallId];
|
|
800
|
+
if (owner !== void 0 && sentBack.has(owner)) answers.set(toolCallId, answer);
|
|
801
|
+
else messages.push(answer);
|
|
674
802
|
}
|
|
803
|
+
messages.push(...subagentMessages(pausedAgents, answers));
|
|
675
804
|
const left = ticket ? [] : unanswered.get(threadId) ?? [];
|
|
676
805
|
unanswered.delete(threadId);
|
|
677
806
|
const resume = ticket?.interrupts.length ? ticket.interrupts.map((interrupt) => resumeItem(interrupt, interrupt.kind === "unanswerable" ? void 0 : outcomes[interrupt.toolCallId])) : left.length ? left.map((interruptId) => ({ interruptId, status: "cancelled" })) : void 0;
|
|
@@ -730,6 +859,9 @@ function createAgUiRunner(options) {
|
|
|
730
859
|
const lastCall = /* @__PURE__ */ new Map();
|
|
731
860
|
const ticketAgents = new Map(Object.entries(ticket?.agents ?? {}).filter((entry) => typeof entry[1] === "string"));
|
|
732
861
|
const agents = /* @__PURE__ */ new Map();
|
|
862
|
+
const transcripts = createSubagentHistory(pausedAgents);
|
|
863
|
+
let tanstackRun = false;
|
|
864
|
+
let ownStep = false;
|
|
733
865
|
const tagOf = (event) => typeof event["subagentRunId"] === "string" ? event["subagentRunId"] : void 0;
|
|
734
866
|
const messageOwner = (event) => {
|
|
735
867
|
let id = typeof event["messageId"] === "string" ? event["messageId"] : void 0;
|
|
@@ -758,10 +890,40 @@ function createAgUiRunner(options) {
|
|
|
758
890
|
agents.set(agentId, { open: true });
|
|
759
891
|
emit({ type: "agent-start", agentId });
|
|
760
892
|
};
|
|
893
|
+
const openSteps = [];
|
|
894
|
+
const closeSteps = (owner) => {
|
|
895
|
+
for (let i = openSteps.length - 1; i >= 0; i -= 1) {
|
|
896
|
+
const step2 = openSteps[i];
|
|
897
|
+
if (owner !== void 0 && step2.owner !== owner) continue;
|
|
898
|
+
openSteps.splice(i, 1);
|
|
899
|
+
emit({ type: "step-end", name: step2.name, ...by(step2.owner) });
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
track.closeSteps = closeSteps;
|
|
903
|
+
const activities = /* @__PURE__ */ new Map();
|
|
904
|
+
const showActivity = (id, at) => {
|
|
905
|
+
activities.set(id, at);
|
|
906
|
+
emit({ type: "activity", id, activityType: at.activityType, content: at.content, ...by(at.owner) });
|
|
907
|
+
};
|
|
908
|
+
const paused = ticket?.plan;
|
|
909
|
+
let routerPlan = paused && Array.isArray(paused.steps) ? { steps: paused.steps, status: new Map(Object.entries(paused.status ?? {})) } : void 0;
|
|
910
|
+
const planId = typeof paused?.id === "string" ? paused.id : `router-plan:${runId}`;
|
|
911
|
+
const tickRouterPlan = (name, status) => {
|
|
912
|
+
const at = name === void 0 ? -1 : routerPlan?.steps.findIndex((step2) => step2.includes(name)) ?? -1;
|
|
913
|
+
if (!routerPlan || at < 0) return;
|
|
914
|
+
if (status === "in-progress") {
|
|
915
|
+
for (const before of routerPlan.steps.slice(0, at).flat()) if (routerPlan.status.get(before) !== "failed") routerPlan.status.set(before, "done");
|
|
916
|
+
}
|
|
917
|
+
routerPlan.status.set(name, status);
|
|
918
|
+
const steps = routerPlan.steps.flat().map((title) => ({ title, status: routerPlan.status.get(title) ?? "pending" }));
|
|
919
|
+
showActivity(planId, { activityType: "plan", content: { steps }, owner: PARENT });
|
|
920
|
+
};
|
|
761
921
|
const endAgent = (agentId, status, detail = {}) => {
|
|
922
|
+
closeSteps(agentId);
|
|
762
923
|
const agent = agents.get(agentId);
|
|
763
924
|
if (agent) agent.open = false;
|
|
764
925
|
emit({ type: "agent-end", agentId, status, ...detail, ...agent?.parent ? { parentAgentId: agent.parent } : {} });
|
|
926
|
+
if (agent && !agent.parent) tickRouterPlan(agent.name, status === "waiting" ? "blocked" : status);
|
|
765
927
|
};
|
|
766
928
|
const failOpenAgents = (error) => {
|
|
767
929
|
for (const [agentId, agent] of [...agents].reverse()) {
|
|
@@ -844,6 +1006,8 @@ function createAgUiRunner(options) {
|
|
|
844
1006
|
if (/^(TEXT_MESSAGE|REASONING|TOOL_CALL)_/.test(event.type)) endedOnFinish = false;
|
|
845
1007
|
const tag = tagOf(event);
|
|
846
1008
|
if (tag !== void 0 && !event.type.startsWith("SUBAGENT_")) meet(tag);
|
|
1009
|
+
const metadata = event.metadata;
|
|
1010
|
+
if (metadata && typeof metadata === "object" && typeof metadata.tanstack === "object") tanstackRun = true;
|
|
847
1011
|
switch (event.type) {
|
|
848
1012
|
case "RUN_STARTED": {
|
|
849
1013
|
if (runError) {
|
|
@@ -866,18 +1030,24 @@ function createAgUiRunner(options) {
|
|
|
866
1030
|
}
|
|
867
1031
|
case "TEXT_MESSAGE_START":
|
|
868
1032
|
case "REASONING_START":
|
|
869
|
-
case "REASONING_MESSAGE_START":
|
|
870
|
-
|
|
1033
|
+
case "REASONING_MESSAGE_START": {
|
|
1034
|
+
const owner = messageOwner(event);
|
|
1035
|
+
if (owner === PARENT) ownStep = true;
|
|
1036
|
+
nextStep(owner);
|
|
871
1037
|
break;
|
|
1038
|
+
}
|
|
872
1039
|
case "TEXT_MESSAGE_CONTENT":
|
|
873
1040
|
case "TEXT_MESSAGE_CHUNK": {
|
|
874
1041
|
const owner = messageOwner(event);
|
|
875
1042
|
const delta = typeof event["delta"] === "string" ? event["delta"] : "";
|
|
1043
|
+
if (owner === PARENT) ownStep = true;
|
|
876
1044
|
if (delta) {
|
|
877
1045
|
nextStep(owner);
|
|
878
1046
|
emit({ type: "text-delta", delta, ...by(owner) });
|
|
879
1047
|
lane(owner).textOpen = true;
|
|
880
1048
|
}
|
|
1049
|
+
const id = typeof event["messageId"] === "string" ? event["messageId"] : chunkMessages.get(`${event.type}\0${tag ?? PARENT}`);
|
|
1050
|
+
if (owner !== PARENT && delta && id !== void 0) transcripts.text(owner, id, delta);
|
|
881
1051
|
break;
|
|
882
1052
|
}
|
|
883
1053
|
case "TEXT_MESSAGE_END":
|
|
@@ -896,12 +1066,20 @@ function createAgUiRunner(options) {
|
|
|
896
1066
|
case "TOOL_CALL_START": {
|
|
897
1067
|
const id = String(event["toolCallId"]);
|
|
898
1068
|
if (!calls.has(id)) lastCall.set(tag ?? PARENT, id);
|
|
899
|
-
|
|
1069
|
+
const owner = newCallOwner(event);
|
|
1070
|
+
const name = String(event["toolCallName"] ?? "");
|
|
1071
|
+
calls.set(id, { name, args: "", owner });
|
|
1072
|
+
if (owner === PARENT) ownStep = true;
|
|
1073
|
+
else transcripts.call(owner, id, name);
|
|
900
1074
|
break;
|
|
901
1075
|
}
|
|
902
1076
|
case "TOOL_CALL_ARGS": {
|
|
903
|
-
const
|
|
904
|
-
|
|
1077
|
+
const id = String(event["toolCallId"]);
|
|
1078
|
+
const call = calls.get(id);
|
|
1079
|
+
if (call && typeof event["delta"] === "string") {
|
|
1080
|
+
call.args += event["delta"];
|
|
1081
|
+
if (call.owner !== PARENT) transcripts.args(call.owner, id, event["delta"]);
|
|
1082
|
+
}
|
|
905
1083
|
break;
|
|
906
1084
|
}
|
|
907
1085
|
case "TOOL_CALL_CHUNK": {
|
|
@@ -915,6 +1093,12 @@ function createAgUiRunner(options) {
|
|
|
915
1093
|
}
|
|
916
1094
|
if (typeof event["toolCallName"] === "string" && event["toolCallName"]) call.name = event["toolCallName"];
|
|
917
1095
|
if (typeof event["delta"] === "string") call.args += event["delta"];
|
|
1096
|
+
if (call.owner === PARENT) {
|
|
1097
|
+
ownStep = true;
|
|
1098
|
+
} else {
|
|
1099
|
+
transcripts.call(call.owner, id, call.name);
|
|
1100
|
+
if (typeof event["delta"] === "string") transcripts.args(call.owner, id, event["delta"]);
|
|
1101
|
+
}
|
|
918
1102
|
break;
|
|
919
1103
|
}
|
|
920
1104
|
case "TOOL_CALL_END": {
|
|
@@ -928,6 +1112,7 @@ function createAgUiRunner(options) {
|
|
|
928
1112
|
answered.add(toolCallId);
|
|
929
1113
|
const owner = callOwner(toolCallId) ?? tag ?? PARENT;
|
|
930
1114
|
const content = resultText(event["content"]);
|
|
1115
|
+
if (owner !== PARENT) transcripts.result(owner, typeof event["messageId"] === "string" ? event["messageId"] : void 0, toolCallId, content);
|
|
931
1116
|
const meta = event["metadata"];
|
|
932
1117
|
if (meta?.tanstack?.state === "output-error") {
|
|
933
1118
|
emit({ type: "tool-failed", toolCallId, error: content, ...by(owner) });
|
|
@@ -942,11 +1127,27 @@ function createAgUiRunner(options) {
|
|
|
942
1127
|
const toolCallId = typeof event["parentToolCallId"] === "string" ? event["parentToolCallId"] : void 0;
|
|
943
1128
|
const parent = typeof event["parentSubagentRunId"] === "string" ? event["parentSubagentRunId"] : void 0;
|
|
944
1129
|
if (toolCallId) startTool(toolCallId);
|
|
945
|
-
|
|
1130
|
+
const name = String(event["name"]);
|
|
1131
|
+
agents.set(agentId, { open: true, name, ...parent ? { parent } : {} });
|
|
1132
|
+
const metadata2 = event["metadata"];
|
|
1133
|
+
transcripts.start(agentId, {
|
|
1134
|
+
name,
|
|
1135
|
+
...typeof event["description"] === "string" ? { description: event["description"] } : {},
|
|
1136
|
+
...toolCallId ? { parentToolCallId: toolCallId } : {},
|
|
1137
|
+
...parent ? { parentSubagentRunId: parent } : {},
|
|
1138
|
+
...metadata2 && typeof metadata2 === "object" && !Array.isArray(metadata2) ? { metadata: metadata2 } : {}
|
|
1139
|
+
});
|
|
1140
|
+
if (!parent) {
|
|
1141
|
+
if (!routerPlan) {
|
|
1142
|
+
const steps = routerPlanOf(event);
|
|
1143
|
+
if (steps) routerPlan = { steps, status: /* @__PURE__ */ new Map() };
|
|
1144
|
+
}
|
|
1145
|
+
tickRouterPlan(name, "in-progress");
|
|
1146
|
+
}
|
|
946
1147
|
emit({
|
|
947
1148
|
type: "agent-start",
|
|
948
1149
|
agentId,
|
|
949
|
-
name
|
|
1150
|
+
name,
|
|
950
1151
|
...typeof event["description"] === "string" ? { description: event["description"] } : {},
|
|
951
1152
|
...toolCallId ? { toolCallId } : {},
|
|
952
1153
|
...parent ? { parentAgentId: parent } : {}
|
|
@@ -958,11 +1159,16 @@ function createAgUiRunner(options) {
|
|
|
958
1159
|
const agentId = String(event["subagentRunId"]);
|
|
959
1160
|
flush(agentId);
|
|
960
1161
|
for (const [id, call] of calls) if (call.owner === agentId) startTool(id);
|
|
1162
|
+
const outcome = event["outcome"];
|
|
961
1163
|
if (event.type === "SUBAGENT_ERROR") {
|
|
1164
|
+
transcripts.end(agentId, "error");
|
|
962
1165
|
endAgent(agentId, "failed", { error: typeof event["message"] === "string" && event["message"] ? event["message"] : "The sub-agent failed." });
|
|
963
|
-
} else if (
|
|
1166
|
+
} else if (outcome?.type === "suspended") {
|
|
1167
|
+
const ids = Array.isArray(outcome.interruptIds) ? outcome.interruptIds.filter((id) => typeof id === "string") : [];
|
|
1168
|
+
transcripts.end(agentId, "suspended", ids);
|
|
964
1169
|
endAgent(agentId, "waiting");
|
|
965
1170
|
} else {
|
|
1171
|
+
transcripts.end(agentId, "finished");
|
|
966
1172
|
endAgent(agentId, "done", event["result"] !== void 0 ? { result: resultText(event["result"]) } : {});
|
|
967
1173
|
}
|
|
968
1174
|
break;
|
|
@@ -987,7 +1193,9 @@ function createAgUiRunner(options) {
|
|
|
987
1193
|
case "RUN_FINISHED": {
|
|
988
1194
|
for (const id of calls.keys()) startTool(id);
|
|
989
1195
|
for (const owner of lanes.keys()) flush(owner);
|
|
990
|
-
usage = readUsage(event["usage"]) ?? usage;
|
|
1196
|
+
if (agents.size === 0) usage = readUsage(event["usage"]) ?? usage;
|
|
1197
|
+
else if (ownStep) usage = readUsage(event["usage"], "first") ?? usage;
|
|
1198
|
+
ownStep = false;
|
|
991
1199
|
lane(PARENT).stepEnded = true;
|
|
992
1200
|
for (const at of lanes.values()) at.stepEnded = true;
|
|
993
1201
|
endedOnFinish = true;
|
|
@@ -997,8 +1205,52 @@ function createAgUiRunner(options) {
|
|
|
997
1205
|
};
|
|
998
1206
|
break;
|
|
999
1207
|
}
|
|
1208
|
+
// A step is a name with a start and an end, its owner's: shown while it lasts
|
|
1209
|
+
// (the waiting indicator, a sub-agent's line). Steps may nest and overlap, and
|
|
1210
|
+
// one ends the latest open step of its name under the same owner.
|
|
1211
|
+
case "STEP_STARTED": {
|
|
1212
|
+
const owner = tag ?? PARENT;
|
|
1213
|
+
const name = String(event["stepName"] ?? "");
|
|
1214
|
+
openSteps.push({ owner, name });
|
|
1215
|
+
emit({ type: "step-start", name, ...by(owner) });
|
|
1216
|
+
break;
|
|
1217
|
+
}
|
|
1218
|
+
case "STEP_FINISHED": {
|
|
1219
|
+
const owner = tag ?? PARENT;
|
|
1220
|
+
const name = String(event["stepName"] ?? "");
|
|
1221
|
+
for (let i = openSteps.length - 1; i >= 0; i -= 1) {
|
|
1222
|
+
if (openSteps[i].owner !== owner || openSteps[i].name !== name) continue;
|
|
1223
|
+
openSteps.splice(i, 1);
|
|
1224
|
+
emit({ type: "step-end", name, ...by(owner) });
|
|
1225
|
+
break;
|
|
1226
|
+
}
|
|
1227
|
+
break;
|
|
1228
|
+
}
|
|
1229
|
+
// An activity is structured progress, drawn by the renderer the app registered for
|
|
1230
|
+
// its type. A snapshot overwrites it unless it says `replace: false` (AG-UI 1.0:
|
|
1231
|
+
// "only an explicit false asks a consumer to leave what is already there") — its
|
|
1232
|
+
// type included; a delta patches what is held. Either way the page gets it whole.
|
|
1233
|
+
case "ACTIVITY_SNAPSHOT": {
|
|
1234
|
+
const id = String(event["messageId"] ?? "");
|
|
1235
|
+
const held = activities.get(id);
|
|
1236
|
+
if (held && event["replace"] === false) break;
|
|
1237
|
+
showActivity(id, { activityType: String(event["activityType"] ?? ""), content: event["content"], owner: held?.owner ?? tag ?? PARENT });
|
|
1238
|
+
break;
|
|
1239
|
+
}
|
|
1240
|
+
case "ACTIVITY_DELTA": {
|
|
1241
|
+
const id = String(event["messageId"] ?? "");
|
|
1242
|
+
const held = activities.get(id);
|
|
1243
|
+
if (!held) {
|
|
1244
|
+
warn(`ACTIVITY_DELTA for "${id}", an activity no ACTIVITY_SNAPSHOT created: skipped.`);
|
|
1245
|
+
break;
|
|
1246
|
+
}
|
|
1247
|
+
const content = applyActivityDelta(held.content, event["patch"], warn);
|
|
1248
|
+
if (content !== held.content) showActivity(id, { ...held, content });
|
|
1249
|
+
break;
|
|
1250
|
+
}
|
|
1000
1251
|
}
|
|
1001
1252
|
}
|
|
1253
|
+
closeSteps();
|
|
1002
1254
|
if (opts.signal.aborted) return usage;
|
|
1003
1255
|
if (runError) {
|
|
1004
1256
|
for (const [toolCallId, call] of started) if (!answered.has(toolCallId)) emit({ type: "tool-aborted", toolCallId, ...by(call.owner) });
|
|
@@ -1053,7 +1305,9 @@ function createAgUiRunner(options) {
|
|
|
1053
1305
|
threadId,
|
|
1054
1306
|
runId: finished.runId,
|
|
1055
1307
|
interrupts: [...interrupts, ...cancelled],
|
|
1056
|
-
...owned.length ? { agents: Object.fromEntries(owned) } : {}
|
|
1308
|
+
...owned.length ? { agents: Object.fromEntries(owned) } : {},
|
|
1309
|
+
...transcripts.records().length ? { subagents: transcripts.records(tanstackRun) } : {},
|
|
1310
|
+
...routerPlan ? { plan: { id: planId, steps: routerPlan.steps, status: Object.fromEntries(routerPlan.status) } } : {}
|
|
1057
1311
|
};
|
|
1058
1312
|
track.suspended = true;
|
|
1059
1313
|
emit({ type: "run-suspended", pending, ...usage ? { usage } : {}, ticket: JSON.stringify(next) });
|
|
@@ -1069,6 +1323,7 @@ function createAgUiRunner(options) {
|
|
|
1069
1323
|
return await runOnce(opts, track);
|
|
1070
1324
|
} finally {
|
|
1071
1325
|
if (track.threadId && !stableThread && !track.suspended) states.delete(track.threadId);
|
|
1326
|
+
track.closeSteps?.();
|
|
1072
1327
|
}
|
|
1073
1328
|
};
|
|
1074
1329
|
}
|