agentopology 0.3.0 → 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 +52 -4
- 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 +2 -1
- 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 +149 -3
- 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 +1386 -210
- 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/index.d.ts +50 -6
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +183 -28
- 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 +77 -2
- 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__/topology-brain-link.test.js +19 -4
- package/dist/visualizer/__tests__/topology-brain-link.test.js.map +1 -1
- package/examples/code-review.at +32 -4
- package/examples/data-processing.at +33 -4
- package/package.json +1 -1
- package/spec/grammar.md +25 -1
- package/spec/validation.md +103 -35
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The spec must not drift from the implementation.
|
|
3
|
+
*
|
|
4
|
+
* `spec/validation.md` documented 35 rules while `validator.ts` implemented 89.
|
|
5
|
+
* Fifty-four rules existed only in source. Nobody noticed because nothing
|
|
6
|
+
* compared them — which is the actual bug. Documentation drift is not a
|
|
7
|
+
* discipline problem, it is a missing test.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=spec-sync.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-sync.test.d.ts","sourceRoot":"","sources":["../../../src/parser/__tests__/spec-sync.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The spec must not drift from the implementation.
|
|
3
|
+
*
|
|
4
|
+
* `spec/validation.md` documented 35 rules while `validator.ts` implemented 89.
|
|
5
|
+
* Fifty-four rules existed only in source. Nobody noticed because nothing
|
|
6
|
+
* compared them — which is the actual bug. Documentation drift is not a
|
|
7
|
+
* discipline problem, it is a missing test.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect } from "vitest";
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { resolve, dirname } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
|
|
14
|
+
const read = (p) => readFileSync(resolve(root, p), "utf8");
|
|
15
|
+
/** Rule numbers the validator actually registers, from its `validate()` spread. */
|
|
16
|
+
function implementedRules() {
|
|
17
|
+
const src = read("src/parser/validator.ts");
|
|
18
|
+
const body = src.slice(src.indexOf("export function validate("));
|
|
19
|
+
const fns = [...body.matchAll(/\.\.\.(v(\d+)[A-Za-z0-9_]*)\(ast\)/g)];
|
|
20
|
+
return [...new Set(fns.map((m) => Number(m[2])))].sort((a, b) => a - b);
|
|
21
|
+
}
|
|
22
|
+
/** Rule numbers documented in the spec's summary table. */
|
|
23
|
+
function documentedRules() {
|
|
24
|
+
const md = read("spec/validation.md");
|
|
25
|
+
const rows = [...md.matchAll(/^\|\s*V(\d+)\s*\|/gm)];
|
|
26
|
+
return [...new Set(rows.map((m) => Number(m[1])))].sort((a, b) => a - b);
|
|
27
|
+
}
|
|
28
|
+
describe("spec/validation.md tracks the validator", () => {
|
|
29
|
+
it("documents every implemented rule", () => {
|
|
30
|
+
const impl = implementedRules();
|
|
31
|
+
const docs = documentedRules();
|
|
32
|
+
const undocumented = impl.filter((n) => !docs.includes(n));
|
|
33
|
+
expect(undocumented, `implemented but undocumented: ${undocumented.map((n) => "V" + n).join(", ")}`).toEqual([]);
|
|
34
|
+
});
|
|
35
|
+
it("documents no rule the validator does not implement", () => {
|
|
36
|
+
const impl = implementedRules();
|
|
37
|
+
const docs = documentedRules();
|
|
38
|
+
const phantom = docs.filter((n) => !impl.includes(n));
|
|
39
|
+
expect(phantom, `documented but not implemented: ${phantom.map((n) => "V" + n).join(", ")}`).toEqual([]);
|
|
40
|
+
});
|
|
41
|
+
it("numbers rules contiguously from V1", () => {
|
|
42
|
+
const impl = implementedRules();
|
|
43
|
+
const gaps = [];
|
|
44
|
+
for (let i = 1; i <= Math.max(...impl); i++)
|
|
45
|
+
if (!impl.includes(i))
|
|
46
|
+
gaps.push(i);
|
|
47
|
+
expect(gaps, `gaps in rule numbering: ${gaps.join(", ")}`).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
it("states the same rule count in prose as it lists in the table", () => {
|
|
50
|
+
const md = read("spec/validation.md");
|
|
51
|
+
const docs = documentedRules();
|
|
52
|
+
// The prose above the table names the count; a stale number is exactly the
|
|
53
|
+
// drift this file exists to catch.
|
|
54
|
+
const claimed = [...md.matchAll(/All (\d+) rules/g)].map((m) => Number(m[1]));
|
|
55
|
+
expect(claimed.length, "spec should state its rule count").toBeGreaterThan(0);
|
|
56
|
+
for (const n of claimed)
|
|
57
|
+
expect(n).toBe(docs.length);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=spec-sync.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-sync.test.js","sourceRoot":"","sources":["../../../src/parser/__tests__/spec-sync.test.ts"],"names":[],"mappings":"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"}
|
package/dist/parser/index.d.ts
CHANGED
|
@@ -14,6 +14,20 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { TopologyAST, TopologyMeta, OrchestratorNode, ActionNode, AgentNode, GateNode, HumanNode, GroupNode, EdgeDef, DepthDef, TriggerDef, HookDef, SkillDef, ToolBlockDef, MeteringDef, ProviderDef, ScheduleJobDef, InterfaceDef, DefaultsDef, SchemaType, SchemaFieldDef, SchemaDef, SensitiveValue, ObservabilityDef, ParamDef, InterfaceEndpoints, ImportDef, IncludeDef, CheckpointDef, ArtifactDef, StoreNode, RetrievalNode } from "./ast.js";
|
|
16
16
|
export type * from "./ast.js";
|
|
17
|
+
/**
|
|
18
|
+
* Placeholders the parser injects for REQUIRED fields that were not declared.
|
|
19
|
+
*
|
|
20
|
+
* The parser cannot leave them undefined without changing non-optional AST
|
|
21
|
+
* types, so it substitutes a sentinel — and that sentinel then satisfied every
|
|
22
|
+
* "is it present?" check downstream. V91 recognises them and reports the field
|
|
23
|
+
* as missing, which is what the spec says it is.
|
|
24
|
+
*/
|
|
25
|
+
export declare const MISSING: {
|
|
26
|
+
/** `orchestrator.model` — spec/grammar.md, orchestrator field table. */
|
|
27
|
+
readonly model: "unknown";
|
|
28
|
+
/** `meta.version` — spec/grammar.md:282, Required: yes. */
|
|
29
|
+
readonly version: "0.0.0";
|
|
30
|
+
};
|
|
17
31
|
/**
|
|
18
32
|
* Parse a type expression string into a {@link SchemaType}.
|
|
19
33
|
*
|
|
@@ -57,7 +71,12 @@ export declare function parseMeta(body: string): Partial<TopologyMeta>;
|
|
|
57
71
|
/**
|
|
58
72
|
* Parse the `orchestrator { ... }` block into an {@link OrchestratorNode}.
|
|
59
73
|
*/
|
|
60
|
-
export declare function parseOrchestrator(body: string
|
|
74
|
+
export declare function parseOrchestrator(body: string, _blockFieldMisuseWarnings?: Array<{
|
|
75
|
+
rule: string;
|
|
76
|
+
level: "error" | "warning";
|
|
77
|
+
message: string;
|
|
78
|
+
node?: string;
|
|
79
|
+
}>): OrchestratorNode;
|
|
61
80
|
/**
|
|
62
81
|
* Parse the `roles { ... }` block into a map of role name to description.
|
|
63
82
|
*/
|
|
@@ -65,7 +84,12 @@ export declare function parseRoles(body: string): Record<string, string>;
|
|
|
65
84
|
/**
|
|
66
85
|
* Parse an `action <id> { ... }` block into an {@link ActionNode}.
|
|
67
86
|
*/
|
|
68
|
-
export declare function parseAction(id: string, body: string
|
|
87
|
+
export declare function parseAction(id: string, body: string, _blockFieldMisuseWarnings?: Array<{
|
|
88
|
+
rule: string;
|
|
89
|
+
level: "error" | "warning";
|
|
90
|
+
message: string;
|
|
91
|
+
node?: string;
|
|
92
|
+
}>): ActionNode;
|
|
69
93
|
/**
|
|
70
94
|
* Parse an `agent <id> { ... }` block into an {@link AgentNode}.
|
|
71
95
|
*
|
|
@@ -74,19 +98,39 @@ export declare function parseAction(id: string, body: string): ActionNode;
|
|
|
74
98
|
* @param roles - Role descriptions from the `roles` block, used to attach a
|
|
75
99
|
* role description to matching agents.
|
|
76
100
|
*/
|
|
77
|
-
export declare function parseAgent(id: string, body: string, roles: Record<string, string
|
|
101
|
+
export declare function parseAgent(id: string, body: string, roles: Record<string, string>, _blockFieldMisuseWarnings?: Array<{
|
|
102
|
+
rule: string;
|
|
103
|
+
level: "error" | "warning";
|
|
104
|
+
message: string;
|
|
105
|
+
node?: string;
|
|
106
|
+
}>): AgentNode;
|
|
78
107
|
/**
|
|
79
108
|
* Parse a `gate <id> { ... }` block into a {@link GateNode}.
|
|
80
109
|
*/
|
|
81
|
-
export declare function parseGate(id: string, body: string
|
|
110
|
+
export declare function parseGate(id: string, body: string, _blockFieldMisuseWarnings?: Array<{
|
|
111
|
+
rule: string;
|
|
112
|
+
level: "error" | "warning";
|
|
113
|
+
message: string;
|
|
114
|
+
node?: string;
|
|
115
|
+
}>): GateNode;
|
|
82
116
|
/**
|
|
83
117
|
* Parse a `human <id> { ... }` block into a {@link HumanNode}.
|
|
84
118
|
*/
|
|
85
|
-
export declare function parseHuman(id: string, body: string
|
|
119
|
+
export declare function parseHuman(id: string, body: string, _blockFieldMisuseWarnings?: Array<{
|
|
120
|
+
rule: string;
|
|
121
|
+
level: "error" | "warning";
|
|
122
|
+
message: string;
|
|
123
|
+
node?: string;
|
|
124
|
+
}>): HumanNode;
|
|
86
125
|
/**
|
|
87
126
|
* Parse a `group <id> { ... }` block into a {@link GroupNode}.
|
|
88
127
|
*/
|
|
89
|
-
export declare function parseGroup(id: string, body: string
|
|
128
|
+
export declare function parseGroup(id: string, body: string, _blockFieldMisuseWarnings?: Array<{
|
|
129
|
+
rule: string;
|
|
130
|
+
level: "error" | "warning";
|
|
131
|
+
message: string;
|
|
132
|
+
node?: string;
|
|
133
|
+
}>): GroupNode;
|
|
90
134
|
/**
|
|
91
135
|
* Parse the `flow { ... }` block into an array of {@link EdgeDef}.
|
|
92
136
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EAET,OAAO,EACP,QAAQ,EAER,UAAU,EACV,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,WAAW,EAEX,WAAW,EACX,cAAc,EACd,YAAY,EAGZ,WAAW,EACX,UAAU,EACV,cAAc,EACd,SAAS,EACT,cAAc,EAEd,gBAAgB,EAGhB,QAAQ,EACR,kBAAkB,EAClB,SAAS,EACT,UAAU,EACV,aAAa,EAEb,WAAW,EAGX,SAAS,EACT,aAAa,EAOd,MAAM,UAAU,CAAC;AAgBlB,mBAAmB,UAAU,CAAC;AAgE9B;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAsB3D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,CAwBhE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAgBzD;AAMD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAYA;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAkB7D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"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"}
|
package/dist/parser/index.js
CHANGED
|
@@ -16,6 +16,78 @@ import { stripComments, extractBlock, extractAllBlocks, parseKV, parseList, pars
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
// Helpers
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
|
+
/**
|
|
20
|
+
* V90: detect a field value that swallowed the next field.
|
|
21
|
+
*
|
|
22
|
+
* `parseFields` is line-based, and the grammar never says so. Writing two
|
|
23
|
+
* fields on one line — `agent a { model: sonnet retry: 3 }` — makes the first
|
|
24
|
+
* key take the whole rest of the line as its value (`"sonnet retry: 3"`), and
|
|
25
|
+
* nothing catches it. The value is then silently wrong: a model name that does
|
|
26
|
+
* not exist, a version string carrying a description, an action `kind` that
|
|
27
|
+
* fails its enum for a confusing reason.
|
|
28
|
+
*
|
|
29
|
+
* Detection: an UNQUOTED value containing whitespace followed by
|
|
30
|
+
* `identifier:` is almost certainly a swallowed field. Quoted values are
|
|
31
|
+
* exempt (a description may legitimately contain a colon), and a leading
|
|
32
|
+
* scheme like `https://` is not matched because it has no preceding
|
|
33
|
+
* whitespace.
|
|
34
|
+
*/
|
|
35
|
+
function detectSwallowedFields(fields, nodeId, out) {
|
|
36
|
+
for (const [key, raw] of Object.entries(fields)) {
|
|
37
|
+
if (typeof raw !== "string")
|
|
38
|
+
continue;
|
|
39
|
+
const value = raw.trim();
|
|
40
|
+
// Free-prose fields whose quotes have already been stripped are exempt: a
|
|
41
|
+
// colon inside a description is ordinary English ("Demo: agent pipeline"),
|
|
42
|
+
// and once the quotes are gone there is no way to tell prose from a
|
|
43
|
+
// swallow. Where quotes SURVIVE parsing — every block except `meta` — the
|
|
44
|
+
// check below still catches `description: "k" model: sonnet`.
|
|
45
|
+
const PROSE_FIELDS = new Set(["description", "role", "generates", "termination"]);
|
|
46
|
+
if (PROSE_FIELDS.has(key) && !value.startsWith('"'))
|
|
47
|
+
continue;
|
|
48
|
+
// A balanced brace block is a NESTED OBJECT, not a swallow. The single-line
|
|
49
|
+
// form is spec-legal and parses correctly — `spec/grammar.md:117` gives
|
|
50
|
+
// `outputs: { depth: 1 | 2 | 3 }` verbatim. Flagging it rejected valid
|
|
51
|
+
// topologies, which is worse than missing one.
|
|
52
|
+
if (value.startsWith("{") && value.endsWith("}"))
|
|
53
|
+
continue;
|
|
54
|
+
// Same for a list.
|
|
55
|
+
if (value.startsWith("[") && value.endsWith("]"))
|
|
56
|
+
continue;
|
|
57
|
+
// A WELL-FORMED quoted string is exempt — a description may contain a
|
|
58
|
+
// colon. `"1.0.0" description: "x"` is NOT well-formed: it is one quoted
|
|
59
|
+
// token followed by more content, i.e. a swallowed field.
|
|
60
|
+
if (/^"(?:[^"\\]|\\.)*"$/.test(value))
|
|
61
|
+
continue;
|
|
62
|
+
const search = value.startsWith('"')
|
|
63
|
+
? value.replace(/^"(?:[^"\\]|\\.)*"/, "")
|
|
64
|
+
: value;
|
|
65
|
+
const m = /\s([a-zA-Z][a-zA-Z0-9_-]*):/.exec(search.startsWith(" ") ? search : " " + search);
|
|
66
|
+
if (!m)
|
|
67
|
+
continue;
|
|
68
|
+
out.push({
|
|
69
|
+
rule: "V90",
|
|
70
|
+
level: "error",
|
|
71
|
+
message: `"${nodeId}" field \`${key}\` has the value \`${value}\`, which looks like it swallowed the ` +
|
|
72
|
+
`field \`${m[1]}\` — fields must be one per line`,
|
|
73
|
+
node: nodeId,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Placeholders the parser injects for REQUIRED fields that were not declared.
|
|
79
|
+
*
|
|
80
|
+
* The parser cannot leave them undefined without changing non-optional AST
|
|
81
|
+
* types, so it substitutes a sentinel — and that sentinel then satisfied every
|
|
82
|
+
* "is it present?" check downstream. V91 recognises them and reports the field
|
|
83
|
+
* as missing, which is what the spec says it is.
|
|
84
|
+
*/
|
|
85
|
+
export const MISSING = {
|
|
86
|
+
/** `orchestrator.model` — spec/grammar.md, orchestrator field table. */
|
|
87
|
+
model: "unknown",
|
|
88
|
+
/** `meta.version` — spec/grammar.md:282, Required: yes. */
|
|
89
|
+
version: "0.0.0",
|
|
90
|
+
};
|
|
19
91
|
/** Convert a kebab-case or snake_case identifier to a Title Case label. */
|
|
20
92
|
function toLabel(id) {
|
|
21
93
|
return id
|
|
@@ -203,8 +275,11 @@ export function parseMeta(body) {
|
|
|
203
275
|
/**
|
|
204
276
|
* Parse the `orchestrator { ... }` block into an {@link OrchestratorNode}.
|
|
205
277
|
*/
|
|
206
|
-
export function parseOrchestrator(body) {
|
|
278
|
+
export function parseOrchestrator(body, _blockFieldMisuseWarnings) {
|
|
207
279
|
const fields = parseFields(body);
|
|
280
|
+
if (_blockFieldMisuseWarnings) {
|
|
281
|
+
detectSwallowedFields(fields, "orchestrator", _blockFieldMisuseWarnings);
|
|
282
|
+
}
|
|
208
283
|
const handles = parseMultilineList(body, "handles");
|
|
209
284
|
const outputs = parseOutputsBlock(body);
|
|
210
285
|
const node = {
|
|
@@ -247,8 +322,11 @@ export function parseRoles(body) {
|
|
|
247
322
|
/**
|
|
248
323
|
* Parse an `action <id> { ... }` block into an {@link ActionNode}.
|
|
249
324
|
*/
|
|
250
|
-
export function parseAction(id, body) {
|
|
325
|
+
export function parseAction(id, body, _blockFieldMisuseWarnings) {
|
|
251
326
|
const fields = parseFields(body);
|
|
327
|
+
if (_blockFieldMisuseWarnings) {
|
|
328
|
+
detectSwallowedFields(fields, id, _blockFieldMisuseWarnings);
|
|
329
|
+
}
|
|
252
330
|
const commands = parseMultilineList(body, "commands");
|
|
253
331
|
const node = {
|
|
254
332
|
id,
|
|
@@ -279,7 +357,7 @@ export function parseAction(id, body) {
|
|
|
279
357
|
* @param roles - Role descriptions from the `roles` block, used to attach a
|
|
280
358
|
* role description to matching agents.
|
|
281
359
|
*/
|
|
282
|
-
export function parseAgent(id, body, roles) {
|
|
360
|
+
export function parseAgent(id, body, roles, _blockFieldMisuseWarnings) {
|
|
283
361
|
const fields = parseFields(body);
|
|
284
362
|
const tools = parseMultilineList(body, "tools");
|
|
285
363
|
const skills = parseMultilineList(body, "skills");
|
|
@@ -298,7 +376,24 @@ export function parseAgent(id, body, roles) {
|
|
|
298
376
|
node.model = fields.model;
|
|
299
377
|
if (fields.permissions)
|
|
300
378
|
node.permissions = fields.permissions;
|
|
301
|
-
// Extract prompt from prompt {} block (not KV pair)
|
|
379
|
+
// Extract prompt from prompt {} block (not KV pair).
|
|
380
|
+
//
|
|
381
|
+
// V89: `agent.prompt` is a BLOCK per spec/grammar.md:350. The string form
|
|
382
|
+
// (`prompt: "path.md"`) belongs to `skill`, a different node type
|
|
383
|
+
// (spec/grammar.md:1377). A KV `prompt` on an agent is silently unparseable,
|
|
384
|
+
// so flag it rather than dropping the author's instructions on the floor.
|
|
385
|
+
if (fields.prompt) {
|
|
386
|
+
_blockFieldMisuseWarnings?.push({
|
|
387
|
+
rule: "V89",
|
|
388
|
+
level: "error",
|
|
389
|
+
message: `agent "${id}" uses \`prompt:\` as a key-value field, but agent prompts must be a \`prompt { }\` block — ` +
|
|
390
|
+
`this value is ignored and the agent will have no instructions`,
|
|
391
|
+
node: id,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
if (_blockFieldMisuseWarnings) {
|
|
395
|
+
detectSwallowedFields(fields, id, _blockFieldMisuseWarnings);
|
|
396
|
+
}
|
|
302
397
|
const promptBlock = extractBlock(body, "prompt");
|
|
303
398
|
if (promptBlock) {
|
|
304
399
|
node.prompt = dedentBlock(promptBlock.body);
|
|
@@ -555,8 +650,11 @@ export function parseAgent(id, body, roles) {
|
|
|
555
650
|
/**
|
|
556
651
|
* Parse a `gate <id> { ... }` block into a {@link GateNode}.
|
|
557
652
|
*/
|
|
558
|
-
export function parseGate(id, body) {
|
|
653
|
+
export function parseGate(id, body, _blockFieldMisuseWarnings) {
|
|
559
654
|
const fields = parseFields(body);
|
|
655
|
+
if (_blockFieldMisuseWarnings) {
|
|
656
|
+
detectSwallowedFields(fields, id, _blockFieldMisuseWarnings);
|
|
657
|
+
}
|
|
560
658
|
const checks = parseMultilineList(body, "checks");
|
|
561
659
|
const node = {
|
|
562
660
|
id,
|
|
@@ -587,8 +685,11 @@ export function parseGate(id, body) {
|
|
|
587
685
|
/**
|
|
588
686
|
* Parse a `human <id> { ... }` block into a {@link HumanNode}.
|
|
589
687
|
*/
|
|
590
|
-
export function parseHuman(id, body) {
|
|
688
|
+
export function parseHuman(id, body, _blockFieldMisuseWarnings) {
|
|
591
689
|
const fields = parseFields(body);
|
|
690
|
+
if (_blockFieldMisuseWarnings) {
|
|
691
|
+
detectSwallowedFields(fields, id, _blockFieldMisuseWarnings);
|
|
692
|
+
}
|
|
592
693
|
const node = {
|
|
593
694
|
id,
|
|
594
695
|
type: "human",
|
|
@@ -605,8 +706,11 @@ export function parseHuman(id, body) {
|
|
|
605
706
|
/**
|
|
606
707
|
* Parse a `group <id> { ... }` block into a {@link GroupNode}.
|
|
607
708
|
*/
|
|
608
|
-
export function parseGroup(id, body) {
|
|
709
|
+
export function parseGroup(id, body, _blockFieldMisuseWarnings) {
|
|
609
710
|
const fields = parseFields(body);
|
|
711
|
+
if (_blockFieldMisuseWarnings) {
|
|
712
|
+
detectSwallowedFields(fields, id, _blockFieldMisuseWarnings);
|
|
713
|
+
}
|
|
610
714
|
const members = parseList(fields.members ?? "");
|
|
611
715
|
const node = {
|
|
612
716
|
id,
|
|
@@ -651,6 +755,24 @@ export function parseFlow(body, _edgeAttributeErrors) {
|
|
|
651
755
|
// Match annotation at end of line: [when ..., max N, per agent-id, tolerance: N, race, wait 30s, reflection]
|
|
652
756
|
// Must start with "when", "max", "per", "tolerance", "race", "wait", "join", "weight", or "reflection" to avoid matching fan-out lists.
|
|
653
757
|
const bracketMatch = trimmed.match(/\[(when\s+.+?|max\s+\d+.*?|per\s+\S+.*?|tolerance\s*:.+?|race.*?|wait\s+\S+.*?|join\s+\S+.*?|weight\s+\S+.*?|reflection.*?)\]\s*$/);
|
|
758
|
+
// V92: two bracket groups on one edge. The grammar defines ONE annotation
|
|
759
|
+
// with comma-separated attributes (`[when ..., max N]`). Written as
|
|
760
|
+
// `[when ...] [max N]`, the matcher below spans from the first `[` to the
|
|
761
|
+
// last `]` and produces a corrupt condition — literally
|
|
762
|
+
// `a.verdict == fail] [max 3` — with no error at all.
|
|
763
|
+
// Count only ANNOTATION brackets. A fan-out list is also bracketed —
|
|
764
|
+
// `analyzer -> [reporter, alerter] [when ...]` is legal — so testing for
|
|
765
|
+
// `] [` alone flagged valid topologies.
|
|
766
|
+
const ATTR = /\[\s*(?:when|max|per|tolerance|race|wait|join|weight|reflection)\b/g;
|
|
767
|
+
const annotationBrackets = (trimmed.match(ATTR) ?? []).length;
|
|
768
|
+
if (annotationBrackets > 1 && _edgeAttributeErrors) {
|
|
769
|
+
_edgeAttributeErrors.push({
|
|
770
|
+
rule: "V92",
|
|
771
|
+
level: "error",
|
|
772
|
+
message: "Edge has two bracket groups — attributes go in ONE bracket, " +
|
|
773
|
+
`comma-separated: [when ..., max N] (line: ${trimmed})`,
|
|
774
|
+
});
|
|
775
|
+
}
|
|
654
776
|
if (bracketMatch) {
|
|
655
777
|
flowPart = trimmed.slice(0, bracketMatch.index).trim();
|
|
656
778
|
const annotation = bracketMatch[1];
|
|
@@ -1062,10 +1184,7 @@ export function parseMemory(body, _unknownSubBlockWarnings) {
|
|
|
1062
1184
|
];
|
|
1063
1185
|
const knownSubsSet = new Set([
|
|
1064
1186
|
...knownSubs,
|
|
1065
|
-
"store", "retrieval",
|
|
1066
|
-
// Sub-blocks within store {} and retrieval {} (not top-level memory subs,
|
|
1067
|
-
// but the V24 scanner sees them because it doesn't track depth)
|
|
1068
|
-
"embedding", "index", "ingestion", "search", "lifecycle", "backend-config", "scoring",
|
|
1187
|
+
"store", "retrieval", // the two typed top-level blocks
|
|
1069
1188
|
]);
|
|
1070
1189
|
for (const name of knownSubs) {
|
|
1071
1190
|
const block = extractBlock(body, name);
|
|
@@ -1098,19 +1217,47 @@ export function parseMemory(body, _unknownSubBlockWarnings) {
|
|
|
1098
1217
|
continue;
|
|
1099
1218
|
retrievals.push(parseRetrieval(block.id, block.body));
|
|
1100
1219
|
}
|
|
1101
|
-
// Detect unknown sub-blocks
|
|
1220
|
+
// Detect unknown sub-blocks. Only consider TOP-LEVEL blocks of the memory
|
|
1221
|
+
// body — names nested inside a `store {}` or `retrieval {}` (e.g. a brain
|
|
1222
|
+
// store's `sources { gmail {} }`) are that block's own concern, not memory
|
|
1223
|
+
// sub-blocks. Walk the body tracking brace depth so we only flag depth-0 names.
|
|
1102
1224
|
if (_unknownSubBlockWarnings) {
|
|
1103
|
-
|
|
1104
|
-
let
|
|
1105
|
-
while (
|
|
1106
|
-
const
|
|
1107
|
-
if (
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
message: `Unknown sub-block "${name}" in memory block — known sub-blocks are: ${knownSubs.join(", ")}`,
|
|
1112
|
-
});
|
|
1225
|
+
let depth = 0;
|
|
1226
|
+
let i = 0;
|
|
1227
|
+
while (i < body.length) {
|
|
1228
|
+
const ch = body[i];
|
|
1229
|
+
if (ch === "}") {
|
|
1230
|
+
depth = Math.max(0, depth - 1);
|
|
1231
|
+
i++;
|
|
1232
|
+
continue;
|
|
1113
1233
|
}
|
|
1234
|
+
if (ch === "{") {
|
|
1235
|
+
depth++;
|
|
1236
|
+
i++;
|
|
1237
|
+
continue;
|
|
1238
|
+
}
|
|
1239
|
+
// At top level, match a block header before `{`. Handles both the bare
|
|
1240
|
+
// form (`workspace {`) and the typed-block form (`store brain {`,
|
|
1241
|
+
// `retrieval smart {`) — in the latter the FIRST token is the keyword.
|
|
1242
|
+
if (depth === 0) {
|
|
1243
|
+
const rest = body.slice(i);
|
|
1244
|
+
const m = /^([a-zA-Z][a-zA-Z0-9_-]*)(?:\s+[a-zA-Z][a-zA-Z0-9_-]*)?\s*\{/.exec(rest);
|
|
1245
|
+
if (m && (i === 0 || /\s/.test(body[i - 1]))) {
|
|
1246
|
+
const name = m[1]; // the leading keyword (store / retrieval / workspace / …)
|
|
1247
|
+
if (!knownSubsSet.has(name)) {
|
|
1248
|
+
_unknownSubBlockWarnings.push({
|
|
1249
|
+
rule: "V24",
|
|
1250
|
+
level: "warning",
|
|
1251
|
+
message: `Unknown sub-block "${name}" in memory block — known sub-blocks are: ${knownSubs.join(", ")}`,
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
// Advance into the block (past the `{`) so its body is depth-counted.
|
|
1255
|
+
i += m[0].length;
|
|
1256
|
+
depth++;
|
|
1257
|
+
continue;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
i++;
|
|
1114
1261
|
}
|
|
1115
1262
|
}
|
|
1116
1263
|
return { memory, stores, retrievals };
|
|
@@ -2026,7 +2173,11 @@ export function parse(source) {
|
|
|
2026
2173
|
const topBody = stripComments(topoBlock.body);
|
|
2027
2174
|
// --- Meta ---
|
|
2028
2175
|
const metaBlock = extractBlock(topBody, "meta");
|
|
2176
|
+
// V89/V90: collected while parsing blocks — a field written in a form the
|
|
2177
|
+
// grammar does not define. Attached to the AST below.
|
|
2178
|
+
const blockFieldMisuseWarnings = [];
|
|
2029
2179
|
const metaFields = metaBlock ? parseMeta(metaBlock.body) : {};
|
|
2180
|
+
detectSwallowedFields(metaFields, "<meta>", blockFieldMisuseWarnings);
|
|
2030
2181
|
const topology = {
|
|
2031
2182
|
name: header.name,
|
|
2032
2183
|
version: metaFields.version ?? "0.0.0",
|
|
@@ -2047,20 +2198,20 @@ export function parse(source) {
|
|
|
2047
2198
|
// Orchestrator
|
|
2048
2199
|
const orchBlock = extractBlock(topBody, "orchestrator");
|
|
2049
2200
|
if (orchBlock) {
|
|
2050
|
-
nodes.push(parseOrchestrator(orchBlock.body));
|
|
2201
|
+
nodes.push(parseOrchestrator(orchBlock.body, blockFieldMisuseWarnings));
|
|
2051
2202
|
}
|
|
2052
2203
|
// Actions
|
|
2053
2204
|
const actionBlocks = extractAllBlocks(topBody, "action");
|
|
2054
2205
|
for (const block of actionBlocks) {
|
|
2055
2206
|
if (block.id) {
|
|
2056
|
-
nodes.push(parseAction(block.id, block.body));
|
|
2207
|
+
nodes.push(parseAction(block.id, block.body, blockFieldMisuseWarnings));
|
|
2057
2208
|
}
|
|
2058
2209
|
}
|
|
2059
2210
|
// Agents
|
|
2060
2211
|
const agentBlocks = extractAllBlocks(topBody, "agent");
|
|
2061
2212
|
for (const block of agentBlocks) {
|
|
2062
2213
|
if (block.id) {
|
|
2063
|
-
nodes.push(parseAgent(block.id, block.body, roles));
|
|
2214
|
+
nodes.push(parseAgent(block.id, block.body, roles, blockFieldMisuseWarnings));
|
|
2064
2215
|
}
|
|
2065
2216
|
}
|
|
2066
2217
|
// Gates
|
|
@@ -2069,7 +2220,7 @@ export function parse(source) {
|
|
|
2069
2220
|
const gateBlocks = extractAllBlocks(gatesBlock.body, "gate");
|
|
2070
2221
|
for (const block of gateBlocks) {
|
|
2071
2222
|
if (block.id) {
|
|
2072
|
-
nodes.push(parseGate(block.id, block.body));
|
|
2223
|
+
nodes.push(parseGate(block.id, block.body, blockFieldMisuseWarnings));
|
|
2073
2224
|
}
|
|
2074
2225
|
}
|
|
2075
2226
|
}
|
|
@@ -2077,14 +2228,14 @@ export function parse(source) {
|
|
|
2077
2228
|
const humanBlocks = extractAllBlocks(topBody, "human");
|
|
2078
2229
|
for (const block of humanBlocks) {
|
|
2079
2230
|
if (block.id) {
|
|
2080
|
-
nodes.push(parseHuman(block.id, block.body));
|
|
2231
|
+
nodes.push(parseHuman(block.id, block.body, blockFieldMisuseWarnings));
|
|
2081
2232
|
}
|
|
2082
2233
|
}
|
|
2083
2234
|
// Group chat nodes
|
|
2084
2235
|
const groupBlocks = extractAllBlocks(topBody, "group");
|
|
2085
2236
|
for (const block of groupBlocks) {
|
|
2086
2237
|
if (block.id) {
|
|
2087
|
-
nodes.push(parseGroup(block.id, block.body));
|
|
2238
|
+
nodes.push(parseGroup(block.id, block.body, blockFieldMisuseWarnings));
|
|
2088
2239
|
}
|
|
2089
2240
|
}
|
|
2090
2241
|
// --- Skills ---
|
|
@@ -2265,6 +2416,10 @@ export function parse(source) {
|
|
|
2265
2416
|
if (unknownMemorySubBlockWarnings.length > 0) {
|
|
2266
2417
|
ast._unknownMemorySubBlockWarnings = unknownMemorySubBlockWarnings;
|
|
2267
2418
|
}
|
|
2419
|
+
// Attach V89 block-vs-KV field misuse errors for the validator to consume.
|
|
2420
|
+
if (blockFieldMisuseWarnings.length > 0) {
|
|
2421
|
+
ast._blockFieldMisuseWarnings = blockFieldMisuseWarnings;
|
|
2422
|
+
}
|
|
2268
2423
|
// Attach source map for line number tracking in validation results.
|
|
2269
2424
|
if (Object.keys(sourceMap).length > 0) {
|
|
2270
2425
|
ast._sourceMap = sourceMap;
|