@botbotgo/agent-harness 0.0.251 → 0.0.252
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 +13 -14
- package/README.zh.md +11 -12
- package/dist/api.d.ts +13 -6
- package/dist/api.js +70 -6
- package/dist/config/agents/direct.yaml +3 -3
- package/dist/config/agents/orchestra.yaml +3 -3
- package/dist/config/catalogs/stores.yaml +3 -9
- package/dist/config/runtime/workspace.yaml +1 -2
- package/dist/contracts/runtime.d.ts +9 -14
- package/dist/flow/build-flow-graph.js +198 -67
- package/dist/flow/export-mermaid.js +314 -4
- package/dist/flow/export-sequence-mermaid.js +149 -2
- package/dist/flow/types.d.ts +11 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +1 -1
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/persistence/file-store.d.ts +3 -2
- package/dist/persistence/file-store.js +34 -8
- package/dist/persistence/sqlite-store.d.ts +2 -2
- package/dist/persistence/sqlite-store.js +64 -11
- package/dist/persistence/types.d.ts +3 -3
- package/dist/protocol/a2a/http.js +2 -4
- package/dist/resource/isolation.js +30 -2
- package/dist/runtime/harness/events/streaming.js +8 -8
- package/dist/runtime/harness/run/inspection.d.ts +2 -0
- package/dist/runtime/harness/run/inspection.js +91 -46
- package/dist/runtime/harness/run/stream-run.d.ts +2 -2
- package/dist/runtime/harness/run/stream-run.js +34 -23
- package/dist/runtime/harness/run/surface-semantics.d.ts +14 -0
- package/dist/runtime/harness/run/surface-semantics.js +106 -0
- package/dist/runtime/harness/run/thread-records.js +2 -34
- package/dist/runtime/harness/system/store.d.ts +6 -4
- package/dist/runtime/harness/system/store.js +76 -42
- package/dist/runtime/harness.js +5 -7
- package/dist/runtime/maintenance/checkpoint-maintenance.js +4 -119
- package/dist/runtime/maintenance/index.d.ts +0 -1
- package/dist/runtime/maintenance/index.js +0 -1
- package/dist/runtime/support/runtime-factories.js +2 -42
- package/dist/upstream-events.js +14 -0
- package/package.json +1 -3
- package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.d.ts +0 -9
- package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.js +0 -39
- package/dist/runtime/support/sqlite-drivers.d.ts +0 -12
- package/dist/runtime/support/sqlite-drivers.js +0 -24
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
import { formatAgentName } from "../utils/agent-display.js";
|
|
2
|
+
const PRODUCT_VIEW_KINDS = new Set(["agent", "tool", "skill", "memory"]);
|
|
2
3
|
const STRUCTURAL_EDGE_KINDS = new Set(["sequence", "contains"]);
|
|
4
|
+
const PRODUCT_VIEW_EDGE_KINDS = new Set(["sequence", "approval", "resume", "result"]);
|
|
5
|
+
const GENERIC_PRODUCT_TOOL_FAMILIES = new Set([
|
|
6
|
+
"ls",
|
|
7
|
+
"read-file",
|
|
8
|
+
"write-file",
|
|
9
|
+
"task",
|
|
10
|
+
"glob",
|
|
11
|
+
"grep",
|
|
12
|
+
"bash",
|
|
13
|
+
"sh",
|
|
14
|
+
"cat",
|
|
15
|
+
]);
|
|
3
16
|
function sanitizeMermaidId(value) {
|
|
4
17
|
return value.replace(/[^A-Za-z0-9_]/g, "_");
|
|
5
18
|
}
|
|
@@ -25,6 +38,15 @@ function renderNode(node, mermaidId) {
|
|
|
25
38
|
return ` ${mermaidId}["${label}"]`;
|
|
26
39
|
}
|
|
27
40
|
}
|
|
41
|
+
function renderAgentRoot(label, mermaidId) {
|
|
42
|
+
return ` ${mermaidId}["${escapeMermaidLabel(label)}"]`;
|
|
43
|
+
}
|
|
44
|
+
function renderStartNode(mermaidId) {
|
|
45
|
+
return ` ${mermaidId}(("Start"))`;
|
|
46
|
+
}
|
|
47
|
+
function renderEndNode(mermaidId) {
|
|
48
|
+
return ` ${mermaidId}(("End"))`;
|
|
49
|
+
}
|
|
28
50
|
function renderEdge(edge, fromId, toId) {
|
|
29
51
|
const connector = edge.kind === "retry" || edge.kind === "fallback" ? "-.->" : edge.kind === "resume" ? "==>" : "-->";
|
|
30
52
|
const label = edge.label ?? edge.condition ?? (edge.kind !== "sequence" && edge.kind !== "contains" ? edge.kind : "");
|
|
@@ -48,6 +70,133 @@ function collapseEdgePath(source, target, path) {
|
|
|
48
70
|
sourceEventIds,
|
|
49
71
|
};
|
|
50
72
|
}
|
|
73
|
+
function nodeIdentity(node) {
|
|
74
|
+
if (node.kind === "agent") {
|
|
75
|
+
const fromAgentId = typeof node.detail.fromAgentId === "string" ? node.detail.fromAgentId : "agent";
|
|
76
|
+
const toAgentId = node.agentId ?? "agent";
|
|
77
|
+
return `${node.kind}:${fromAgentId}:${toAgentId}`;
|
|
78
|
+
}
|
|
79
|
+
return `${node.kind}:${node.agentId ?? "-"}:${node.label}`;
|
|
80
|
+
}
|
|
81
|
+
function findCrossAgentBridgeNodeIds(nodes, edges) {
|
|
82
|
+
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
83
|
+
const incidentAgentIdsByNodeId = new Map();
|
|
84
|
+
for (const edge of edges) {
|
|
85
|
+
const fromNode = nodeById.get(edge.from);
|
|
86
|
+
const toNode = nodeById.get(edge.to);
|
|
87
|
+
if (!fromNode || !toNode) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (toNode.agentId) {
|
|
91
|
+
let agentIds = incidentAgentIdsByNodeId.get(fromNode.id);
|
|
92
|
+
if (!agentIds) {
|
|
93
|
+
agentIds = new Set();
|
|
94
|
+
incidentAgentIdsByNodeId.set(fromNode.id, agentIds);
|
|
95
|
+
}
|
|
96
|
+
agentIds.add(toNode.agentId);
|
|
97
|
+
}
|
|
98
|
+
if (fromNode.agentId) {
|
|
99
|
+
let agentIds = incidentAgentIdsByNodeId.get(toNode.id);
|
|
100
|
+
if (!agentIds) {
|
|
101
|
+
agentIds = new Set();
|
|
102
|
+
incidentAgentIdsByNodeId.set(toNode.id, agentIds);
|
|
103
|
+
}
|
|
104
|
+
agentIds.add(fromNode.agentId);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const bridgeNodeIds = new Set();
|
|
108
|
+
for (const node of nodes) {
|
|
109
|
+
if (node.kind === "agent") {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const agentIds = incidentAgentIdsByNodeId.get(node.id) ?? new Set();
|
|
113
|
+
if (node.agentId) {
|
|
114
|
+
agentIds.add(node.agentId);
|
|
115
|
+
}
|
|
116
|
+
if (agentIds.size > 1) {
|
|
117
|
+
bridgeNodeIds.add(node.id);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return bridgeNodeIds;
|
|
121
|
+
}
|
|
122
|
+
function findDelegationCarrierToolNodeIds(nodes) {
|
|
123
|
+
const delegationSourceEventIds = new Set(nodes
|
|
124
|
+
.filter((node) => node.kind === "agent")
|
|
125
|
+
.flatMap((node) => node.sourceEventIds));
|
|
126
|
+
return new Set(nodes
|
|
127
|
+
.filter((node) => node.kind === "tool" && node.sourceEventIds.some((id) => delegationSourceEventIds.has(id)))
|
|
128
|
+
.map((node) => node.id));
|
|
129
|
+
}
|
|
130
|
+
function selectProductViewNodes(nodes) {
|
|
131
|
+
const selected = new Map();
|
|
132
|
+
for (const node of nodes) {
|
|
133
|
+
const key = nodeIdentity(node);
|
|
134
|
+
const existing = selected.get(key);
|
|
135
|
+
if (!existing) {
|
|
136
|
+
selected.set(key, node);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const existingCompleted = existing.status === "completed";
|
|
140
|
+
const nodeCompleted = node.status === "completed";
|
|
141
|
+
const preferCompleted = !existingCompleted && nodeCompleted;
|
|
142
|
+
const preserveCompleted = existingCompleted && !nodeCompleted;
|
|
143
|
+
const preferLatest = !preserveCompleted && (existing.sequenceEnd ?? 0) <= (node.sequenceEnd ?? 0);
|
|
144
|
+
if (preferCompleted || preferLatest) {
|
|
145
|
+
selected.set(key, node);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return [...selected.values()].sort((left, right) => (left.sequenceStart ?? 0) - (right.sequenceStart ?? 0));
|
|
149
|
+
}
|
|
150
|
+
function trimToolNodesForProductView(nodes, delegatingAgentIds) {
|
|
151
|
+
const keptToolKeys = new Set();
|
|
152
|
+
const orderedToolKeysByAgentId = new Map();
|
|
153
|
+
const toolKeyByNodeId = new Map();
|
|
154
|
+
const genericToolKeysByAgentId = new Map();
|
|
155
|
+
for (const node of nodes) {
|
|
156
|
+
if (node.kind !== "tool") {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const agentId = node.agentId ?? "";
|
|
160
|
+
const toolName = (() => {
|
|
161
|
+
if (typeof node.detail.toolName === "string" && node.detail.toolName.trim().length > 0) {
|
|
162
|
+
return node.detail.toolName.trim();
|
|
163
|
+
}
|
|
164
|
+
const labelMatch = /^(?:Calling|Completed) tool (.+?)(?: \[[^\]]+\])?$/i.exec(node.label);
|
|
165
|
+
return labelMatch?.[1]?.trim() || node.label;
|
|
166
|
+
})();
|
|
167
|
+
const normalizedToolName = toolName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
168
|
+
const toolKey = `${agentId}:${normalizedToolName}`;
|
|
169
|
+
toolKeyByNodeId.set(node.id, toolKey);
|
|
170
|
+
if (GENERIC_PRODUCT_TOOL_FAMILIES.has(normalizedToolName)) {
|
|
171
|
+
let genericToolKeys = genericToolKeysByAgentId.get(agentId);
|
|
172
|
+
if (!genericToolKeys) {
|
|
173
|
+
genericToolKeys = new Set();
|
|
174
|
+
genericToolKeysByAgentId.set(agentId, genericToolKeys);
|
|
175
|
+
}
|
|
176
|
+
genericToolKeys.add(toolKey);
|
|
177
|
+
}
|
|
178
|
+
const toolKeys = orderedToolKeysByAgentId.get(agentId);
|
|
179
|
+
if (toolKeys) {
|
|
180
|
+
if (!toolKeys.includes(toolKey)) {
|
|
181
|
+
toolKeys.push(toolKey);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
orderedToolKeysByAgentId.set(agentId, [toolKey]);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const [agentId, toolKeys] of orderedToolKeysByAgentId.entries()) {
|
|
189
|
+
const genericToolKeys = genericToolKeysByAgentId.get(agentId) ?? new Set();
|
|
190
|
+
const preferredToolKeys = toolKeys.filter((toolKey) => !genericToolKeys.has(toolKey));
|
|
191
|
+
const selectedToolKeys = preferredToolKeys.length > 0
|
|
192
|
+
? preferredToolKeys.slice(-2)
|
|
193
|
+
: delegatingAgentIds.has(agentId)
|
|
194
|
+
? []
|
|
195
|
+
: toolKeys.slice(-2);
|
|
196
|
+
selectedToolKeys.forEach((toolKey) => keptToolKeys.add(toolKey));
|
|
197
|
+
}
|
|
198
|
+
return nodes.filter((node) => node.kind !== "tool" || keptToolKeys.has(toolKeyByNodeId.get(node.id) ?? ""));
|
|
199
|
+
}
|
|
51
200
|
function collectVisibleEdges(graph, visibleNodes, includedEdgeKinds) {
|
|
52
201
|
const visibleNodeIds = new Set(visibleNodes.map((node) => node.id));
|
|
53
202
|
const candidateEdges = graph.edges.filter((edge) => includedEdgeKinds ? includedEdgeKinds.has(edge.kind) : true);
|
|
@@ -94,30 +243,115 @@ function collectVisibleEdges(graph, visibleNodes, includedEdgeKinds) {
|
|
|
94
243
|
}
|
|
95
244
|
return visibleEdges;
|
|
96
245
|
}
|
|
246
|
+
function getRuntimeSurface(graph) {
|
|
247
|
+
const runtimeSurface = graph.metadata.runtimeSurface;
|
|
248
|
+
return Array.isArray(runtimeSurface) ? runtimeSurface : [];
|
|
249
|
+
}
|
|
97
250
|
export function exportFlowGraphToMermaid(graph, options = {}) {
|
|
98
251
|
const direction = options.direction ?? "TD";
|
|
99
252
|
const view = options.view ?? "product";
|
|
253
|
+
const productView = view === "product";
|
|
100
254
|
const includedKinds = options.includeKinds
|
|
101
255
|
? new Set(options.includeKinds)
|
|
102
256
|
: view === "debug"
|
|
103
257
|
? null
|
|
104
258
|
: PRODUCT_VIEW_KINDS;
|
|
105
|
-
const includedEdgeKinds = options.includeEdgeKinds
|
|
259
|
+
const includedEdgeKinds = options.includeEdgeKinds
|
|
260
|
+
? new Set(options.includeEdgeKinds)
|
|
261
|
+
: view === "debug"
|
|
262
|
+
? null
|
|
263
|
+
: new Set(PRODUCT_VIEW_EDGE_KINDS);
|
|
106
264
|
const includeGroups = options.includeGroups ?? true;
|
|
107
265
|
const includeDetails = options.includeDetails ?? false;
|
|
108
|
-
const
|
|
266
|
+
const allFilteredNodes = graph.nodes.filter((node) => {
|
|
109
267
|
if (!includeDetails && node.layer === "detail") {
|
|
110
268
|
return false;
|
|
111
269
|
}
|
|
112
270
|
return includedKinds ? includedKinds.has(node.kind) : true;
|
|
113
271
|
});
|
|
272
|
+
const hiddenDelegationNodes = productView
|
|
273
|
+
? allFilteredNodes.filter((node) => node.kind === "agent")
|
|
274
|
+
: [];
|
|
275
|
+
const delegationCarrierToolNodeIds = productView
|
|
276
|
+
? findDelegationCarrierToolNodeIds(allFilteredNodes)
|
|
277
|
+
: new Set();
|
|
278
|
+
const delegatingAgentIds = productView
|
|
279
|
+
? new Set(allFilteredNodes
|
|
280
|
+
.filter((node) => node.kind === "agent" && typeof node.detail.fromAgentId === "string")
|
|
281
|
+
.map((node) => node.detail.fromAgentId))
|
|
282
|
+
: new Set();
|
|
283
|
+
const filteredNodes = productView
|
|
284
|
+
? allFilteredNodes.filter((node) => node.kind !== "agent" && !delegationCarrierToolNodeIds.has(node.id))
|
|
285
|
+
: allFilteredNodes;
|
|
286
|
+
const initiallySelectedNodes = productView
|
|
287
|
+
? trimToolNodesForProductView(selectProductViewNodes(filteredNodes), delegatingAgentIds)
|
|
288
|
+
: filteredNodes;
|
|
289
|
+
let nodes = initiallySelectedNodes;
|
|
290
|
+
let edges = collectVisibleEdges(graph, nodes, includedEdgeKinds);
|
|
291
|
+
if (productView) {
|
|
292
|
+
const crossAgentBridgeNodeIds = findCrossAgentBridgeNodeIds(nodes, edges);
|
|
293
|
+
if (crossAgentBridgeNodeIds.size > 0) {
|
|
294
|
+
nodes = nodes.filter((node) => !crossAgentBridgeNodeIds.has(node.id));
|
|
295
|
+
edges = collectVisibleEdges(graph, nodes, includedEdgeKinds);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
114
299
|
const nodeIdSet = new Set(nodes.map((node) => node.id));
|
|
115
|
-
|
|
300
|
+
if (productView) {
|
|
301
|
+
edges = edges.filter((edge) => {
|
|
302
|
+
const fromNode = nodeById.get(edge.from);
|
|
303
|
+
const toNode = nodeById.get(edge.to);
|
|
304
|
+
if (!fromNode || !toNode) {
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
return !fromNode.agentId || !toNode.agentId || fromNode.agentId === toNode.agentId;
|
|
308
|
+
});
|
|
309
|
+
}
|
|
116
310
|
const lines = [`flowchart ${direction}`];
|
|
117
311
|
const mermaidIdByNodeId = new Map();
|
|
312
|
+
const initialAgentId = typeof graph.metadata.initialAgentId === "string" ? graph.metadata.initialAgentId : null;
|
|
313
|
+
const startNodeId = productView && initialAgentId ? "graph_start" : null;
|
|
314
|
+
const endNodeId = productView ? "graph_end" : null;
|
|
315
|
+
const agentRootIdByAgentId = new Map();
|
|
118
316
|
for (const node of nodes) {
|
|
119
317
|
mermaidIdByNodeId.set(node.id, sanitizeMermaidId(node.id));
|
|
120
318
|
}
|
|
319
|
+
if (productView) {
|
|
320
|
+
const seenAgentIds = new Set();
|
|
321
|
+
for (const node of allFilteredNodes) {
|
|
322
|
+
const agentId = node.agentId;
|
|
323
|
+
if (!agentId || seenAgentIds.has(agentId)) {
|
|
324
|
+
// noop
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
seenAgentIds.add(agentId);
|
|
328
|
+
agentRootIdByAgentId.set(agentId, sanitizeMermaidId(`agent_root:${agentId}`));
|
|
329
|
+
}
|
|
330
|
+
if (node.kind !== "agent") {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
const fromAgentId = typeof node.detail.fromAgentId === "string" ? node.detail.fromAgentId : null;
|
|
334
|
+
if (!fromAgentId || seenAgentIds.has(fromAgentId)) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
seenAgentIds.add(fromAgentId);
|
|
338
|
+
agentRootIdByAgentId.set(fromAgentId, sanitizeMermaidId(`agent_root:${fromAgentId}`));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
for (const [agentId, mermaidId] of agentRootIdByAgentId) {
|
|
342
|
+
const representative = nodes.find((node) => node.agentId === agentId
|
|
343
|
+
|| (node.kind === "agent" && typeof node.detail.fromAgentId === "string" && node.detail.fromAgentId === agentId));
|
|
344
|
+
const representativeName = representative?.agentId === agentId
|
|
345
|
+
? representative.agentName
|
|
346
|
+
: (typeof representative?.detail.fromAgentName === "string" ? representative.detail.fromAgentName : undefined);
|
|
347
|
+
lines.push(renderAgentRoot(representativeName ?? formatAgentName(agentId), mermaidId));
|
|
348
|
+
}
|
|
349
|
+
if (startNodeId) {
|
|
350
|
+
lines.push(renderStartNode(startNodeId));
|
|
351
|
+
}
|
|
352
|
+
if (endNodeId) {
|
|
353
|
+
lines.push(renderEndNode(endNodeId));
|
|
354
|
+
}
|
|
121
355
|
if (includeGroups) {
|
|
122
356
|
const groupedNodeIds = new Set();
|
|
123
357
|
for (const group of graph.groups) {
|
|
@@ -150,5 +384,81 @@ export function exportFlowGraphToMermaid(graph, options = {}) {
|
|
|
150
384
|
for (const edge of edges) {
|
|
151
385
|
lines.push(renderEdge(edge, mermaidIdByNodeId.get(edge.from), mermaidIdByNodeId.get(edge.to)));
|
|
152
386
|
}
|
|
387
|
+
if (productView) {
|
|
388
|
+
const handoffPairs = new Set();
|
|
389
|
+
for (const item of getRuntimeSurface(graph)) {
|
|
390
|
+
if (item.kind !== "agent") {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
const fromAgentId = item.ownerAgentId ?? null;
|
|
394
|
+
const toAgentId = item.agentId ?? null;
|
|
395
|
+
if (!fromAgentId || !toAgentId) {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
const fromRoot = agentRootIdByAgentId.get(fromAgentId);
|
|
399
|
+
const toRoot = agentRootIdByAgentId.get(toAgentId);
|
|
400
|
+
if (!fromRoot || !toRoot || fromRoot === toRoot) {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
const pairKey = `${fromAgentId}:${toAgentId}`;
|
|
404
|
+
if (handoffPairs.has(pairKey)) {
|
|
405
|
+
// noop
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
handoffPairs.add(pairKey);
|
|
409
|
+
lines.push(renderEdge({
|
|
410
|
+
id: `synthetic:handoff:${pairKey}`,
|
|
411
|
+
from: `agent_root:${fromAgentId}`,
|
|
412
|
+
to: `agent_root:${toAgentId}`,
|
|
413
|
+
kind: "spawn",
|
|
414
|
+
label: "handoff",
|
|
415
|
+
sourceEventIds: item.sourceEventId ? [item.sourceEventId] : [],
|
|
416
|
+
}, fromRoot, toRoot));
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (startNodeId && initialAgentId) {
|
|
421
|
+
const initialRootId = agentRootIdByAgentId.get(initialAgentId);
|
|
422
|
+
if (initialRootId) {
|
|
423
|
+
lines.push(` ${startNodeId} --> ${initialRootId}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
for (const [agentId, rootMermaidId] of agentRootIdByAgentId) {
|
|
427
|
+
const firstAgentStep = nodes.find((node) => node.agentId === agentId && node.kind !== "agent");
|
|
428
|
+
if (!firstAgentStep) {
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
const incomingDelegation = !productView
|
|
432
|
+
? hiddenDelegationNodes.find((node) => node.agentId === agentId)
|
|
433
|
+
: null;
|
|
434
|
+
if (incomingDelegation) {
|
|
435
|
+
lines.push(renderEdge({
|
|
436
|
+
id: `synthetic:${incomingDelegation.id}->agent-root:${agentId}`,
|
|
437
|
+
from: incomingDelegation.id,
|
|
438
|
+
to: `agent_root:${agentId}`,
|
|
439
|
+
kind: "spawn",
|
|
440
|
+
label: "spawn",
|
|
441
|
+
sourceEventIds: [],
|
|
442
|
+
}, mermaidIdByNodeId.get(incomingDelegation.id), rootMermaidId));
|
|
443
|
+
}
|
|
444
|
+
lines.push(renderEdge({
|
|
445
|
+
id: `synthetic:agent-root:${agentId}->${firstAgentStep.id}`,
|
|
446
|
+
from: `agent_root:${agentId}`,
|
|
447
|
+
to: firstAgentStep.id,
|
|
448
|
+
kind: "sequence",
|
|
449
|
+
sourceEventIds: [],
|
|
450
|
+
}, rootMermaidId, mermaidIdByNodeId.get(firstAgentStep.id)));
|
|
451
|
+
}
|
|
452
|
+
if (endNodeId) {
|
|
453
|
+
const finalVisibleStep = [...nodes]
|
|
454
|
+
.filter((node) => node.kind !== "agent")
|
|
455
|
+
.sort((left, right) => (right.sequenceEnd ?? right.sequenceStart ?? 0) - (left.sequenceEnd ?? left.sequenceStart ?? 0))[0];
|
|
456
|
+
const finalAnchorId = finalVisibleStep
|
|
457
|
+
? mermaidIdByNodeId.get(finalVisibleStep.id)
|
|
458
|
+
: (initialAgentId ? agentRootIdByAgentId.get(initialAgentId) : undefined);
|
|
459
|
+
if (finalAnchorId) {
|
|
460
|
+
lines.push(` ${finalAnchorId} --> ${endNodeId}`);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
153
463
|
return lines.join("\n");
|
|
154
464
|
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
const PRODUCT_VIEW_KINDS = new Set(["agent", "
|
|
1
|
+
const PRODUCT_VIEW_KINDS = new Set(["agent", "tool", "skill", "memory"]);
|
|
2
|
+
const GENERIC_PRODUCT_TOOL_FAMILIES = new Set([
|
|
3
|
+
"ls",
|
|
4
|
+
"read-file",
|
|
5
|
+
"write-file",
|
|
6
|
+
"task",
|
|
7
|
+
"glob",
|
|
8
|
+
"grep",
|
|
9
|
+
"bash",
|
|
10
|
+
"sh",
|
|
11
|
+
"cat",
|
|
12
|
+
]);
|
|
2
13
|
function sanitizeAlias(value) {
|
|
3
14
|
return value.replace(/[^A-Za-z0-9_]/g, "_");
|
|
4
15
|
}
|
|
@@ -12,13 +23,99 @@ function formatAgentParticipantLabel(node) {
|
|
|
12
23
|
}
|
|
13
24
|
return `Agent:${agentId}`;
|
|
14
25
|
}
|
|
26
|
+
function nodeIdentity(node) {
|
|
27
|
+
if (node.kind === "agent") {
|
|
28
|
+
const fromAgentId = typeof node.detail.fromAgentId === "string" ? node.detail.fromAgentId : "agent";
|
|
29
|
+
const toAgentId = node.agentId ?? "agent";
|
|
30
|
+
return `${node.kind}:${fromAgentId}:${toAgentId}`;
|
|
31
|
+
}
|
|
32
|
+
return `${node.kind}:${node.agentId ?? "-"}:${node.label}`;
|
|
33
|
+
}
|
|
34
|
+
function collapseProductNodes(nodes) {
|
|
35
|
+
const selected = new Map();
|
|
36
|
+
for (const node of nodes) {
|
|
37
|
+
const key = nodeIdentity(node);
|
|
38
|
+
const existing = selected.get(key);
|
|
39
|
+
if (!existing) {
|
|
40
|
+
selected.set(key, node);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const existingCompleted = existing.status === "completed";
|
|
44
|
+
const nodeCompleted = node.status === "completed";
|
|
45
|
+
const preferCompleted = !existingCompleted && nodeCompleted;
|
|
46
|
+
const preserveCompleted = existingCompleted && !nodeCompleted;
|
|
47
|
+
const preferLatest = !preserveCompleted && (existing.sequenceEnd ?? 0) <= (node.sequenceEnd ?? 0);
|
|
48
|
+
if (preferCompleted || preferLatest) {
|
|
49
|
+
selected.set(key, node);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return [...selected.values()].sort((left, right) => (left.sequenceStart ?? 0) - (right.sequenceStart ?? 0));
|
|
53
|
+
}
|
|
54
|
+
function findDelegationCarrierToolNodeIds(nodes) {
|
|
55
|
+
const delegationSourceEventIds = new Set(nodes
|
|
56
|
+
.filter((node) => node.kind === "agent")
|
|
57
|
+
.flatMap((node) => node.sourceEventIds));
|
|
58
|
+
return new Set(nodes
|
|
59
|
+
.filter((node) => node.kind === "tool" && node.sourceEventIds.some((id) => delegationSourceEventIds.has(id)))
|
|
60
|
+
.map((node) => node.id));
|
|
61
|
+
}
|
|
62
|
+
function trimToolNodesForProductView(nodes, delegatingAgentIds) {
|
|
63
|
+
const keptToolKeys = new Set();
|
|
64
|
+
const orderedToolKeysByAgentId = new Map();
|
|
65
|
+
const toolKeyByNodeId = new Map();
|
|
66
|
+
const genericToolKeysByAgentId = new Map();
|
|
67
|
+
for (const node of nodes) {
|
|
68
|
+
if (node.kind !== "tool") {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const agentId = node.agentId ?? "";
|
|
72
|
+
const toolName = (() => {
|
|
73
|
+
if (typeof node.detail.toolName === "string" && node.detail.toolName.trim().length > 0) {
|
|
74
|
+
return node.detail.toolName.trim();
|
|
75
|
+
}
|
|
76
|
+
const labelMatch = /^(?:Calling|Completed) tool (.+?)(?: \[[^\]]+\])?$/i.exec(node.label);
|
|
77
|
+
return labelMatch?.[1]?.trim() || node.label;
|
|
78
|
+
})();
|
|
79
|
+
const normalizedToolName = toolName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
80
|
+
const toolKey = `${agentId}:${normalizedToolName}`;
|
|
81
|
+
toolKeyByNodeId.set(node.id, toolKey);
|
|
82
|
+
if (GENERIC_PRODUCT_TOOL_FAMILIES.has(normalizedToolName)) {
|
|
83
|
+
let genericToolKeys = genericToolKeysByAgentId.get(agentId);
|
|
84
|
+
if (!genericToolKeys) {
|
|
85
|
+
genericToolKeys = new Set();
|
|
86
|
+
genericToolKeysByAgentId.set(agentId, genericToolKeys);
|
|
87
|
+
}
|
|
88
|
+
genericToolKeys.add(toolKey);
|
|
89
|
+
}
|
|
90
|
+
const toolKeys = orderedToolKeysByAgentId.get(agentId);
|
|
91
|
+
if (toolKeys) {
|
|
92
|
+
if (!toolKeys.includes(toolKey)) {
|
|
93
|
+
toolKeys.push(toolKey);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
orderedToolKeysByAgentId.set(agentId, [toolKey]);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (const [agentId, toolKeys] of orderedToolKeysByAgentId.entries()) {
|
|
101
|
+
const genericToolKeys = genericToolKeysByAgentId.get(agentId) ?? new Set();
|
|
102
|
+
const preferredToolKeys = toolKeys.filter((toolKey) => !genericToolKeys.has(toolKey));
|
|
103
|
+
const selectedToolKeys = preferredToolKeys.length > 0
|
|
104
|
+
? preferredToolKeys.slice(-2)
|
|
105
|
+
: delegatingAgentIds.has(agentId)
|
|
106
|
+
? []
|
|
107
|
+
: toolKeys.slice(-2);
|
|
108
|
+
selectedToolKeys.forEach((toolKey) => keptToolKeys.add(toolKey));
|
|
109
|
+
}
|
|
110
|
+
return nodes.filter((node) => node.kind !== "tool" || keptToolKeys.has(toolKeyByNodeId.get(node.id) ?? ""));
|
|
111
|
+
}
|
|
15
112
|
function selectNodes(graph, options) {
|
|
16
113
|
const includedKinds = options.includeKinds
|
|
17
114
|
? new Set(options.includeKinds)
|
|
18
115
|
: options.view === "debug"
|
|
19
116
|
? null
|
|
20
117
|
: PRODUCT_VIEW_KINDS;
|
|
21
|
-
|
|
118
|
+
const filtered = graph.nodes.filter((node) => {
|
|
22
119
|
if (node.layer === "detail") {
|
|
23
120
|
return false;
|
|
24
121
|
}
|
|
@@ -32,8 +129,19 @@ function selectNodes(graph, options) {
|
|
|
32
129
|
|| node.kind === "llm"
|
|
33
130
|
|| node.kind === "tool"
|
|
34
131
|
|| node.kind === "skill"
|
|
132
|
+
|| node.kind === "memory"
|
|
35
133
|
|| node.kind === "artifact";
|
|
36
134
|
});
|
|
135
|
+
if (options.view === "debug") {
|
|
136
|
+
return filtered;
|
|
137
|
+
}
|
|
138
|
+
const collapsedNodes = collapseProductNodes(filtered);
|
|
139
|
+
const delegationCarrierToolNodeIds = findDelegationCarrierToolNodeIds(collapsedNodes);
|
|
140
|
+
const delegatingAgentIds = new Set(collapsedNodes
|
|
141
|
+
.filter((node) => node.kind === "agent" && typeof node.detail.fromAgentId === "string")
|
|
142
|
+
.map((node) => node.detail.fromAgentId));
|
|
143
|
+
const productNodes = collapsedNodes.filter((node) => node.kind === "agent" || !delegationCarrierToolNodeIds.has(node.id));
|
|
144
|
+
return trimToolNodesForProductView(productNodes, delegatingAgentIds);
|
|
37
145
|
}
|
|
38
146
|
function getParticipantsForNode(node) {
|
|
39
147
|
const runtimeParticipant = { id: "runtime", alias: "Runtime", label: "Runtime" };
|
|
@@ -90,6 +198,12 @@ function getParticipantsForNode(node) {
|
|
|
90
198
|
{ id: "skill", alias: "Skill", label: "Skill" },
|
|
91
199
|
];
|
|
92
200
|
}
|
|
201
|
+
if (node.kind === "memory") {
|
|
202
|
+
return [
|
|
203
|
+
agentParticipant,
|
|
204
|
+
{ id: "memory", alias: "Memory", label: "Memory" },
|
|
205
|
+
];
|
|
206
|
+
}
|
|
93
207
|
if (node.kind === "recovery") {
|
|
94
208
|
return [
|
|
95
209
|
runtimeParticipant,
|
|
@@ -152,13 +266,26 @@ function renderArrowForNode(node) {
|
|
|
152
266
|
dashed: node.status === "completed" || node.status === "failed",
|
|
153
267
|
};
|
|
154
268
|
}
|
|
269
|
+
if (node.kind === "memory") {
|
|
270
|
+
return {
|
|
271
|
+
from: sanitizeAlias(`Agent_${node.agentId ?? "agent"}`),
|
|
272
|
+
to: "Memory",
|
|
273
|
+
text: node.label,
|
|
274
|
+
dashed: node.status === "completed" || node.status === "failed",
|
|
275
|
+
};
|
|
276
|
+
}
|
|
155
277
|
if (node.kind === "artifact") {
|
|
156
278
|
return { from: "Runtime", to: "ArtifactStore", text: node.label };
|
|
157
279
|
}
|
|
158
280
|
return { from: "Runtime", to: sanitizeAlias(`Agent_${node.agentId ?? "agent"}`), text: node.label };
|
|
159
281
|
}
|
|
282
|
+
function getRuntimeSurface(graph) {
|
|
283
|
+
const runtimeSurface = graph.metadata.runtimeSurface;
|
|
284
|
+
return Array.isArray(runtimeSurface) ? runtimeSurface : [];
|
|
285
|
+
}
|
|
160
286
|
export function exportFlowGraphToSequenceMermaid(graph, options = {}) {
|
|
161
287
|
const nodes = selectNodes(graph, options);
|
|
288
|
+
const productView = (options.view ?? "product") === "product";
|
|
162
289
|
const lines = ["sequenceDiagram"];
|
|
163
290
|
const participants = new Map();
|
|
164
291
|
for (const node of nodes) {
|
|
@@ -174,5 +301,25 @@ export function exportFlowGraphToSequenceMermaid(graph, options = {}) {
|
|
|
174
301
|
const connector = arrow.dashed ? "-->>" : "->>";
|
|
175
302
|
lines.push(` ${arrow.from}${connector}${arrow.to}: ${escapeLabel(node.label)}`);
|
|
176
303
|
}
|
|
304
|
+
if (productView) {
|
|
305
|
+
const seenReturnPairs = new Set();
|
|
306
|
+
for (const item of getRuntimeSurface(graph)) {
|
|
307
|
+
if (item.kind !== "agent" || item.status !== "completed") {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
const fromAgentId = item.ownerAgentId ?? null;
|
|
311
|
+
const toAgentId = item.agentId ?? null;
|
|
312
|
+
if (!fromAgentId || !toAgentId) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
const pairKey = `${fromAgentId}:${toAgentId}`;
|
|
316
|
+
if (seenReturnPairs.has(pairKey)) {
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
seenReturnPairs.add(pairKey);
|
|
320
|
+
const fromAgentName = item.ownerAgentName ?? fromAgentId;
|
|
321
|
+
lines.push(` ${sanitizeAlias(`Agent_${toAgentId}`)}-->>${sanitizeAlias(`Agent_${fromAgentId}`)}: Return to ${escapeLabel(fromAgentName)}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
177
324
|
return lines.join("\n");
|
|
178
325
|
}
|
package/dist/flow/types.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import type { HarnessEvent, RuntimeTimelineItem } from "../contracts/types.js";
|
|
2
2
|
import type { UpstreamTimelineProjection } from "../upstream-events.js";
|
|
3
|
+
export type BuildFlowGraphRuntimeEvent = HarnessEvent | {
|
|
4
|
+
eventId: string;
|
|
5
|
+
eventType: string;
|
|
6
|
+
timestamp: string;
|
|
7
|
+
sessionId: string;
|
|
8
|
+
requestId: string;
|
|
9
|
+
sequence: number;
|
|
10
|
+
source: "runtime" | "policy" | "surface" | "worker" | (string & {});
|
|
11
|
+
payload: Record<string, unknown>;
|
|
12
|
+
};
|
|
3
13
|
export type FlowNodeLayer = "runtime" | "execution" | "attempt" | "detail";
|
|
4
14
|
export type FlowNodeKind = "run" | "agent" | "queue" | "approval" | "recovery" | "artifact" | "llm" | "tool" | "skill" | "memory" | "chain" | "result" | "thinking" | "other";
|
|
5
15
|
export type FlowNodeStatus = "pending" | "started" | "completed" | "failed" | "waiting" | "resolved" | "skipped";
|
|
@@ -62,7 +72,7 @@ export type BuildFlowGraphInput = {
|
|
|
62
72
|
threadId?: string;
|
|
63
73
|
runId?: string;
|
|
64
74
|
scope?: FlowGraph["scope"];
|
|
65
|
-
runtimeEvents?: readonly
|
|
75
|
+
runtimeEvents?: readonly BuildFlowGraphRuntimeEvent[];
|
|
66
76
|
runtimeTimeline?: readonly RuntimeTimelineItem[];
|
|
67
77
|
upstreamEvents?: readonly unknown[];
|
|
68
78
|
upstreamProjections?: readonly UpstreamTimelineProjection[];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
export { AgentHarnessAcpServer, AgentHarnessRuntime,
|
|
1
|
+
export { AgentHarnessAcpServer, AgentHarnessRuntime, cancelRun, createAgentHarness, createAcpServer, createAcpStdioClient, createRuntimeMcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, exportFlow, exportSequence, exportRequestPackage, exportSessionPackage, replayEvaluationBundle, getArtifact, getAgent, getApproval, getOperatorOverview, getRequest, getHealth, listMemories, listRequestTraceItems, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listRequestEvents, listSessionSummaries, listSessions, memorize, normalizeUserChatInput, request, recall, removeMemory, resolveApproval, serveA2aHttp, serveAcpHttp, serveAcpStdio, serveAgUiHttp, serveRuntimeMcpOverStdio, serveToolsOverStdio, subscribe, stop, updateMemory, } from "./api.js";
|
|
2
2
|
export type { AcpApproval, AcpArtifact, AcpEventNotification, AcpJsonRpcError, AcpJsonRpcRequest, AcpJsonRpcResponse, AcpJsonRpcSuccess, AcpRequestRecord, AcpRunRequestParams, AcpServerCapabilities, AcpSessionRecord, AcpStdioClient, AcpStdioClientOptions, } from "./acp.js";
|
|
3
|
-
export type { Approval, ListMemoriesInput, ListMemoriesResult, MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, NormalizeUserChatInputOptions, OperatorOverview, PublicRunListeners, RequestArtifactListing, RequestEvent, RequestEventType, RequestPackage, RequestPackageInput, RequestResult,
|
|
4
|
-
export type { BuildFlowGraphInput, FlowEdge, FlowEdgeKind, FlowGraph, FlowGraphMermaidOptions, FlowGraphSequenceMermaidOptions, FlowGroup, FlowGroupKind, FlowNode, FlowNodeKind, FlowNodeLayer, FlowNodeStatus, } from "./flow/index.js";
|
|
3
|
+
export type { Approval, ListMemoriesInput, ListMemoriesResult, MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, NormalizeUserChatInputOptions, OperatorOverview, PublicRunListeners, RequestArtifactListing, RequestEvent, RequestEventType, RequestPackage, RequestPackageInput, RequestFlowGraphInput, RequestResult, RequestTraceItem, RecallInput, RecallResult, RemoveMemoryInput, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeEvaluationReplayResult, SessionListSummary, RuntimeSessionPackage, RuntimeSessionPackageInput, UpdateMemoryInput, UserChatInput, UserChatMessage, } from "./api.js";
|
|
5
4
|
export type { A2aAgentCard, A2aHttpServer, A2aHttpServerOptions, A2aTask, A2aTaskState, AcpHttpServer, AcpHttpServerOptions, AcpStdioServer, AcpStdioServerOptions, AgUiEvent, AgUiHttpServer, AgUiHttpServerOptions, AgUiRunAgentInput, } from "./api.js";
|
|
6
5
|
export type { RuntimeMcpServerOptions, ToolMcpServerOptions } from "./mcp.js";
|
|
7
6
|
export { tool } from "./tools.js";
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AgentHarnessAcpServer, AgentHarnessRuntime,
|
|
1
|
+
export { AgentHarnessAcpServer, AgentHarnessRuntime, cancelRun, createAgentHarness, createAcpServer, createAcpStdioClient, createRuntimeMcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, exportFlow, exportSequence, exportRequestPackage, exportSessionPackage, replayEvaluationBundle, getArtifact, getAgent, getApproval, getOperatorOverview, getRequest, getHealth, listMemories, listRequestTraceItems, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listRequestEvents, listSessionSummaries, listSessions, memorize, normalizeUserChatInput, request, recall, removeMemory, resolveApproval, serveA2aHttp, serveAcpHttp, serveAcpStdio, serveAgUiHttp, serveRuntimeMcpOverStdio, serveToolsOverStdio, subscribe, stop, updateMemory, } from "./api.js";
|
|
2
2
|
export { tool } from "./tools.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.251";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.251";
|
|
@@ -15,6 +15,7 @@ export declare class FilePersistence implements RuntimePersistence {
|
|
|
15
15
|
private approvalIndexPath;
|
|
16
16
|
private runQueuePath;
|
|
17
17
|
private runControlPath;
|
|
18
|
+
private traceItemsPath;
|
|
18
19
|
initialize(): Promise<void>;
|
|
19
20
|
threadDir(threadId: string): string;
|
|
20
21
|
runDir(threadId: string, runId: string): string;
|
|
@@ -61,9 +62,9 @@ export declare class FilePersistence implements RuntimePersistence {
|
|
|
61
62
|
currentAgentId?: string | null;
|
|
62
63
|
delegationChain?: string[];
|
|
63
64
|
runtimeSnapshot?: RunSummary["runtimeSnapshot"] | null;
|
|
64
|
-
upstreamEvents?: unknown[];
|
|
65
|
-
appendUpstreamEvent?: unknown;
|
|
66
65
|
}): Promise<void>;
|
|
66
|
+
appendRunTraceItem(threadId: string, runId: string, item: unknown): Promise<void>;
|
|
67
|
+
listRunTraceItems(threadId: string, runId: string): Promise<unknown[]>;
|
|
67
68
|
deleteThread(threadId: string): Promise<boolean>;
|
|
68
69
|
saveRunRequest(threadId: string, runId: string, request: PersistedRunRequest): Promise<void>;
|
|
69
70
|
getRunRequest(threadId: string, runId: string): Promise<PersistedRunRequest | null>;
|