@botbotgo/agent-harness 0.0.245 → 0.0.247

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.
@@ -1,4 +1,5 @@
1
1
  const PRODUCT_VIEW_KINDS = new Set(["agent", "llm", "tool", "skill"]);
2
+ const STRUCTURAL_EDGE_KINDS = new Set(["sequence", "contains"]);
2
3
  function sanitizeMermaidId(value) {
3
4
  return value.replace(/[^A-Za-z0-9_]/g, "_");
4
5
  }
@@ -31,6 +32,68 @@ function renderEdge(edge, fromId, toId) {
31
32
  ? ` ${fromId} ${connector}|"${escapeMermaidLabel(label)}"| ${toId}`
32
33
  : ` ${fromId} ${connector} ${toId}`;
33
34
  }
35
+ function edgeKey(edge) {
36
+ return `${edge.from}::${edge.to}::${edge.kind}::${edge.label ?? ""}::${edge.condition ?? ""}`;
37
+ }
38
+ function collapseEdgePath(source, target, path) {
39
+ const representative = [...path].reverse().find((edge) => !STRUCTURAL_EDGE_KINDS.has(edge.kind) || edge.label || edge.condition);
40
+ const sourceEventIds = [...new Set(path.flatMap((edge) => edge.sourceEventIds))];
41
+ return {
42
+ id: `collapsed:${source}:${target}:${sourceEventIds.join(",")}`,
43
+ from: source,
44
+ to: target,
45
+ kind: representative?.kind ?? "sequence",
46
+ label: representative?.label,
47
+ condition: representative?.condition,
48
+ sourceEventIds,
49
+ };
50
+ }
51
+ function collectVisibleEdges(graph, visibleNodes, includedEdgeKinds) {
52
+ const visibleNodeIds = new Set(visibleNodes.map((node) => node.id));
53
+ const candidateEdges = graph.edges.filter((edge) => includedEdgeKinds ? includedEdgeKinds.has(edge.kind) : true);
54
+ const outgoingByNodeId = new Map();
55
+ for (const edge of candidateEdges) {
56
+ const outgoing = outgoingByNodeId.get(edge.from);
57
+ if (outgoing) {
58
+ outgoing.push(edge);
59
+ }
60
+ else {
61
+ outgoingByNodeId.set(edge.from, [edge]);
62
+ }
63
+ }
64
+ const visibleEdges = candidateEdges.filter((edge) => visibleNodeIds.has(edge.from) && visibleNodeIds.has(edge.to));
65
+ const seenEdgeKeys = new Set(visibleEdges.map((edge) => edgeKey(edge)));
66
+ for (const node of visibleNodes) {
67
+ const queue = (outgoingByNodeId.get(node.id) ?? [])
68
+ .filter((edge) => !visibleNodeIds.has(edge.to))
69
+ .map((edge) => ({ nodeId: edge.to, path: [edge] }));
70
+ const visitedHiddenNodes = new Set();
71
+ while (queue.length > 0) {
72
+ const current = queue.shift();
73
+ if (visitedHiddenNodes.has(current.nodeId)) {
74
+ continue;
75
+ }
76
+ visitedHiddenNodes.add(current.nodeId);
77
+ for (const edge of outgoingByNodeId.get(current.nodeId) ?? []) {
78
+ const nextPath = [...current.path, edge];
79
+ if (visibleNodeIds.has(edge.to)) {
80
+ if (edge.to === node.id) {
81
+ continue;
82
+ }
83
+ const collapsed = collapseEdgePath(node.id, edge.to, nextPath);
84
+ const key = edgeKey(collapsed);
85
+ if (!seenEdgeKeys.has(key)) {
86
+ seenEdgeKeys.add(key);
87
+ visibleEdges.push(collapsed);
88
+ }
89
+ continue;
90
+ }
91
+ queue.push({ nodeId: edge.to, path: nextPath });
92
+ }
93
+ }
94
+ }
95
+ return visibleEdges;
96
+ }
34
97
  export function exportFlowGraphToMermaid(graph, options = {}) {
35
98
  const direction = options.direction ?? "TD";
36
99
  const view = options.view ?? "product";
@@ -49,12 +112,7 @@ export function exportFlowGraphToMermaid(graph, options = {}) {
49
112
  return includedKinds ? includedKinds.has(node.kind) : true;
50
113
  });
51
114
  const nodeIdSet = new Set(nodes.map((node) => node.id));
52
- const edges = graph.edges.filter((edge) => {
53
- if (!nodeIdSet.has(edge.from) || !nodeIdSet.has(edge.to)) {
54
- return false;
55
- }
56
- return includedEdgeKinds ? includedEdgeKinds.has(edge.kind) : true;
57
- });
115
+ const edges = collectVisibleEdges(graph, nodes, includedEdgeKinds);
58
116
  const lines = [`flowchart ${direction}`];
59
117
  const mermaidIdByNodeId = new Map();
60
118
  for (const node of nodes) {
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.244";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.246";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.244";
1
+ export const AGENT_HARNESS_VERSION = "0.0.246";
@@ -158,6 +158,13 @@ export async function* streamHarnessRun(options) {
158
158
  }
159
159
  }
160
160
  await options.appendAssistantMessage(options.threadId, options.runId, assistantOutput);
161
+ const completedEvent = await options.setRunStateAndEmit(options.threadId, options.runId, 6, "completed", {
162
+ previousState: "running",
163
+ });
164
+ yield {
165
+ type: "event",
166
+ event: completedEvent,
167
+ };
161
168
  yield {
162
169
  type: "result",
163
170
  result: {
@@ -169,12 +176,6 @@ export async function* streamHarnessRun(options) {
169
176
  finalMessageText: assistantOutput,
170
177
  },
171
178
  };
172
- yield {
173
- type: "event",
174
- event: await options.setRunStateAndEmit(options.threadId, options.runId, 6, "completed", {
175
- previousState: "running",
176
- }),
177
- };
178
179
  }
179
180
  catch (error) {
180
181
  const shouldRetryAfterStreamingCompatibilityError = streamActivityObserved &&
@@ -251,6 +252,13 @@ export async function* streamHarnessRun(options) {
251
252
  if (Array.isArray(actual.contentBlocks) && actual.contentBlocks.length > 0) {
252
253
  yield createContentBlocksItem(options.threadId, options.runId, options.selectedAgentId, actual.contentBlocks);
253
254
  }
255
+ const terminalEvent = await options.setRunStateAndEmit(options.threadId, options.runId, 6, actual.state, {
256
+ previousState: "running",
257
+ });
258
+ yield {
259
+ type: "event",
260
+ event: terminalEvent,
261
+ };
254
262
  yield {
255
263
  type: "result",
256
264
  result: {
@@ -264,12 +272,6 @@ export async function* streamHarnessRun(options) {
264
272
  },
265
273
  },
266
274
  };
267
- yield {
268
- type: "event",
269
- event: await options.setRunStateAndEmit(options.threadId, options.runId, 6, actual.state, {
270
- previousState: "running",
271
- }),
272
- };
273
275
  return;
274
276
  }
275
277
  catch (invokeError) {
@@ -31,8 +31,17 @@ function readEventContext(event) {
31
31
  function containsSemanticHint(values, hint) {
32
32
  return values.some((value) => hint.test(value));
33
33
  }
34
+ function normalizeSemanticHint(value) {
35
+ return value
36
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
37
+ .replace(/[.:/]+/g, " ")
38
+ .replace(/[_-]+/g, " ")
39
+ .replace(/\s+/g, " ")
40
+ .trim()
41
+ .toLowerCase();
42
+ }
34
43
  function classifyStepCategory(context) {
35
- const hints = [context.name, context.runType, ...context.tags, ...context.ns].map((value) => value.toLowerCase());
44
+ const hints = [context.name, context.runType, ...context.tags, ...context.ns].map(normalizeSemanticHint);
36
45
  if (containsSemanticHint(hints, /\b(skill|skills)\b/)) {
37
46
  return "skill";
38
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.245",
3
+ "version": "0.0.247",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",