agentopology 0.2.4 → 0.3.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.
Files changed (64) hide show
  1. package/README.md +92 -10
  2. package/dist/bindings/__tests__/bindings.test.js +2 -2
  3. package/dist/bindings/__tests__/claude-code-hybrid.test.d.ts +2 -0
  4. package/dist/bindings/__tests__/claude-code-hybrid.test.d.ts.map +1 -0
  5. package/dist/bindings/__tests__/claude-code-hybrid.test.js +152 -0
  6. package/dist/bindings/__tests__/claude-code-hybrid.test.js.map +1 -0
  7. package/dist/bindings/__tests__/claude-workflow.test.d.ts +2 -0
  8. package/dist/bindings/__tests__/claude-workflow.test.d.ts.map +1 -0
  9. package/dist/bindings/__tests__/claude-workflow.test.js +204 -0
  10. package/dist/bindings/__tests__/claude-workflow.test.js.map +1 -0
  11. package/dist/bindings/__tests__/hybrid-e2e.test.d.ts +15 -0
  12. package/dist/bindings/__tests__/hybrid-e2e.test.d.ts.map +1 -0
  13. package/dist/bindings/__tests__/hybrid-e2e.test.js +94 -0
  14. package/dist/bindings/__tests__/hybrid-e2e.test.js.map +1 -0
  15. package/dist/bindings/claude-code.d.ts.map +1 -1
  16. package/dist/bindings/claude-code.js +407 -3
  17. package/dist/bindings/claude-code.js.map +1 -1
  18. package/dist/bindings/claude-workflow.d.ts +28 -0
  19. package/dist/bindings/claude-workflow.d.ts.map +1 -0
  20. package/dist/bindings/claude-workflow.js +684 -0
  21. package/dist/bindings/claude-workflow.js.map +1 -0
  22. package/dist/bindings/index.d.ts +1 -0
  23. package/dist/bindings/index.d.ts.map +1 -1
  24. package/dist/bindings/index.js +3 -0
  25. package/dist/bindings/index.js.map +1 -1
  26. package/dist/bindings/lib/seam.d.ts +35 -0
  27. package/dist/bindings/lib/seam.d.ts.map +1 -0
  28. package/dist/bindings/lib/seam.js +42 -0
  29. package/dist/bindings/lib/seam.js.map +1 -0
  30. package/dist/cli/index.js +195 -2
  31. package/dist/cli/index.js.map +1 -1
  32. package/dist/parser/__tests__/parser.test.js +170 -0
  33. package/dist/parser/__tests__/parser.test.js.map +1 -1
  34. package/dist/parser/ast.d.ts +39 -0
  35. package/dist/parser/ast.d.ts.map +1 -1
  36. package/dist/parser/index.d.ts.map +1 -1
  37. package/dist/parser/index.js +58 -2
  38. package/dist/parser/index.js.map +1 -1
  39. package/dist/parser/validator.d.ts.map +1 -1
  40. package/dist/parser/validator.js +66 -3
  41. package/dist/parser/validator.js.map +1 -1
  42. package/dist/visualizer/__tests__/brain.test.d.ts +2 -0
  43. package/dist/visualizer/__tests__/brain.test.d.ts.map +1 -0
  44. package/dist/visualizer/__tests__/brain.test.js +138 -0
  45. package/dist/visualizer/__tests__/brain.test.js.map +1 -0
  46. package/dist/visualizer/__tests__/topology-brain-link.test.d.ts +2 -0
  47. package/dist/visualizer/__tests__/topology-brain-link.test.d.ts.map +1 -0
  48. package/dist/visualizer/__tests__/topology-brain-link.test.js +41 -0
  49. package/dist/visualizer/__tests__/topology-brain-link.test.js.map +1 -0
  50. package/dist/visualizer/brain.d.ts +97 -0
  51. package/dist/visualizer/brain.d.ts.map +1 -0
  52. package/dist/visualizer/brain.js +473 -0
  53. package/dist/visualizer/brain.js.map +1 -0
  54. package/dist/visualizer/index.d.ts +19 -1
  55. package/dist/visualizer/index.d.ts.map +1 -1
  56. package/dist/visualizer/index.js +26 -11
  57. package/dist/visualizer/index.js.map +1 -1
  58. package/examples/company-brain-team.at +241 -0
  59. package/examples/company-brain.at +104 -0
  60. package/examples/logos/gmail.svg +1 -0
  61. package/examples/logos/slack.svg +1 -0
  62. package/examples/matchmat-ship.at +249 -0
  63. package/package.json +1 -1
  64. package/spec/grammar.md +42 -3
@@ -7059,4 +7059,174 @@ describe("Auth block in providers (F42)", () => {
7059
7059
  });
7060
7060
  });
7061
7061
  });
7062
+ describe("Company brain — pattern, brain store type, custody primitive", () => {
7063
+ const brainSrc = `topology cb : [brain] {
7064
+ meta { version: "1.0.0" description: "x" }
7065
+ memory {
7066
+ store brain {
7067
+ type: brain
7068
+ description: "the company brain"
7069
+ path: "brain/"
7070
+ format: obsidian
7071
+ }
7072
+ }
7073
+ agent librarian {
7074
+ model: sonnet
7075
+ description: "keeps the brain"
7076
+ tools: [Read, Write, Grep]
7077
+ custodian-of: [brain] {
7078
+ does: [link, tag, index, dedupe]
7079
+ }
7080
+ }
7081
+ agent researcher {
7082
+ model: sonnet
7083
+ description: "reads the brain"
7084
+ tools: [Read, Grep]
7085
+ memory: [brain]
7086
+ }
7087
+ action intake {
7088
+ kind: inline
7089
+ description: "in"
7090
+ }
7091
+ flow {
7092
+ intake -> librarian
7093
+ librarian -> researcher
7094
+ }
7095
+ }`;
7096
+ it("parses a brain-type store with files backend default and format", () => {
7097
+ const ast = parse(brainSrc);
7098
+ const store = (ast.stores ?? []).find((s) => s.id === "brain");
7099
+ expect(store).toBeDefined();
7100
+ expect(store.type).toBe("brain");
7101
+ expect(store.backend).toBe("files"); // file-native default, not lancedb
7102
+ expect(store.format).toBe("obsidian");
7103
+ expect(store.path).toBe("brain/");
7104
+ });
7105
+ it("parses custodian-of and the optional does verbs on an agent", () => {
7106
+ const ast = parse(brainSrc);
7107
+ const librarian = ast.nodes.find((n) => n.id === "librarian");
7108
+ expect(librarian.custodianOf).toEqual(["brain"]);
7109
+ expect(librarian.custodianDoes).toEqual(["link", "tag", "index", "dedupe"]);
7110
+ });
7111
+ it("custody is optional — bare custodian-of with no verbs parses", () => {
7112
+ const src = brainSrc.replace("custodian-of: [brain] {\n does: [link, tag, index, dedupe]\n }", "custodian-of: [brain]");
7113
+ const ast = parse(src);
7114
+ const librarian = ast.nodes.find((n) => n.id === "librarian");
7115
+ expect(librarian.custodianOf).toEqual(["brain"]);
7116
+ expect(librarian.custodianDoes).toBeUndefined();
7117
+ });
7118
+ it("the brain pattern tag is recognized (no V2 reserved-keyword error)", () => {
7119
+ const ast = parse(brainSrc);
7120
+ expect(ast.topology.patterns).toContain("brain");
7121
+ const results = validate(ast);
7122
+ expect(results.filter((r) => r.level === "error")).toHaveLength(0);
7123
+ });
7124
+ it("V81: a brain store needs no DB backend (exempt from backend enum)", () => {
7125
+ const ast = parse(brainSrc);
7126
+ const results = validate(ast);
7127
+ expect(results.filter((r) => r.rule === "V81")).toHaveLength(0);
7128
+ });
7129
+ it("V88: custodian-of referencing an undeclared store is an error", () => {
7130
+ const ast = minimalASTForWave6({
7131
+ nodes: [
7132
+ { id: "orchestrator", type: "orchestrator", label: "O", model: "opus", handles: ["intake"] },
7133
+ { id: "intake", type: "action", label: "Intake" },
7134
+ { id: "keeper", type: "agent", label: "Keeper", model: "sonnet", custodianOf: ["ghost-store"] },
7135
+ ],
7136
+ edges: [{ from: "intake", to: "keeper", condition: null, maxIterations: null }],
7137
+ stores: [{ id: "brain", type: "brain", backend: "files" }],
7138
+ retrievals: [],
7139
+ });
7140
+ const v88 = validate(ast).filter((r) => r.rule === "V88" && r.level === "error");
7141
+ expect(v88).toHaveLength(1);
7142
+ expect(v88[0].message).toContain("ghost-store");
7143
+ });
7144
+ it("V88: custody over a declared brain store is clean", () => {
7145
+ const ast = minimalASTForWave6({
7146
+ nodes: [
7147
+ { id: "orchestrator", type: "orchestrator", label: "O", model: "opus", handles: ["intake"] },
7148
+ { id: "intake", type: "action", label: "Intake" },
7149
+ { id: "keeper", type: "agent", label: "Keeper", model: "sonnet", custodianOf: ["brain"] },
7150
+ ],
7151
+ edges: [{ from: "intake", to: "keeper", condition: null, maxIterations: null }],
7152
+ stores: [{ id: "brain", type: "brain", backend: "files" }],
7153
+ retrievals: [],
7154
+ });
7155
+ const v88 = validate(ast).filter((r) => r.rule === "V88");
7156
+ expect(v88).toHaveLength(0);
7157
+ });
7158
+ it("V88: custody over a non-brain store is a warning, not an error", () => {
7159
+ const ast = minimalASTForWave6({
7160
+ nodes: [
7161
+ { id: "orchestrator", type: "orchestrator", label: "O", model: "opus", handles: ["intake"] },
7162
+ { id: "intake", type: "action", label: "Intake" },
7163
+ { id: "keeper", type: "agent", label: "Keeper", model: "sonnet", custodianOf: ["kg"] },
7164
+ ],
7165
+ edges: [{ from: "intake", to: "keeper", condition: null, maxIterations: null }],
7166
+ stores: [{ id: "kg", type: "graph", backend: "kuzu" }],
7167
+ retrievals: [],
7168
+ });
7169
+ const v88 = validate(ast).filter((r) => r.rule === "V88");
7170
+ expect(v88).toHaveLength(1);
7171
+ expect(v88[0].level).toBe("warning");
7172
+ });
7173
+ it("parses a brain store's presentation-only `sources` block", () => {
7174
+ const src = `topology cb : [brain] {
7175
+ meta { version: "1.0.0" description: "x" }
7176
+ memory {
7177
+ store brain {
7178
+ type: brain
7179
+ path: "brain/"
7180
+ sources {
7181
+ gmail {
7182
+ color: "#EA4335"
7183
+ icon: "./logos/gmail.svg"
7184
+ }
7185
+ slack {
7186
+ color: "#4A154B"
7187
+ }
7188
+ }
7189
+ }
7190
+ }
7191
+ agent lib { model: sonnet description: "k" tools: [Read, Write] custodian-of: [brain] }
7192
+ action intake {
7193
+ kind: inline
7194
+ description: "in"
7195
+ }
7196
+ flow { intake -> lib }
7197
+ }`;
7198
+ const ast = parse(src);
7199
+ const store = (ast.stores ?? []).find((s) => s.id === "brain");
7200
+ expect(store?.sources).toEqual({
7201
+ gmail: { color: "#EA4335", icon: "./logos/gmail.svg" },
7202
+ slack: { color: "#4A154B" },
7203
+ });
7204
+ // It is presentation-only — purely data on the AST, no validation error.
7205
+ expect(validate(ast).filter((r) => r.level === "error")).toHaveLength(0);
7206
+ });
7207
+ it("round-trips the company-brain.at example with no validation errors", () => {
7208
+ const src = readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), "../../../examples/company-brain.at"), "utf8");
7209
+ const ast = parse(src);
7210
+ const errors = validate(ast).filter((r) => r.level === "error");
7211
+ expect(errors).toHaveLength(0);
7212
+ });
7213
+ it("round-trips the team-of-N company-brain-team.at example cleanly", () => {
7214
+ const src = readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), "../../../examples/company-brain-team.at"), "utf8");
7215
+ const ast = parse(src);
7216
+ const errors = validate(ast).filter((r) => r.level === "error");
7217
+ expect(errors).toHaveLength(0);
7218
+ });
7219
+ it("team example: two distinct custodians of the same brain, with different verbs", () => {
7220
+ const src = readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), "../../../examples/company-brain-team.at"), "utf8");
7221
+ const ast = parse(src);
7222
+ const librarian = ast.nodes.find((n) => n.id === "librarian");
7223
+ const auditor = ast.nodes.find((n) => n.id === "auditor");
7224
+ // Both own the same brain store — custody is not exclusive.
7225
+ expect(librarian.custodianOf).toEqual(["brain"]);
7226
+ expect(auditor.custodianOf).toEqual(["brain"]);
7227
+ // But they perform different maintenance verbs (different roles, same layer).
7228
+ expect(librarian.custodianDoes).toEqual(["link", "tag", "index", "dedupe"]);
7229
+ expect(auditor.custodianDoes).toEqual(["heal", "dedupe", "index"]);
7230
+ });
7231
+ });
7062
7232
  //# sourceMappingURL=parser.test.js.map