agentopology 0.1.1 → 0.2.1

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 +8 -6
  2. package/dist/analyzer/index.d.ts +1 -0
  3. package/dist/analyzer/index.d.ts.map +1 -1
  4. package/dist/analyzer/index.js +35 -0
  5. package/dist/analyzer/index.js.map +1 -1
  6. package/dist/bindings/__tests__/bindings.test.js +11 -11
  7. package/dist/bindings/__tests__/bindings.test.js.map +1 -1
  8. package/dist/bindings/claude-code.d.ts.map +1 -1
  9. package/dist/bindings/claude-code.js +339 -85
  10. package/dist/bindings/claude-code.js.map +1 -1
  11. package/dist/bindings/openclaw.d.ts.map +1 -1
  12. package/dist/bindings/openclaw.js +367 -5
  13. package/dist/bindings/openclaw.js.map +1 -1
  14. package/dist/cli/index.js +2 -2
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/docs/__tests__/docs.test.js +2 -1
  17. package/dist/docs/__tests__/docs.test.js.map +1 -1
  18. package/dist/docs/content.d.ts.map +1 -1
  19. package/dist/docs/content.js +730 -4
  20. package/dist/docs/content.js.map +1 -1
  21. package/dist/exporters/markdown.d.ts.map +1 -1
  22. package/dist/exporters/markdown.js +165 -0
  23. package/dist/exporters/markdown.js.map +1 -1
  24. package/dist/exporters/mermaid.d.ts.map +1 -1
  25. package/dist/exporters/mermaid.js +30 -0
  26. package/dist/exporters/mermaid.js.map +1 -1
  27. package/dist/import/__tests__/import.test.js +556 -2
  28. package/dist/import/__tests__/import.test.js.map +1 -1
  29. package/dist/import/claude-code.d.ts.map +1 -1
  30. package/dist/import/claude-code.js +2 -0
  31. package/dist/import/claude-code.js.map +1 -1
  32. package/dist/import/index.d.ts +1 -0
  33. package/dist/import/index.d.ts.map +1 -1
  34. package/dist/import/index.js +7 -1
  35. package/dist/import/index.js.map +1 -1
  36. package/dist/import/openclaw.d.ts +70 -0
  37. package/dist/import/openclaw.d.ts.map +1 -0
  38. package/dist/import/openclaw.js +1060 -0
  39. package/dist/import/openclaw.js.map +1 -0
  40. package/dist/import/serializer.d.ts.map +1 -1
  41. package/dist/import/serializer.js +171 -4
  42. package/dist/import/serializer.js.map +1 -1
  43. package/dist/parser/__tests__/parser.test.js +463 -0
  44. package/dist/parser/__tests__/parser.test.js.map +1 -1
  45. package/dist/parser/ast.d.ts +122 -0
  46. package/dist/parser/ast.d.ts.map +1 -1
  47. package/dist/parser/index.d.ts +14 -2
  48. package/dist/parser/index.d.ts.map +1 -1
  49. package/dist/parser/index.js +226 -5
  50. package/dist/parser/index.js.map +1 -1
  51. package/dist/parser/validator.d.ts +1 -1
  52. package/dist/parser/validator.d.ts.map +1 -1
  53. package/dist/parser/validator.js +214 -1
  54. package/dist/parser/validator.js.map +1 -1
  55. package/dist/visualizer/index.d.ts.map +1 -1
  56. package/dist/visualizer/index.js +104 -16
  57. package/dist/visualizer/index.js.map +1 -1
  58. package/examples/memory-system.at +162 -0
  59. package/examples/script-agent-pipeline.at +65 -0
  60. package/package.json +2 -1
  61. package/skill/SKILL.md +7 -7
  62. package/spec/grammar.md +345 -10
  63. package/spec/reserved-keywords.md +25 -1
  64. package/spec/validation.md +171 -2
@@ -739,6 +739,8 @@ describe("Validator", () => {
739
739
  includes: [],
740
740
  checkpoint: null,
741
741
  artifacts: [],
742
+ stores: [],
743
+ retrievals: [],
742
744
  ...overrides,
743
745
  };
744
746
  }
@@ -2358,6 +2360,8 @@ describe("Error Handling (timeout, on-fail, retry block)", () => {
2358
2360
  includes: [],
2359
2361
  checkpoint: null,
2360
2362
  artifacts: [],
2363
+ stores: [],
2364
+ retrievals: [],
2361
2365
  ...overrides,
2362
2366
  };
2363
2367
  }
@@ -3127,6 +3131,8 @@ describe("Wave 2: Validator rules V39-V45", () => {
3127
3131
  includes: [],
3128
3132
  checkpoint: null,
3129
3133
  artifacts: [],
3134
+ stores: [],
3135
+ retrievals: [],
3130
3136
  ...overrides,
3131
3137
  };
3132
3138
  }
@@ -3645,6 +3651,8 @@ topology test : [pipeline] {
3645
3651
  includes: [],
3646
3652
  checkpoint: null,
3647
3653
  artifacts: [],
3654
+ stores: [],
3655
+ retrievals: [],
3648
3656
  ...overrides,
3649
3657
  };
3650
3658
  }
@@ -3721,6 +3729,8 @@ topology test : [pipeline] {
3721
3729
  includes: [],
3722
3730
  checkpoint: null,
3723
3731
  artifacts: [],
3732
+ stores: [],
3733
+ retrievals: [],
3724
3734
  ...overrides,
3725
3735
  };
3726
3736
  }
@@ -4145,6 +4155,8 @@ describe("Wave 3: Validator rules V48-V50 (observability)", () => {
4145
4155
  includes: [],
4146
4156
  checkpoint: null,
4147
4157
  artifacts: [],
4158
+ stores: [],
4159
+ retrievals: [],
4148
4160
  ...overrides,
4149
4161
  };
4150
4162
  }
@@ -4606,6 +4618,8 @@ topology simple : [pipeline] {
4606
4618
  includes: [],
4607
4619
  checkpoint: null,
4608
4620
  artifacts: [],
4621
+ stores: [],
4622
+ retrievals: [],
4609
4623
  ...overrides,
4610
4624
  };
4611
4625
  }
@@ -4912,6 +4926,8 @@ describe("Circuit Breaker (F26)", () => {
4912
4926
  includes: [],
4913
4927
  checkpoint: null,
4914
4928
  artifacts: [],
4929
+ stores: [],
4930
+ retrievals: [],
4915
4931
  ...overrides,
4916
4932
  };
4917
4933
  }
@@ -5323,6 +5339,8 @@ function minimalASTForWave6(overrides = {}) {
5323
5339
  includes: [],
5324
5340
  checkpoint: null,
5325
5341
  artifacts: [],
5342
+ stores: [],
5343
+ retrievals: [],
5326
5344
  ...overrides,
5327
5345
  };
5328
5346
  }
@@ -6510,5 +6528,450 @@ describe("Auth block in providers (F42)", () => {
6510
6528
  const v80 = results.filter((r) => r.rule === "V80");
6511
6529
  expect(v80.length).toBe(0);
6512
6530
  });
6531
+ // =========================================================================
6532
+ // Store and retrieval blocks (parser tests)
6533
+ // =========================================================================
6534
+ describe("store and retrieval blocks", () => {
6535
+ it("parses a basic store with type, backend, and path", () => {
6536
+ const src = `topology t : [pipeline] {\n meta { version: "1.0.0" }\n orchestrator { model: opus handles: [a] }\n action a { kind: inline }\n flow { a -> a }\n memory {\n store my-store {\n type: semantic\n backend: lancedb\n path: ".memory/"\n }\n }\n}`;
6537
+ const ast = parse(src);
6538
+ expect(ast.stores).toHaveLength(1);
6539
+ expect(ast.stores[0].id).toBe("my-store");
6540
+ expect(ast.stores[0].type).toBe("semantic");
6541
+ expect(ast.stores[0].backend).toBe("lancedb");
6542
+ expect(ast.stores[0].path).toBe(".memory/");
6543
+ });
6544
+ it("parses multiple stores in one memory block", () => {
6545
+ const src = `topology t : [pipeline] {\n meta { version: "1.0.0" }\n orchestrator { model: opus handles: [a] }\n action a { kind: inline }\n flow { a -> a }\n memory {\n store kb {\n type: semantic\n backend: lancedb\n }\n store relations {\n type: graph\n backend: kuzu\n }\n store history {\n type: episodic\n backend: sqlite-vec\n }\n }\n}`;
6546
+ const ast = parse(src);
6547
+ expect(ast.stores).toHaveLength(3);
6548
+ expect(ast.stores[0].id).toBe("kb");
6549
+ expect(ast.stores[0].type).toBe("semantic");
6550
+ expect(ast.stores[0].backend).toBe("lancedb");
6551
+ expect(ast.stores[1].id).toBe("relations");
6552
+ expect(ast.stores[1].type).toBe("graph");
6553
+ expect(ast.stores[1].backend).toBe("kuzu");
6554
+ expect(ast.stores[2].id).toBe("history");
6555
+ expect(ast.stores[2].type).toBe("episodic");
6556
+ expect(ast.stores[2].backend).toBe("sqlite-vec");
6557
+ });
6558
+ it("parses store with all sub-blocks", () => {
6559
+ const src = [
6560
+ "topology t : [pipeline] {",
6561
+ " meta { version: \"1.0.0\" }",
6562
+ " orchestrator { model: opus handles: [a] }",
6563
+ " action a { kind: inline }",
6564
+ " flow { a -> a }",
6565
+ " memory {",
6566
+ " store full-store {",
6567
+ " type: semantic",
6568
+ " backend: lancedb",
6569
+ " path: \".memory/full\"",
6570
+ " scope: agent",
6571
+ " embedding {",
6572
+ " provider: ollama",
6573
+ " model: nomic-embed-text",
6574
+ " dimensions: 1024",
6575
+ " }",
6576
+ " index {",
6577
+ " collection: \"full_collection\"",
6578
+ " metric: cosine",
6579
+ " }",
6580
+ " ingestion {",
6581
+ " chunking: recursive",
6582
+ " chunk-size: 512",
6583
+ " overlap: 50",
6584
+ " }",
6585
+ " search {",
6586
+ " strategy: hybrid",
6587
+ " rerank: true",
6588
+ " top-k: 10",
6589
+ " }",
6590
+ " lifecycle {",
6591
+ " retention: 90d",
6592
+ " decay-half-life: 30d",
6593
+ " consolidation: 0.8",
6594
+ " contradiction: overwrite",
6595
+ " audit-log: true",
6596
+ " }",
6597
+ " backend-config {",
6598
+ " region: \"us-east-1\"",
6599
+ " replicas: 3",
6600
+ " }",
6601
+ " }",
6602
+ " }",
6603
+ "}",
6604
+ ].join("\n");
6605
+ const ast = parse(src);
6606
+ expect(ast.stores).toHaveLength(1);
6607
+ const store = ast.stores[0];
6608
+ expect(store.id).toBe("full-store");
6609
+ expect(store.type).toBe("semantic");
6610
+ expect(store.backend).toBe("lancedb");
6611
+ expect(store.path).toBe(".memory/full");
6612
+ expect(store.scope).toBe("agent");
6613
+ // embedding
6614
+ expect(store.embedding).toBeDefined();
6615
+ expect(store.embedding.provider).toBe("ollama");
6616
+ expect(store.embedding.model).toBe("nomic-embed-text");
6617
+ expect(store.embedding.dimensions).toBe(1024);
6618
+ // index
6619
+ expect(store.index).toBeDefined();
6620
+ expect(store.index.collection).toBe("full_collection");
6621
+ expect(store.index.metric).toBe("cosine");
6622
+ // ingestion
6623
+ expect(store.ingestion).toBeDefined();
6624
+ expect(store.ingestion.chunking).toBe("recursive");
6625
+ expect(store.ingestion.chunkSize).toBe(512);
6626
+ expect(store.ingestion.overlap).toBe(50);
6627
+ // search
6628
+ expect(store.search).toBeDefined();
6629
+ expect(store.search.strategy).toBe("hybrid");
6630
+ expect(store.search.rerank).toBe(true);
6631
+ expect(store.search.topK).toBe(10);
6632
+ // lifecycle
6633
+ expect(store.lifecycle).toBeDefined();
6634
+ expect(store.lifecycle.retention).toBe("90d");
6635
+ expect(store.lifecycle.decayHalfLife).toBe("30d");
6636
+ expect(store.lifecycle.consolidation).toBe(0.8);
6637
+ expect(store.lifecycle.contradiction).toBe("overwrite");
6638
+ expect(store.lifecycle.auditLog).toBe(true);
6639
+ // backend-config
6640
+ expect(store.backendConfig).toBeDefined();
6641
+ expect(store.backendConfig["region"]).toBe("us-east-1");
6642
+ expect(store.backendConfig["replicas"]).toBe(3);
6643
+ });
6644
+ it("parses store with connection secret", () => {
6645
+ const src = `topology t : [pipeline] {\n meta { version: "1.0.0" }\n orchestrator { model: opus handles: [a] }\n action a { kind: inline }\n flow { a -> a }\n memory {\n store remote-db {\n type: semantic\n backend: pinecone\n connection: secret "DB_URL"\n }\n }\n}`;
6646
+ const ast = parse(src);
6647
+ expect(ast.stores).toHaveLength(1);
6648
+ expect(ast.stores[0].connection).toBe("DB_URL");
6649
+ });
6650
+ it("parses retrieval block with all fields", () => {
6651
+ const src = [
6652
+ "topology t : [pipeline] {",
6653
+ " meta { version: \"1.0.0\" }",
6654
+ " orchestrator { model: opus handles: [a] }",
6655
+ " action a { kind: inline }",
6656
+ " flow { a -> a }",
6657
+ " memory {",
6658
+ " store kb { type: semantic backend: lancedb }",
6659
+ " store history { type: episodic backend: sqlite-vec }",
6660
+ " retrieval planner {",
6661
+ " sources: [kb, history]",
6662
+ " budget: 4096",
6663
+ " paths: [semantic, graph]",
6664
+ " rerank: true",
6665
+ " diversity: true",
6666
+ " cache-hit-threshold: 0.92",
6667
+ " cache-hit-action: short-circuit",
6668
+ " }",
6669
+ " }",
6670
+ "}",
6671
+ ].join("\n");
6672
+ const ast = parse(src);
6673
+ expect(ast.retrievals).toHaveLength(1);
6674
+ const r = ast.retrievals[0];
6675
+ expect(r.id).toBe("planner");
6676
+ expect(r.sources).toEqual(["kb", "history"]);
6677
+ expect(r.budget).toBe(4096);
6678
+ expect(r.paths).toEqual(["semantic", "graph"]);
6679
+ expect(r.rerank).toBe(true);
6680
+ expect(r.diversity).toBe(true);
6681
+ expect(r.cacheHitThreshold).toBe(0.92);
6682
+ expect(r.cacheHitAction).toBe("short-circuit");
6683
+ });
6684
+ it("parses retrieval with scoring sub-block", () => {
6685
+ const src = [
6686
+ "topology t : [pipeline] {",
6687
+ " meta { version: \"1.0.0\" }",
6688
+ " orchestrator { model: opus handles: [a] }",
6689
+ " action a { kind: inline }",
6690
+ " flow { a -> a }",
6691
+ " memory {",
6692
+ " retrieval scored {",
6693
+ " sources: [kb]",
6694
+ " scoring {",
6695
+ " recency-weight: 0.3",
6696
+ " semantic-weight: 0.5",
6697
+ " importance-weight: 0.2",
6698
+ " }",
6699
+ " }",
6700
+ " }",
6701
+ "}",
6702
+ ].join("\n");
6703
+ const ast = parse(src);
6704
+ expect(ast.retrievals).toHaveLength(1);
6705
+ const r = ast.retrievals[0];
6706
+ expect(r.scoring).toBeDefined();
6707
+ expect(r.scoring.recencyWeight).toBe(0.3);
6708
+ expect(r.scoring.semanticWeight).toBe(0.5);
6709
+ expect(r.scoring.importanceWeight).toBe(0.2);
6710
+ });
6711
+ it("parses agent with memory and retrieval fields", () => {
6712
+ const src = [
6713
+ "topology t : [pipeline] {",
6714
+ " meta { version: \"1.0.0\" }",
6715
+ " orchestrator { model: opus handles: [intake] }",
6716
+ " action intake { kind: inline }",
6717
+ " agent worker {",
6718
+ " model: sonnet",
6719
+ " memory: [kb, history]",
6720
+ " retrieval: planner",
6721
+ " }",
6722
+ " flow { intake -> worker }",
6723
+ " memory {",
6724
+ " store kb { type: semantic backend: lancedb }",
6725
+ " store history { type: episodic backend: sqlite-vec }",
6726
+ " retrieval planner { sources: [kb, history] }",
6727
+ " }",
6728
+ "}",
6729
+ ].join("\n");
6730
+ const ast = parse(src);
6731
+ const agent = ast.nodes.find((n) => n.id === "worker");
6732
+ expect(agent.memory).toEqual(["kb", "history"]);
6733
+ expect(agent.retrieval).toBe("planner");
6734
+ });
6735
+ it("parses empty memory block with no stores", () => {
6736
+ const src = `topology t : [pipeline] {\n meta { version: "1.0.0" }\n orchestrator { model: opus handles: [a] }\n action a { kind: inline }\n flow { a -> a }\n memory {}\n}`;
6737
+ const ast = parse(src);
6738
+ expect(ast.stores).toEqual([]);
6739
+ expect(ast.retrievals).toEqual([]);
6740
+ });
6741
+ it("parses store alongside existing memory sub-blocks (workspace, domains)", () => {
6742
+ const src = [
6743
+ "topology t : [pipeline] {",
6744
+ " meta { version: \"1.0.0\" }",
6745
+ " orchestrator { model: opus handles: [a] }",
6746
+ " action a { kind: inline }",
6747
+ " flow { a -> a }",
6748
+ " memory {",
6749
+ " workspace {",
6750
+ " path: \".state\"",
6751
+ " }",
6752
+ " domains {",
6753
+ " primary: \"engineering\"",
6754
+ " }",
6755
+ " store kb {",
6756
+ " type: semantic",
6757
+ " backend: lancedb",
6758
+ " }",
6759
+ " }",
6760
+ "}",
6761
+ ].join("\n");
6762
+ const ast = parse(src);
6763
+ // Old-style memory sub-blocks still populated
6764
+ const ws = ast.memory["workspace"];
6765
+ expect(ws).toBeDefined();
6766
+ expect(ws["path"]).toBe(".state");
6767
+ const domains = ast.memory["domains"];
6768
+ expect(domains).toBeDefined();
6769
+ expect(domains["primary"]).toBe("engineering");
6770
+ // New store also populated
6771
+ expect(ast.stores).toHaveLength(1);
6772
+ expect(ast.stores[0].id).toBe("kb");
6773
+ });
6774
+ it("does not produce V24 warning for store/retrieval inside memory", () => {
6775
+ const src = [
6776
+ "topology t : [pipeline] {",
6777
+ " meta { version: \"1.0.0\" }",
6778
+ " orchestrator { model: opus handles: [a] }",
6779
+ " action a { kind: inline }",
6780
+ " flow { a -> a }",
6781
+ " memory {",
6782
+ " store kb {",
6783
+ " type: semantic",
6784
+ " backend: lancedb",
6785
+ " }",
6786
+ " retrieval planner {",
6787
+ " sources: [kb]",
6788
+ " }",
6789
+ " }",
6790
+ "}",
6791
+ ].join("\n");
6792
+ const ast = parse(src);
6793
+ const results = validate(ast);
6794
+ const v24 = results.filter((r) => r.rule === "V24");
6795
+ expect(v24).toHaveLength(0);
6796
+ });
6797
+ });
6798
+ // =========================================================================
6799
+ // V81-V86 memory store validation
6800
+ // =========================================================================
6801
+ describe("V81-V86 memory store validation", () => {
6802
+ it("V81: invalid store backend produces error", () => {
6803
+ const ast = minimalASTForWave6({
6804
+ stores: [
6805
+ { id: "bad-store", type: "semantic", backend: "invalid-db" },
6806
+ ],
6807
+ });
6808
+ const results = validate(ast);
6809
+ const v81 = results.filter((r) => r.rule === "V81");
6810
+ expect(v81.length).toBeGreaterThan(0);
6811
+ expect(v81[0].level).toBe("error");
6812
+ expect(v81[0].message).toContain("invalid-db");
6813
+ });
6814
+ it("V81: valid store backend produces no error", () => {
6815
+ const ast = minimalASTForWave6({
6816
+ stores: [
6817
+ { id: "good-store", type: "semantic", backend: "lancedb" },
6818
+ ],
6819
+ });
6820
+ const results = validate(ast);
6821
+ const v81 = results.filter((r) => r.rule === "V81");
6822
+ expect(v81).toHaveLength(0);
6823
+ });
6824
+ it("V82: semantic store without embedding produces warning", () => {
6825
+ const ast = minimalASTForWave6({
6826
+ stores: [
6827
+ { id: "kb", type: "semantic", backend: "lancedb" },
6828
+ ],
6829
+ });
6830
+ const results = validate(ast);
6831
+ const v82 = results.filter((r) => r.rule === "V82");
6832
+ expect(v82.length).toBeGreaterThan(0);
6833
+ expect(v82[0].level).toBe("warning");
6834
+ expect(v82[0].message).toContain("embedding");
6835
+ });
6836
+ it("V82: semantic store with embedding produces no warning", () => {
6837
+ const ast = minimalASTForWave6({
6838
+ stores: [
6839
+ { id: "kb", type: "semantic", backend: "lancedb", embedding: { provider: "ollama", model: "nomic", dimensions: 768 } },
6840
+ ],
6841
+ });
6842
+ const results = validate(ast);
6843
+ const v82 = results.filter((r) => r.rule === "V82");
6844
+ expect(v82).toHaveLength(0);
6845
+ });
6846
+ it("V83: store without scope produces warning", () => {
6847
+ const ast = minimalASTForWave6({
6848
+ stores: [
6849
+ { id: "kb", type: "semantic", backend: "lancedb" },
6850
+ ],
6851
+ });
6852
+ const results = validate(ast);
6853
+ const v83 = results.filter((r) => r.rule === "V83");
6854
+ expect(v83.length).toBeGreaterThan(0);
6855
+ expect(v83[0].level).toBe("warning");
6856
+ expect(v83[0].message).toContain("scope");
6857
+ });
6858
+ it("V84: retrieval with nonexistent source produces error", () => {
6859
+ const ast = minimalASTForWave6({
6860
+ stores: [
6861
+ { id: "kb", type: "semantic", backend: "lancedb" },
6862
+ ],
6863
+ retrievals: [
6864
+ { id: "planner", sources: ["kb", "nonexistent"] },
6865
+ ],
6866
+ });
6867
+ const results = validate(ast);
6868
+ const v84 = results.filter((r) => r.rule === "V84");
6869
+ expect(v84.length).toBeGreaterThan(0);
6870
+ expect(v84[0].level).toBe("error");
6871
+ expect(v84[0].message).toContain("nonexistent");
6872
+ });
6873
+ it("V84: retrieval with valid sources produces no error", () => {
6874
+ const ast = minimalASTForWave6({
6875
+ stores: [
6876
+ { id: "kb", type: "semantic", backend: "lancedb" },
6877
+ { id: "history", type: "episodic", backend: "sqlite-vec" },
6878
+ ],
6879
+ retrievals: [
6880
+ { id: "planner", sources: ["kb", "history"] },
6881
+ ],
6882
+ });
6883
+ const results = validate(ast);
6884
+ const v84 = results.filter((r) => r.rule === "V84");
6885
+ expect(v84).toHaveLength(0);
6886
+ });
6887
+ it("V85: remote backend without connection produces error", () => {
6888
+ const ast = minimalASTForWave6({
6889
+ stores: [
6890
+ { id: "cloud-store", type: "semantic", backend: "pinecone" },
6891
+ ],
6892
+ });
6893
+ const results = validate(ast);
6894
+ const v85 = results.filter((r) => r.rule === "V85");
6895
+ expect(v85.length).toBeGreaterThan(0);
6896
+ expect(v85[0].level).toBe("error");
6897
+ expect(v85[0].message).toContain("pinecone");
6898
+ expect(v85[0].message).toContain("connection");
6899
+ });
6900
+ it("V85: local backend without connection produces no error", () => {
6901
+ const ast = minimalASTForWave6({
6902
+ stores: [
6903
+ { id: "local-store", type: "semantic", backend: "lancedb" },
6904
+ ],
6905
+ });
6906
+ const results = validate(ast);
6907
+ const v85 = results.filter((r) => r.rule === "V85");
6908
+ expect(v85).toHaveLength(0);
6909
+ });
6910
+ it("V86: agent memory referencing nonexistent store produces error", () => {
6911
+ const ast = minimalASTForWave6({
6912
+ stores: [
6913
+ { id: "kb", type: "semantic", backend: "lancedb" },
6914
+ ],
6915
+ retrievals: [],
6916
+ nodes: [
6917
+ { id: "orchestrator", type: "orchestrator", label: "O", model: "opus", handles: ["intake"] },
6918
+ { id: "intake", type: "action", label: "Intake" },
6919
+ { id: "worker", type: "agent", label: "Worker", model: "sonnet", memory: ["nonexistent"] },
6920
+ ],
6921
+ });
6922
+ const results = validate(ast);
6923
+ const v86 = results.filter((r) => r.rule === "V86");
6924
+ expect(v86.length).toBeGreaterThan(0);
6925
+ expect(v86[0].level).toBe("error");
6926
+ expect(v86[0].message).toContain("nonexistent");
6927
+ });
6928
+ it("V86: agent memory referencing existing store produces no error", () => {
6929
+ const ast = minimalASTForWave6({
6930
+ stores: [
6931
+ { id: "kb", type: "semantic", backend: "lancedb" },
6932
+ ],
6933
+ retrievals: [],
6934
+ nodes: [
6935
+ { id: "orchestrator", type: "orchestrator", label: "O", model: "opus", handles: ["intake"] },
6936
+ { id: "intake", type: "action", label: "Intake" },
6937
+ { id: "worker", type: "agent", label: "Worker", model: "sonnet", memory: ["kb"] },
6938
+ ],
6939
+ });
6940
+ const results = validate(ast);
6941
+ const v86 = results.filter((r) => r.rule === "V86");
6942
+ expect(v86).toHaveLength(0);
6943
+ });
6944
+ it("V86: agent retrieval referencing nonexistent strategy produces error", () => {
6945
+ const ast = minimalASTForWave6({
6946
+ stores: [
6947
+ { id: "kb", type: "semantic", backend: "lancedb" },
6948
+ ],
6949
+ retrievals: [
6950
+ { id: "planner", sources: ["kb"] },
6951
+ ],
6952
+ nodes: [
6953
+ { id: "orchestrator", type: "orchestrator", label: "O", model: "opus", handles: ["intake"] },
6954
+ { id: "intake", type: "action", label: "Intake" },
6955
+ { id: "worker", type: "agent", label: "Worker", model: "sonnet", retrieval: "nonexistent" },
6956
+ ],
6957
+ });
6958
+ const results = validate(ast);
6959
+ const v86 = results.filter((r) => r.rule === "V86");
6960
+ expect(v86.length).toBeGreaterThan(0);
6961
+ expect(v86[0].level).toBe("error");
6962
+ expect(v86[0].message).toContain("nonexistent");
6963
+ });
6964
+ it("V81: duckdb backend produces no error", () => {
6965
+ const ast = minimalASTForWave6({
6966
+ stores: [
6967
+ { id: "duck-store", type: "entity", backend: "duckdb" },
6968
+ ],
6969
+ retrievals: [],
6970
+ });
6971
+ const results = validate(ast);
6972
+ const v81 = results.filter((r) => r.rule === "V81");
6973
+ expect(v81).toHaveLength(0);
6974
+ });
6975
+ });
6513
6976
  });
6514
6977
  //# sourceMappingURL=parser.test.js.map