agentopology 0.2.5 → 0.4.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/README.md +121 -7
- package/dist/analyzer/__tests__/analyzer.test.js +36 -9
- package/dist/analyzer/__tests__/analyzer.test.js.map +1 -1
- package/dist/analyzer/__tests__/observability-gap.test.js +40 -10
- package/dist/analyzer/__tests__/observability-gap.test.js.map +1 -1
- package/dist/analyzer/index.d.ts +2 -1
- package/dist/analyzer/index.d.ts.map +1 -1
- package/dist/analyzer/index.js +1 -1
- package/dist/analyzer/index.js.map +1 -1
- package/dist/bindings/__tests__/bindings.test.js +20 -5
- package/dist/bindings/__tests__/bindings.test.js.map +1 -1
- package/dist/bindings/__tests__/claude-workflow.test.js +12 -3
- package/dist/bindings/__tests__/claude-workflow.test.js.map +1 -1
- package/dist/bindings/__tests__/defaults-agreement.test.d.ts +16 -0
- package/dist/bindings/__tests__/defaults-agreement.test.d.ts.map +1 -0
- package/dist/bindings/__tests__/defaults-agreement.test.js +80 -0
- package/dist/bindings/__tests__/defaults-agreement.test.js.map +1 -0
- package/dist/bindings/__tests__/observability.test.js +8 -2
- package/dist/bindings/__tests__/observability.test.js.map +1 -1
- package/dist/bindings/__tests__/stub-generation.test.js +8 -2
- package/dist/bindings/__tests__/stub-generation.test.js.map +1 -1
- package/dist/bindings/claude-code.d.ts.map +1 -1
- package/dist/bindings/claude-code.js +102 -3
- package/dist/bindings/claude-code.js.map +1 -1
- package/dist/bindings/claude-workflow.d.ts.map +1 -1
- package/dist/bindings/claude-workflow.js +2 -1
- package/dist/bindings/claude-workflow.js.map +1 -1
- package/dist/bindings/codex.d.ts.map +1 -1
- package/dist/bindings/codex.js +6 -2
- package/dist/bindings/codex.js.map +1 -1
- package/dist/bindings/gemini-cli.d.ts.map +1 -1
- package/dist/bindings/gemini-cli.js +2 -1
- package/dist/bindings/gemini-cli.js.map +1 -1
- package/dist/bindings/kiro.d.ts.map +1 -1
- package/dist/bindings/kiro.js +2 -1
- package/dist/bindings/kiro.js.map +1 -1
- package/dist/bindings/openclaw.d.ts.map +1 -1
- package/dist/bindings/openclaw.js +2 -1
- package/dist/bindings/openclaw.js.map +1 -1
- package/dist/cli/__tests__/cli.test.d.ts +12 -0
- package/dist/cli/__tests__/cli.test.d.ts.map +1 -0
- package/dist/cli/__tests__/cli.test.js +171 -0
- package/dist/cli/__tests__/cli.test.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +344 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/docs/content.d.ts.map +1 -1
- package/dist/docs/content.js +112 -0
- package/dist/docs/content.js.map +1 -1
- package/dist/exporters/ascii.d.ts +13 -0
- package/dist/exporters/ascii.d.ts.map +1 -0
- package/dist/exporters/ascii.js +26 -0
- package/dist/exporters/ascii.js.map +1 -0
- package/dist/exporters/brief.d.ts +14 -0
- package/dist/exporters/brief.d.ts.map +1 -0
- package/dist/exporters/brief.js +37 -0
- package/dist/exporters/brief.js.map +1 -0
- package/dist/exporters/index.d.ts +2 -0
- package/dist/exporters/index.d.ts.map +1 -1
- package/dist/exporters/index.js +6 -0
- package/dist/exporters/index.js.map +1 -1
- package/dist/exporters/markdown.d.ts.map +1 -1
- package/dist/exporters/markdown.js +2 -1
- package/dist/exporters/markdown.js.map +1 -1
- package/dist/exporters/mermaid.d.ts.map +1 -1
- package/dist/exporters/mermaid.js +2 -1
- package/dist/exporters/mermaid.js.map +1 -1
- package/dist/import/__tests__/import.test.js +8 -2
- package/dist/import/__tests__/import.test.js.map +1 -1
- package/dist/parser/__tests__/parser.test.js +1553 -207
- package/dist/parser/__tests__/parser.test.js.map +1 -1
- package/dist/parser/__tests__/spec-sync.test.d.ts +10 -0
- package/dist/parser/__tests__/spec-sync.test.d.ts.map +1 -0
- package/dist/parser/__tests__/spec-sync.test.js +60 -0
- package/dist/parser/__tests__/spec-sync.test.js.map +1 -0
- package/dist/parser/ast.d.ts +39 -0
- package/dist/parser/ast.d.ts.map +1 -1
- package/dist/parser/index.d.ts +50 -6
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +241 -30
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/validator.d.ts +7 -1
- package/dist/parser/validator.d.ts.map +1 -1
- package/dist/parser/validator.js +143 -5
- package/dist/parser/validator.js.map +1 -1
- package/dist/plan/__tests__/brief.test.d.ts +2 -0
- package/dist/plan/__tests__/brief.test.d.ts.map +1 -0
- package/dist/plan/__tests__/brief.test.js +442 -0
- package/dist/plan/__tests__/brief.test.js.map +1 -0
- package/dist/plan/brief.d.ts +207 -0
- package/dist/plan/brief.d.ts.map +1 -0
- package/dist/plan/brief.js +342 -0
- package/dist/plan/brief.js.map +1 -0
- package/dist/plan/render.d.ts +20 -0
- package/dist/plan/render.d.ts.map +1 -0
- package/dist/plan/render.js +425 -0
- package/dist/plan/render.js.map +1 -0
- package/dist/render/__tests__/ascii.test.d.ts +2 -0
- package/dist/render/__tests__/ascii.test.d.ts.map +1 -0
- package/dist/render/__tests__/ascii.test.js +171 -0
- package/dist/render/__tests__/ascii.test.js.map +1 -0
- package/dist/render/ascii.d.ts +23 -0
- package/dist/render/ascii.d.ts.map +1 -0
- package/dist/render/ascii.js +178 -0
- package/dist/render/ascii.js.map +1 -0
- package/dist/resolve/__tests__/defaults.test.d.ts +2 -0
- package/dist/resolve/__tests__/defaults.test.d.ts.map +1 -0
- package/dist/resolve/__tests__/defaults.test.js +109 -0
- package/dist/resolve/__tests__/defaults.test.js.map +1 -0
- package/dist/resolve/__tests__/order.test.d.ts +2 -0
- package/dist/resolve/__tests__/order.test.d.ts.map +1 -0
- package/dist/resolve/__tests__/order.test.js +238 -0
- package/dist/resolve/__tests__/order.test.js.map +1 -0
- package/dist/resolve/__tests__/phase.test.d.ts +12 -0
- package/dist/resolve/__tests__/phase.test.d.ts.map +1 -0
- package/dist/resolve/__tests__/phase.test.js +81 -0
- package/dist/resolve/__tests__/phase.test.js.map +1 -0
- package/dist/resolve/defaults.d.ts +51 -0
- package/dist/resolve/defaults.d.ts.map +1 -0
- package/dist/resolve/defaults.js +109 -0
- package/dist/resolve/defaults.js.map +1 -0
- package/dist/resolve/order.d.ts +73 -0
- package/dist/resolve/order.d.ts.map +1 -0
- package/dist/resolve/order.js +208 -0
- package/dist/resolve/order.js.map +1 -0
- package/dist/resolve/phase.d.ts +57 -0
- package/dist/resolve/phase.d.ts.map +1 -0
- package/dist/resolve/phase.js +60 -0
- package/dist/resolve/phase.js.map +1 -0
- package/dist/scaffold/__tests__/scaffold.test.js +8 -2
- package/dist/scaffold/__tests__/scaffold.test.js.map +1 -1
- package/dist/scaffold/__tests__/settings-merge.test.js +95 -0
- package/dist/scaffold/__tests__/settings-merge.test.js.map +1 -1
- package/dist/scaffold/incremental.d.ts.map +1 -1
- package/dist/scaffold/incremental.js +13 -2
- package/dist/scaffold/incremental.js.map +1 -1
- package/dist/visualizer/__tests__/brain.test.d.ts +2 -0
- package/dist/visualizer/__tests__/brain.test.d.ts.map +1 -0
- package/dist/visualizer/__tests__/brain.test.js +138 -0
- package/dist/visualizer/__tests__/brain.test.js.map +1 -0
- package/dist/visualizer/__tests__/topology-brain-link.test.d.ts +2 -0
- package/dist/visualizer/__tests__/topology-brain-link.test.d.ts.map +1 -0
- package/dist/visualizer/__tests__/topology-brain-link.test.js +56 -0
- package/dist/visualizer/__tests__/topology-brain-link.test.js.map +1 -0
- package/dist/visualizer/brain.d.ts +97 -0
- package/dist/visualizer/brain.d.ts.map +1 -0
- package/dist/visualizer/brain.js +473 -0
- package/dist/visualizer/brain.js.map +1 -0
- package/dist/visualizer/index.d.ts +19 -1
- package/dist/visualizer/index.d.ts.map +1 -1
- package/dist/visualizer/index.js +26 -11
- package/dist/visualizer/index.js.map +1 -1
- package/examples/code-review.at +32 -4
- package/examples/company-brain-team.at +241 -0
- package/examples/company-brain.at +104 -0
- package/examples/data-processing.at +33 -4
- package/examples/logos/gmail.svg +1 -0
- package/examples/logos/slack.svg +1 -0
- package/package.json +1 -1
- package/spec/grammar.md +55 -3
- package/spec/validation.md +103 -35
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal renderer — the human-facing view of a topology.
|
|
3
|
+
*
|
|
4
|
+
* Nothing like this existed in the package. The nearest prior art was
|
|
5
|
+
* `exporters/markdown.ts` `renderFlow()`, a flat edge list with no layout.
|
|
6
|
+
* This ranks by resolved execution order (`../resolve/order`), so what you read
|
|
7
|
+
* is the order the host agent will actually enact — including gates spliced at
|
|
8
|
+
* the position their `after` / `before` names, rather than at Kahn's depth 0.
|
|
9
|
+
*
|
|
10
|
+
* Pure: returns a string, prints nothing, applies no colour. The CLI adds
|
|
11
|
+
* colour; keeping this pure is what makes it testable, which the CLI is not.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import type { TopologyAST } from "../parser/ast.js";
|
|
16
|
+
/**
|
|
17
|
+
* Render a topology as a terminal flow graph plus a roles table.
|
|
18
|
+
*
|
|
19
|
+
* @param ast - A parsed topology.
|
|
20
|
+
* @returns A plain string. No ANSI codes, no trailing newline.
|
|
21
|
+
*/
|
|
22
|
+
export declare function renderAscii(ast: TopologyAST): string;
|
|
23
|
+
//# sourceMappingURL=ascii.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ascii.d.ts","sourceRoot":"","sources":["../../src/render/ascii.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAyC,MAAM,kBAAkB,CAAC;AAoK3F;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAKpD"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal renderer — the human-facing view of a topology.
|
|
3
|
+
*
|
|
4
|
+
* Nothing like this existed in the package. The nearest prior art was
|
|
5
|
+
* `exporters/markdown.ts` `renderFlow()`, a flat edge list with no layout.
|
|
6
|
+
* This ranks by resolved execution order (`../resolve/order`), so what you read
|
|
7
|
+
* is the order the host agent will actually enact — including gates spliced at
|
|
8
|
+
* the position their `after` / `before` names, rather than at Kahn's depth 0.
|
|
9
|
+
*
|
|
10
|
+
* Pure: returns a string, prints nothing, applies no colour. The CLI adds
|
|
11
|
+
* colour; keeping this pure is what makes it testable, which the CLI is not.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { resolveOrder } from "../resolve/order.js";
|
|
16
|
+
/** Glyph per node kind. Mirrors the shape vocabulary in `exporters/mermaid.ts`. */
|
|
17
|
+
const GLYPH = {
|
|
18
|
+
spawn: "▸",
|
|
19
|
+
action: "▪",
|
|
20
|
+
gate: "◆",
|
|
21
|
+
human: "☰",
|
|
22
|
+
group: "▤",
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Edge annotation string. Same field order and spelling as
|
|
26
|
+
* `exporters/markdown.ts` `edgeLabel`, so an edge is never described two
|
|
27
|
+
* different ways across two outputs.
|
|
28
|
+
*/
|
|
29
|
+
function edgeLabel(edge) {
|
|
30
|
+
const parts = [];
|
|
31
|
+
if (edge.isError)
|
|
32
|
+
parts.push(edge.errorType ? `error(${edge.errorType})` : "error");
|
|
33
|
+
if (edge.condition)
|
|
34
|
+
parts.push(`when ${edge.condition}`);
|
|
35
|
+
if (edge.maxIterations)
|
|
36
|
+
parts.push(`max ${edge.maxIterations}`);
|
|
37
|
+
if (edge.race)
|
|
38
|
+
parts.push("race");
|
|
39
|
+
if (edge.tolerance != null)
|
|
40
|
+
parts.push(`tolerance: ${edge.tolerance}`);
|
|
41
|
+
if (edge.wait)
|
|
42
|
+
parts.push(`wait ${edge.wait}`);
|
|
43
|
+
if (edge.weight != null)
|
|
44
|
+
parts.push(`weight ${edge.weight}`);
|
|
45
|
+
if (edge.reflection)
|
|
46
|
+
parts.push("reflection");
|
|
47
|
+
return parts.join(", ");
|
|
48
|
+
}
|
|
49
|
+
/** Right-pad to a column width, without truncating content that overflows. */
|
|
50
|
+
function pad(s, w) {
|
|
51
|
+
return s.length >= w ? s : s + " ".repeat(w - s.length);
|
|
52
|
+
}
|
|
53
|
+
function stepAnnotation(step, byId) {
|
|
54
|
+
if (step.kind === "gate") {
|
|
55
|
+
const g = byId.get(step.ids[0]);
|
|
56
|
+
const bits = ["gate"];
|
|
57
|
+
if (g?.behavior)
|
|
58
|
+
bits.push(g.behavior);
|
|
59
|
+
else
|
|
60
|
+
bits.push("blocking");
|
|
61
|
+
if (g?.onFail)
|
|
62
|
+
bits.push(`on-fail: ${g.onFail}`);
|
|
63
|
+
return bits.join(" · ");
|
|
64
|
+
}
|
|
65
|
+
if (step.kind === "spawn") {
|
|
66
|
+
const models = step.ids
|
|
67
|
+
.map((id) => byId.get(id)?.model)
|
|
68
|
+
.filter((m) => !!m);
|
|
69
|
+
const uniq = [...new Set(models)];
|
|
70
|
+
const label = step.ids.length === 1
|
|
71
|
+
? "agent"
|
|
72
|
+
: step.exclusive
|
|
73
|
+
? `branch ×${step.ids.length}, exactly one runs`
|
|
74
|
+
: `agent ×${step.ids.length}, parallel`;
|
|
75
|
+
return uniq.length ? `${label} · ${uniq.join(", ")}` : label;
|
|
76
|
+
}
|
|
77
|
+
return step.kind;
|
|
78
|
+
}
|
|
79
|
+
function renderSpine(ast) {
|
|
80
|
+
const byId = new Map(ast.nodes.map((n) => [n.id, n]));
|
|
81
|
+
const { steps, loops, unreachable, orchestrator } = resolveOrder(ast);
|
|
82
|
+
const out = [];
|
|
83
|
+
// Nodes with no edge at all are not a parallel step — they are declared and
|
|
84
|
+
// never wired in. Kahn ranks them all at depth 0, so they used to render as
|
|
85
|
+
// one big "agent ×N, parallel" line that read like real concurrent work.
|
|
86
|
+
const wired = new Set(ast.edges.flatMap((e) => [e.from, e.to]));
|
|
87
|
+
const orphans = ast.nodes
|
|
88
|
+
.filter((n) => n.type !== "orchestrator" && !wired.has(n.id))
|
|
89
|
+
.map((n) => n.id);
|
|
90
|
+
const orphanSet = new Set(orphans);
|
|
91
|
+
const spineSteps = steps
|
|
92
|
+
.map((s) => ({ ...s, ids: s.ids.filter((id) => !orphanSet.has(id)) }))
|
|
93
|
+
.filter((s) => s.ids.length > 0)
|
|
94
|
+
// Renumber: dropping an unwired step must not leave a gap in the column.
|
|
95
|
+
.map((s, i) => ({ ...s, index: i + 1 }));
|
|
96
|
+
if (spineSteps.length === 0) {
|
|
97
|
+
out.push(" (no flow declared)");
|
|
98
|
+
if (orphans.length) {
|
|
99
|
+
out.push("");
|
|
100
|
+
out.push(` declared but not in the flow: ${orphans.join(", ")}`);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
// Width of the widest node column, so annotations line up.
|
|
105
|
+
// `∥` is concurrency. An exclusive branch is not concurrent — exactly one of
|
|
106
|
+
// its ids runs — so it gets a different separator and a different glyph.
|
|
107
|
+
const bodies = spineSteps.map((s) => s.ids.join(s.exclusive ? " | " : " ∥ "));
|
|
108
|
+
const width = Math.max(...bodies.map((b) => b.length), 20);
|
|
109
|
+
// Pad the step number to the widest index, not a fixed 2 — at step 100 the
|
|
110
|
+
// column shifted and the whole spine lost alignment.
|
|
111
|
+
const numWidth = String(spineSteps.length).length;
|
|
112
|
+
spineSteps.forEach((step, i) => {
|
|
113
|
+
const glyph = step.exclusive ? "⑂" : (GLYPH[step.kind] ?? "▸");
|
|
114
|
+
const num = String(step.index).padStart(numWidth, " ");
|
|
115
|
+
out.push(` ${num} ${glyph} ${pad(bodies[i], width)} ${stepAnnotation(step, byId)}`);
|
|
116
|
+
if (step.exclusive && step.branchOn) {
|
|
117
|
+
for (const b of step.branchOn) {
|
|
118
|
+
out.push(` ${" ".repeat(numWidth)} ├─ ${b.id} when ${b.condition}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (i < spineSteps.length - 1)
|
|
122
|
+
out.push(` ${" ".repeat(numWidth)} │`);
|
|
123
|
+
});
|
|
124
|
+
if (loops.length) {
|
|
125
|
+
out.push("");
|
|
126
|
+
for (const l of loops) {
|
|
127
|
+
const edge = ast.edges.find((e) => e.from === l.from && e.to === l.to);
|
|
128
|
+
const label = edge ? edgeLabel(edge) : "";
|
|
129
|
+
out.push(` ↩ ${l.from} → ${l.to}${label ? ` ${label}` : ""}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (orphans.length) {
|
|
133
|
+
out.push("");
|
|
134
|
+
out.push(` ⚠ declared but not in the flow: ${orphans.join(", ")}`);
|
|
135
|
+
}
|
|
136
|
+
if (unreachable.length) {
|
|
137
|
+
out.push("");
|
|
138
|
+
out.push(` ⚠ unreachable from the entry point: ${unreachable.join(", ")}`);
|
|
139
|
+
}
|
|
140
|
+
if (orchestrator) {
|
|
141
|
+
out.push("");
|
|
142
|
+
out.push(` orchestrator "${orchestrator}" is the host agent — it is not a step.`);
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
function renderRoles(ast) {
|
|
147
|
+
const agents = ast.nodes.filter((n) => n.type === "agent");
|
|
148
|
+
if (!agents.length)
|
|
149
|
+
return [];
|
|
150
|
+
const rows = agents.map((a) => ({
|
|
151
|
+
id: a.id,
|
|
152
|
+
model: a.model ?? "—",
|
|
153
|
+
reads: a.reads?.length ? String(a.reads.length) : "—",
|
|
154
|
+
writes: a.writes?.length ? String(a.writes.length) : "—",
|
|
155
|
+
role: a.role ?? a.description ?? "—",
|
|
156
|
+
}));
|
|
157
|
+
const wId = Math.max(4, ...rows.map((r) => r.id.length));
|
|
158
|
+
const wModel = Math.max(5, ...rows.map((r) => r.model.length));
|
|
159
|
+
const out = ["", " Roles", ""];
|
|
160
|
+
out.push(` ${pad("id", wId)} ${pad("model", wModel)} in out role`);
|
|
161
|
+
out.push(` ${"─".repeat(wId)} ${"─".repeat(wModel)} ── ─── ${"─".repeat(28)}`);
|
|
162
|
+
for (const r of rows) {
|
|
163
|
+
out.push(` ${pad(r.id, wId)} ${pad(r.model, wModel)} ${pad(r.reads, 2)} ${pad(r.writes, 3)} ${r.role}`);
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Render a topology as a terminal flow graph plus a roles table.
|
|
169
|
+
*
|
|
170
|
+
* @param ast - A parsed topology.
|
|
171
|
+
* @returns A plain string. No ANSI codes, no trailing newline.
|
|
172
|
+
*/
|
|
173
|
+
export function renderAscii(ast) {
|
|
174
|
+
const header = `${ast.topology.name} v${ast.topology.version ?? "0.0.0"}`;
|
|
175
|
+
const patterns = ast.topology.patterns?.length ? ` · ${ast.topology.patterns.join(", ")}` : "";
|
|
176
|
+
return [` ${header}${patterns}`, "", ...renderSpine(ast), ...renderRoles(ast)].join("\n");
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=ascii.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ascii.js","sourceRoot":"","sources":["../../src/render/ascii.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.test.d.ts","sourceRoot":"","sources":["../../../src/resolve/__tests__/defaults.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve, dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { parse } from "../../parser/index.js";
|
|
6
|
+
import { resolveDefaults } from "../defaults.js";
|
|
7
|
+
const example = (name) => readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), "../../../examples", name), "utf8");
|
|
8
|
+
const MINIMAL = `topology t : [pipeline] {
|
|
9
|
+
meta {
|
|
10
|
+
version: "1.0.0"
|
|
11
|
+
description: "x"
|
|
12
|
+
}
|
|
13
|
+
agent worker {
|
|
14
|
+
model: sonnet
|
|
15
|
+
description: "w"
|
|
16
|
+
}
|
|
17
|
+
action intake {
|
|
18
|
+
kind: inline
|
|
19
|
+
description: "in"
|
|
20
|
+
}
|
|
21
|
+
flow { intake -> worker }
|
|
22
|
+
}`;
|
|
23
|
+
describe("resolveDefaults", () => {
|
|
24
|
+
it("fills agent.permissions with the SPEC default, not a binding's", () => {
|
|
25
|
+
// The bindings disagree: codex.ts uses "supervised", openclaw.ts uses
|
|
26
|
+
// "auto". spec/grammar.md:1717 says "autonomous". The spec wins here.
|
|
27
|
+
const { ast } = resolveDefaults(parse(MINIMAL));
|
|
28
|
+
const worker = ast.nodes.find((n) => n.id === "worker");
|
|
29
|
+
expect(worker.permissions).toBe("autonomous");
|
|
30
|
+
});
|
|
31
|
+
it("fills concrete agent defaults and leaves 'none' fields undefined", () => {
|
|
32
|
+
const { ast } = resolveDefaults(parse(MINIMAL));
|
|
33
|
+
const worker = ast.nodes.find((n) => n.id === "worker");
|
|
34
|
+
expect(worker.reads).toEqual([]);
|
|
35
|
+
expect(worker.writes).toEqual([]);
|
|
36
|
+
expect(worker.outputs).toEqual({});
|
|
37
|
+
expect(worker.retry).toBe(0);
|
|
38
|
+
expect(worker.background).toBe(false);
|
|
39
|
+
expect(worker.behavior).toBe("blocking");
|
|
40
|
+
// Rows whose spec default is "-- (none)" must stay absent — absence IS
|
|
41
|
+
// their meaning, and inventing a value would change behaviour.
|
|
42
|
+
expect(worker.phase).toBeUndefined();
|
|
43
|
+
expect(worker.prompt).toBeUndefined();
|
|
44
|
+
expect(worker.tools).toBeUndefined();
|
|
45
|
+
expect(worker.isolation).toBeUndefined();
|
|
46
|
+
expect(worker.maxTurns).toBeUndefined();
|
|
47
|
+
});
|
|
48
|
+
it("never overwrites an authored value", () => {
|
|
49
|
+
// NB: fields must be newline-separated. parseFields is line-based, so two
|
|
50
|
+
// fields on one line make the first key swallow the rest of the line.
|
|
51
|
+
const src = MINIMAL.replace('agent worker {\n model: sonnet\n description: "w"\n }', [
|
|
52
|
+
"agent worker {",
|
|
53
|
+
" model: sonnet",
|
|
54
|
+
' description: "w"',
|
|
55
|
+
" permissions: supervised",
|
|
56
|
+
" retry: 3",
|
|
57
|
+
" }",
|
|
58
|
+
].join("\n"));
|
|
59
|
+
const { ast, applied } = resolveDefaults(parse(src));
|
|
60
|
+
const worker = ast.nodes.find((n) => n.id === "worker");
|
|
61
|
+
expect(worker.permissions).toBe("supervised");
|
|
62
|
+
expect(worker.retry).toBe(3);
|
|
63
|
+
expect(applied.find((a) => a.node === "worker" && a.field === "permissions")).toBeUndefined();
|
|
64
|
+
expect(applied.find((a) => a.node === "worker" && a.field === "retry")).toBeUndefined();
|
|
65
|
+
});
|
|
66
|
+
it("does not mutate the input AST", () => {
|
|
67
|
+
const original = parse(MINIMAL);
|
|
68
|
+
const before = original.nodes.find((n) => n.id === "worker");
|
|
69
|
+
expect(before.permissions).toBeUndefined();
|
|
70
|
+
resolveDefaults(original);
|
|
71
|
+
expect(before.permissions).toBeUndefined();
|
|
72
|
+
});
|
|
73
|
+
it("reports every field it filled, so a brief can name them", () => {
|
|
74
|
+
const { applied } = resolveDefaults(parse(MINIMAL));
|
|
75
|
+
const workerFields = applied.filter((a) => a.node === "worker").map((a) => a.field);
|
|
76
|
+
expect(workerFields).toContain("permissions");
|
|
77
|
+
expect(workerFields).toContain("mcp-servers");
|
|
78
|
+
// grammar-name spelling, not the AST camelCase key
|
|
79
|
+
expect(workerFields).not.toContain("mcpServers");
|
|
80
|
+
});
|
|
81
|
+
it("resolves gate defaults from the spec", () => {
|
|
82
|
+
const { ast } = resolveDefaults(parse(example("simple-pipeline.at")));
|
|
83
|
+
const gate = ast.nodes.find((n) => n.type === "gate");
|
|
84
|
+
expect(gate.onFail).toBeDefined();
|
|
85
|
+
expect(gate.behavior).toBeDefined();
|
|
86
|
+
expect(gate.retry).toBeDefined();
|
|
87
|
+
});
|
|
88
|
+
it("leaves a real example's authored values intact", () => {
|
|
89
|
+
const raw = parse(example("code-review.at"));
|
|
90
|
+
const { ast } = resolveDefaults(raw);
|
|
91
|
+
const analyzer = ast.nodes.find((n) => n.id === "analyzer");
|
|
92
|
+
expect(analyzer.permissions).toBe("supervised"); // authored
|
|
93
|
+
expect(analyzer.tools).toEqual(["Read", "Grep", "Glob"]); // authored
|
|
94
|
+
expect(analyzer.memory).toEqual([]); // filled
|
|
95
|
+
});
|
|
96
|
+
it("does not hand the caller a reference to the defaults table", () => {
|
|
97
|
+
// Regression: `applied[].value` aliased the module-level table, so mutating
|
|
98
|
+
// a reported value poisoned the default for every later topology in the
|
|
99
|
+
// process.
|
|
100
|
+
const first = resolveDefaults(parse(MINIMAL));
|
|
101
|
+
const reads = first.applied.find((a) => a.field === "reads");
|
|
102
|
+
reads.value.push("POISON");
|
|
103
|
+
const second = resolveDefaults(parse(MINIMAL));
|
|
104
|
+
const worker = second.ast.nodes.find((n) => n.id === "worker");
|
|
105
|
+
expect(worker.reads).toEqual([]);
|
|
106
|
+
expect(second.applied.find((a) => a.field === "reads").value).toEqual([]);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=defaults.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.test.js","sourceRoot":"","sources":["../../../src/resolve/__tests__/defaults.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"order.test.d.ts","sourceRoot":"","sources":["../../../src/resolve/__tests__/order.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve, dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { parse } from "../../parser/index.js";
|
|
6
|
+
import { computeLayers } from "../../analyzer/index.js";
|
|
7
|
+
import { resolveOrder } from "../order.js";
|
|
8
|
+
const example = (name) => readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), "../../../examples", name), "utf8");
|
|
9
|
+
describe("resolveOrder", () => {
|
|
10
|
+
it("does NOT leave gates at depth 0 the way raw computeLayers does", () => {
|
|
11
|
+
// Regression for the whole reason this module exists. Gates bind by
|
|
12
|
+
// after:/before:, never by edges, so their in-degree is 0 and Kahn ranks
|
|
13
|
+
// them as sources. Measured: `human-approval` lands in layer 0 next to the
|
|
14
|
+
// entry action, which would run a post-review gate before any review.
|
|
15
|
+
const ast = parse(example("code-review.at"));
|
|
16
|
+
const allIds = new Set(ast.nodes.map((n) => n.id));
|
|
17
|
+
const raw = computeLayers(ast.edges, allIds);
|
|
18
|
+
expect(raw[0].nodes).toContain("human-approval");
|
|
19
|
+
const { steps } = resolveOrder(ast);
|
|
20
|
+
const gateStep = steps.find((s) => s.kind === "gate");
|
|
21
|
+
const reviewerStep = steps.find((s) => s.ids.includes("reviewer"));
|
|
22
|
+
expect(gateStep.index).toBeGreaterThan(reviewerStep.index);
|
|
23
|
+
});
|
|
24
|
+
it("splices a gate immediately after its `after` anchor", () => {
|
|
25
|
+
const ast = parse(example("simple-pipeline.at"));
|
|
26
|
+
const { steps } = resolveOrder(ast);
|
|
27
|
+
const writer = steps.find((s) => s.ids.includes("writer"));
|
|
28
|
+
const gate = steps.find((s) => s.ids.includes("quality-check"));
|
|
29
|
+
expect(gate.index).toBe(writer.index + 1);
|
|
30
|
+
expect(gate.depth).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
it("groups a parallel layer into one step", () => {
|
|
33
|
+
const { steps } = resolveOrder(parse(example("code-review.at")));
|
|
34
|
+
const fanout = steps.find((s) => s.ids.length > 1);
|
|
35
|
+
expect(fanout.kind).toBe("spawn");
|
|
36
|
+
expect(fanout.ids.sort()).toEqual(["analyzer", "security-scanner"]);
|
|
37
|
+
});
|
|
38
|
+
it("never mixes node kinds in one step", () => {
|
|
39
|
+
for (const f of ["code-review.at", "simple-pipeline.at", "data-processing.at"]) {
|
|
40
|
+
const ast = parse(example(f));
|
|
41
|
+
const byType = new Map(ast.nodes.map((n) => [n.id, n.type]));
|
|
42
|
+
for (const step of resolveOrder(ast).steps) {
|
|
43
|
+
const types = new Set(step.ids.map((id) => byType.get(id)));
|
|
44
|
+
expect(types.size, `${f} step ${step.index}`).toBe(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
it("excludes the orchestrator from steps and names it as the host", () => {
|
|
49
|
+
const { steps, orchestrator } = resolveOrder(parse(example("simple-pipeline.at")));
|
|
50
|
+
expect(orchestrator).toBe("orchestrator");
|
|
51
|
+
expect(steps.some((s) => s.ids.includes("orchestrator"))).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
it("reports every loop with its traversal budget", () => {
|
|
54
|
+
const { loops } = resolveOrder(parse(example("simple-pipeline.at")));
|
|
55
|
+
const revise = loops.find((l) => l.from === "reviewer" && l.to === "writer");
|
|
56
|
+
expect(revise.budget).toBe(2);
|
|
57
|
+
expect(revise.condition).toContain("revise");
|
|
58
|
+
});
|
|
59
|
+
it("numbers steps contiguously from 1", () => {
|
|
60
|
+
const { steps } = resolveOrder(parse(example("code-review.at")));
|
|
61
|
+
expect(steps.map((s) => s.index)).toEqual(steps.map((_, i) => i + 1));
|
|
62
|
+
});
|
|
63
|
+
it("places a gate anchored to nothing resolvable last, rather than dropping it", () => {
|
|
64
|
+
const src = `topology t : [pipeline] {
|
|
65
|
+
meta {
|
|
66
|
+
version: "1.0.0"
|
|
67
|
+
description: "x"
|
|
68
|
+
}
|
|
69
|
+
agent a {
|
|
70
|
+
model: sonnet
|
|
71
|
+
description: "a"
|
|
72
|
+
}
|
|
73
|
+
action intake {
|
|
74
|
+
kind: inline
|
|
75
|
+
description: "in"
|
|
76
|
+
}
|
|
77
|
+
gates {
|
|
78
|
+
gate orphan {
|
|
79
|
+
after: nowhere
|
|
80
|
+
run: "true"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
flow { intake -> a }
|
|
84
|
+
}`;
|
|
85
|
+
const { steps } = resolveOrder(parse(src));
|
|
86
|
+
expect(steps[steps.length - 1].ids).toEqual(["orphan"]);
|
|
87
|
+
});
|
|
88
|
+
const BRANCH = `topology t : [pipeline] {
|
|
89
|
+
meta {
|
|
90
|
+
version: "1.0.0"
|
|
91
|
+
description: "one decision, two exclusive outcomes"
|
|
92
|
+
}
|
|
93
|
+
agent judge {
|
|
94
|
+
model: opus
|
|
95
|
+
description: "j"
|
|
96
|
+
outputs: {
|
|
97
|
+
verdict: pass | fail
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
agent onpass {
|
|
101
|
+
model: sonnet
|
|
102
|
+
description: "p"
|
|
103
|
+
}
|
|
104
|
+
agent onfail {
|
|
105
|
+
model: sonnet
|
|
106
|
+
description: "f"
|
|
107
|
+
}
|
|
108
|
+
action i {
|
|
109
|
+
kind: inline
|
|
110
|
+
description: "in"
|
|
111
|
+
}
|
|
112
|
+
flow { i -> judge
|
|
113
|
+
judge -> onpass [when judge.verdict == pass]
|
|
114
|
+
judge -> onfail [when judge.verdict == fail] }
|
|
115
|
+
}`;
|
|
116
|
+
it("marks mutually exclusive branches as exclusive, not parallel", () => {
|
|
117
|
+
// Regression: two conditional outcomes of ONE decision sat at the same
|
|
118
|
+
// depth, so they were reported as a parallel fan-out. The brief then marked
|
|
119
|
+
// them mutually blind and told the host to dispatch BOTH in one message —
|
|
120
|
+
// running every branch of a decision instead of taking one.
|
|
121
|
+
const step = resolveOrder(parse(BRANCH)).steps.find((s) => s.ids.length > 1);
|
|
122
|
+
expect(step.exclusive).toBe(true);
|
|
123
|
+
expect(step.branchOn?.map((b) => b.id).sort()).toEqual(["onfail", "onpass"]);
|
|
124
|
+
for (const b of step.branchOn)
|
|
125
|
+
expect(b.condition).toContain("judge.verdict");
|
|
126
|
+
});
|
|
127
|
+
it("does not mark a genuine parallel fan-out exclusive", () => {
|
|
128
|
+
const step = resolveOrder(parse(example("code-review.at"))).steps.find((s) => s.ids.length > 1);
|
|
129
|
+
expect(step.exclusive).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
it("does not report a forward edge carrying [max N] as a loop", () => {
|
|
132
|
+
// A forward edge may legitimately carry a bound. Treating any [max N] as a
|
|
133
|
+
// back-edge reported straight-line flows as cycles.
|
|
134
|
+
const src = `topology t : [pipeline] {
|
|
135
|
+
meta {
|
|
136
|
+
version: "1.0.0"
|
|
137
|
+
description: "x"
|
|
138
|
+
}
|
|
139
|
+
agent a {
|
|
140
|
+
model: sonnet
|
|
141
|
+
description: "a"
|
|
142
|
+
}
|
|
143
|
+
agent b {
|
|
144
|
+
model: sonnet
|
|
145
|
+
description: "b"
|
|
146
|
+
}
|
|
147
|
+
action i {
|
|
148
|
+
kind: inline
|
|
149
|
+
description: "in"
|
|
150
|
+
}
|
|
151
|
+
flow { i -> a
|
|
152
|
+
a -> b [max 3] }
|
|
153
|
+
}`;
|
|
154
|
+
expect(resolveOrder(parse(src)).loops).toEqual([]);
|
|
155
|
+
});
|
|
156
|
+
it("still reports a real back-edge as a loop, with its budget", () => {
|
|
157
|
+
const { loops } = resolveOrder(parse(example("simple-pipeline.at")));
|
|
158
|
+
expect(loops.length).toBeGreaterThan(0);
|
|
159
|
+
expect(loops.find((l) => l.from === "reviewer" && l.to === "writer").budget).toBe(2);
|
|
160
|
+
});
|
|
161
|
+
it("keeps two gates on the same anchor in declaration order", () => {
|
|
162
|
+
// Regression: each gate spliced at the same index and pushed its
|
|
163
|
+
// predecessor down, so they ran in REVERSE declaration order.
|
|
164
|
+
const src = `topology t : [pipeline] {
|
|
165
|
+
meta {
|
|
166
|
+
version: "1.0.0"
|
|
167
|
+
description: "x"
|
|
168
|
+
}
|
|
169
|
+
agent a {
|
|
170
|
+
model: sonnet
|
|
171
|
+
description: "a"
|
|
172
|
+
}
|
|
173
|
+
agent b {
|
|
174
|
+
model: sonnet
|
|
175
|
+
description: "b"
|
|
176
|
+
}
|
|
177
|
+
action i {
|
|
178
|
+
kind: inline
|
|
179
|
+
description: "in"
|
|
180
|
+
}
|
|
181
|
+
gates {
|
|
182
|
+
gate first {
|
|
183
|
+
after: a
|
|
184
|
+
run: "true"
|
|
185
|
+
}
|
|
186
|
+
gate second {
|
|
187
|
+
after: a
|
|
188
|
+
run: "true"
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
flow { i -> a -> b }
|
|
192
|
+
}`;
|
|
193
|
+
const gateSteps = resolveOrder(parse(src))
|
|
194
|
+
.steps.filter((s) => s.kind === "gate")
|
|
195
|
+
.map((s) => s.ids[0]);
|
|
196
|
+
expect(gateSteps).toEqual(["first", "second"]);
|
|
197
|
+
});
|
|
198
|
+
it("places a gate anchored to another gate after it, whatever the declaration order", () => {
|
|
199
|
+
// Regression: gates spliced in declaration order, so a gate whose anchor
|
|
200
|
+
// was declared LATER found nothing, fell through to "runs last", and
|
|
201
|
+
// silently skipped past the step it was meant to guard.
|
|
202
|
+
const src = `topology t : [pipeline] {
|
|
203
|
+
meta {
|
|
204
|
+
version: "1.0.0"
|
|
205
|
+
description: "x"
|
|
206
|
+
}
|
|
207
|
+
agent a {
|
|
208
|
+
model: sonnet
|
|
209
|
+
description: "a"
|
|
210
|
+
}
|
|
211
|
+
agent b {
|
|
212
|
+
model: sonnet
|
|
213
|
+
description: "b"
|
|
214
|
+
}
|
|
215
|
+
action i {
|
|
216
|
+
kind: inline
|
|
217
|
+
description: "in"
|
|
218
|
+
}
|
|
219
|
+
gates {
|
|
220
|
+
gate second {
|
|
221
|
+
after: first
|
|
222
|
+
run: "true"
|
|
223
|
+
}
|
|
224
|
+
gate first {
|
|
225
|
+
after: a
|
|
226
|
+
run: "true"
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
flow { i -> a -> b }
|
|
230
|
+
}`;
|
|
231
|
+
const steps = resolveOrder(parse(src)).steps;
|
|
232
|
+
const idx = (id) => steps.findIndex((s) => s.ids.includes(id));
|
|
233
|
+
expect(idx("first")).toBeGreaterThan(idx("a"));
|
|
234
|
+
expect(idx("second")).toBe(idx("first") + 1);
|
|
235
|
+
expect(idx("second")).toBeLessThan(idx("b"));
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
//# sourceMappingURL=order.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"order.test.js","sourceRoot":"","sources":["../../../src/resolve/__tests__/order.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An unphased agent must sort the same way on every target.
|
|
3
|
+
*
|
|
4
|
+
* Regression: six consumers each picked their own sentinel — MAX_SAFE_INTEGER
|
|
5
|
+
* in claude-code and claude-workflow, 999 in gemini-cli, 0 in openclaw, kiro,
|
|
6
|
+
* mermaid and the markdown exporter. So the same topology put an unphased
|
|
7
|
+
* agent LAST on three targets and FIRST on three others. That is exactly the
|
|
8
|
+
* failure "write once, deploy to any platform" exists to prevent, and nothing
|
|
9
|
+
* compared them.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=phase.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase.test.d.ts","sourceRoot":"","sources":["../../../src/resolve/__tests__/phase.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|