@botbotgo/agent-harness 0.0.251 → 0.0.253

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.
Files changed (46) hide show
  1. package/README.md +13 -14
  2. package/README.zh.md +11 -12
  3. package/dist/api.d.ts +13 -6
  4. package/dist/api.js +70 -6
  5. package/dist/config/agents/direct.yaml +3 -3
  6. package/dist/config/agents/orchestra.yaml +3 -3
  7. package/dist/config/catalogs/stores.yaml +3 -9
  8. package/dist/config/runtime/workspace.yaml +1 -2
  9. package/dist/contracts/runtime.d.ts +9 -14
  10. package/dist/flow/build-flow-graph.js +198 -67
  11. package/dist/flow/export-mermaid.js +314 -4
  12. package/dist/flow/export-sequence-mermaid.js +149 -2
  13. package/dist/flow/types.d.ts +11 -1
  14. package/dist/index.d.ts +2 -3
  15. package/dist/index.js +1 -1
  16. package/dist/package-version.d.ts +1 -1
  17. package/dist/package-version.js +1 -1
  18. package/dist/persistence/file-store.d.ts +3 -2
  19. package/dist/persistence/file-store.js +34 -8
  20. package/dist/persistence/sqlite-store.d.ts +2 -2
  21. package/dist/persistence/sqlite-store.js +64 -11
  22. package/dist/persistence/types.d.ts +3 -3
  23. package/dist/protocol/a2a/http.js +2 -4
  24. package/dist/resource/isolation.js +30 -2
  25. package/dist/runtime/harness/events/streaming.js +8 -8
  26. package/dist/runtime/harness/run/inspection.d.ts +2 -0
  27. package/dist/runtime/harness/run/inspection.js +91 -46
  28. package/dist/runtime/harness/run/stream-run.d.ts +2 -2
  29. package/dist/runtime/harness/run/stream-run.js +34 -23
  30. package/dist/runtime/harness/run/surface-semantics.d.ts +14 -0
  31. package/dist/runtime/harness/run/surface-semantics.js +106 -0
  32. package/dist/runtime/harness/run/thread-records.js +2 -34
  33. package/dist/runtime/harness/system/store.d.ts +6 -4
  34. package/dist/runtime/harness/system/store.js +76 -42
  35. package/dist/runtime/harness.js +5 -7
  36. package/dist/runtime/maintenance/checkpoint-maintenance.js +4 -119
  37. package/dist/runtime/maintenance/index.d.ts +0 -1
  38. package/dist/runtime/maintenance/index.js +0 -1
  39. package/dist/runtime/support/runtime-factories.js +2 -42
  40. package/dist/upstream-events.js +14 -0
  41. package/dist/utils/fs.js +3 -0
  42. package/package.json +1 -3
  43. package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.d.ts +0 -9
  44. package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.js +0 -39
  45. package/dist/runtime/support/sqlite-drivers.d.ts +0 -12
  46. package/dist/runtime/support/sqlite-drivers.js +0 -24
@@ -1,5 +1,18 @@
1
- const PRODUCT_VIEW_KINDS = new Set(["agent", "llm", "tool", "skill"]);
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 ? new Set(options.includeEdgeKinds) : null;
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 nodes = graph.nodes.filter((node) => {
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
- const edges = collectVisibleEdges(graph, nodes, includedEdgeKinds);
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", "llm", "tool", "skill"]);
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
- return graph.nodes.filter((node) => {
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
  }
@@ -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 HarnessEvent[];
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, buildFlowGraph, cancelRun, createAgentHarness, createAcpServer, createAcpStdioClient, createRuntimeMcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, exportRequestPackage, exportSessionPackage, replayEvaluationBundle, getArtifact, getAgent, getApproval, getOperatorOverview, getRequest, getHealth, listMemories, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listRequestEvents, listSessionSummaries, listSessions, memorize, normalizeUserChatInput, request, recall, removeMemory, resolveApproval, serveA2aHttp, serveAcpHttp, serveAcpStdio, serveAgUiHttp, serveRuntimeMcpOverStdio, serveToolsOverStdio, subscribe, stop, updateMemory, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid, } from "./api.js";
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, RequestUpstreamEventItem, RecallInput, RecallResult, RemoveMemoryInput, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeEvaluationReplayResult, SessionListSummary, RuntimeSessionPackage, RuntimeSessionPackageInput, UpdateMemoryInput, UserChatInput, UserChatMessage, } from "./api.js";
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, buildFlowGraph, cancelRun, createAgentHarness, createAcpServer, createAcpStdioClient, createRuntimeMcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, exportRequestPackage, exportSessionPackage, replayEvaluationBundle, getArtifact, getAgent, getApproval, getOperatorOverview, getRequest, getHealth, listMemories, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listRequestEvents, listSessionSummaries, listSessions, memorize, normalizeUserChatInput, request, recall, removeMemory, resolveApproval, serveA2aHttp, serveAcpHttp, serveAcpStdio, serveAgUiHttp, serveRuntimeMcpOverStdio, serveToolsOverStdio, subscribe, stop, updateMemory, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid, } from "./api.js";
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.250";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.252";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.250";
1
+ export const AGENT_HARNESS_VERSION = "0.0.252";
@@ -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>;