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
package/spec/grammar.md CHANGED
@@ -308,11 +308,8 @@ agent-field = 'role' ':' identifier
308
308
  | 'isolation' ':' 'worktree'
309
309
  | 'invocation' ':' ('manual' | 'auto')
310
310
  | 'behavior' ':' ('advisory' | 'blocking')
311
- # NOTE: agent.memory is intentionally deferred. Per-agent memory scope
312
- # is covered by reads/writes (data flow) and the top-level memory {}
313
- # block (workspace structure). A dedicated agent.memory field would be
314
- # redundant. Retained in the grammar for future consideration.
315
- | 'memory' ':' ('user' | 'project' | 'local')
311
+ | 'memory' ':' name-list
312
+ | 'retrieval' ':' identifier
316
313
  | 'skills' ':' name-list
317
314
  | 'mcp-servers' ':' name-list
318
315
  | 'background' ':' boolean
@@ -352,7 +349,8 @@ scale-field = 'mode' ':' ('auto' | 'fixed' | 'config')
352
349
  | `isolation` | `worktree` | no | -- | Git worktree isolation |
353
350
  | `invocation` | `manual` / `auto` | no | `auto` | User-triggered only |
354
351
  | `behavior` | `advisory` / `blocking` | no | `blocking` | Advisory never blocks flow |
355
- | `memory` | `user` / `project` / `local` | no | -- | Persistent memory scope (intentionally deferred — use reads/writes and top-level memory {} instead) |
352
+ | `memory` | name-list | no | `[]` | Store IDs this agent can access (V35) |
353
+ | `retrieval` | identifier | no | -- | Retrieval strategy ID for memory queries (V35) |
356
354
  | `skills` | name-list | no | `[]` | Skills to preload |
357
355
  | `mcp-servers` | name-list | no | `[]` | MCP servers available |
358
356
  | `background` | boolean | no | `false` | Run in background |
@@ -455,8 +453,10 @@ Maps role identifiers to human-readable descriptions. Agents reference roles wit
455
453
  ### 4.9 `memory`
456
454
 
457
455
  ```ebnf
458
- memory = 'memory' '{' memory-sub-block* '}'
456
+ memory = 'memory' '{' (memory-sub-block | store-block | retrieval-block)* '}'
459
457
  memory-sub-block = identifier '{' (identifier ':' (string | identifier | name-list))* '}'
458
+ store-block = 'store' identifier '{' store-field* '}'
459
+ retrieval-block = 'retrieval' identifier '{' retrieval-field* '}'
460
460
  ```
461
461
 
462
462
  ```agenttopology
@@ -499,7 +499,341 @@ memory {
499
499
  | `metrics` | `path` (string), `mode` (identifier: `append-only`) | Execution log |
500
500
  | `workspace` | `path` (string), `protocol` (string), `structure` (name-list) | Per-run working directory |
501
501
 
502
- Memory sub-blocks are named blocks with known field schemas. Unknown sub-block names are parser errors.
502
+ Memory sub-blocks are named blocks with known field schemas. The `store` and `retrieval` blocks are typed named blocks parsed into `StoreNode[]` and `RetrievalNode[]` on the AST. Other unknown sub-block names produce a warning (V24).
503
+
504
+ #### 4.9.1 `store`
505
+
506
+ A named memory store declaration. Stores define the infrastructure for a specific type of agent memory -- backend, embedding model, indexing, ingestion, search, and lifecycle management.
507
+
508
+ ```ebnf
509
+ store-block = 'store' identifier '{' store-field* '}'
510
+ store-field = 'type' ':' store-type
511
+ | 'description' ':' string
512
+ | 'scope' ':' scope-enum
513
+ | 'isolation' ':' isolation-enum
514
+ | 'backend' ':' backend-enum
515
+ | 'path' ':' string
516
+ | 'connection' ':' secret-string
517
+ | 'extraction' ':' extraction-enum
518
+ | embedding-block
519
+ | index-block
520
+ | ingestion-block
521
+ | search-block
522
+ | lifecycle-block
523
+ | backend-config-block
524
+ store-type = 'semantic' | 'episodic' | 'procedural' | 'entity'
525
+ | 'graph' | 'user' | 'session' | 'temporal'
526
+ scope-enum = 'agent' | 'user' | 'session' | 'org' | 'global'
527
+ isolation-enum = 'strict' | 'soft' | 'none'
528
+ backend-enum = 'lancedb' | 'sqlite-vec' | 'chroma' | 'kuzu' | 'falkordb'
529
+ | 'mongodb' | 'pinecone' | 'qdrant' | 'pgvector' | 'neo4j' | 'sqlite'
530
+ | 'duckdb'
531
+ extraction-enum = 'llm' | 'regex' | 'hybrid'
532
+ secret-string = 'secret'? string
533
+ ```
534
+
535
+ ```agenttopology
536
+ memory {
537
+ store codebase-knowledge {
538
+ type: semantic
539
+ description: "Indexed codebase documentation and patterns"
540
+ scope: agent
541
+ isolation: strict
542
+ backend: lancedb
543
+ path: ".memory/semantic/"
544
+ extraction: llm
545
+
546
+ embedding {
547
+ provider: ollama
548
+ model: "nomic-embed-text"
549
+ dimensions: 768
550
+ }
551
+
552
+ index {
553
+ collection: "chunks"
554
+ metric: cosine
555
+ }
556
+
557
+ ingestion {
558
+ sources: ["docs/", "references/"]
559
+ chunking: recursive
560
+ chunk-size: 512
561
+ overlap: 50
562
+ }
563
+
564
+ search {
565
+ strategy: hybrid
566
+ rerank: true
567
+ top-k: 10
568
+ }
569
+
570
+ lifecycle {
571
+ retention: 90d
572
+ decay-half-life: 30d
573
+ consolidation: 0.85
574
+ contradiction: overwrite
575
+ audit-log: true
576
+ }
577
+ }
578
+
579
+ store user-preferences {
580
+ type: user
581
+ description: "Per-user preferences and corrections"
582
+ scope: user
583
+ isolation: soft
584
+ backend: sqlite
585
+ path: ".memory/user.db"
586
+ }
587
+
588
+ store session-context {
589
+ type: session
590
+ description: "Short-lived conversation context"
591
+ scope: session
592
+ isolation: none
593
+ backend: sqlite-vec
594
+ path: ".memory/session/"
595
+ }
596
+
597
+ store knowledge-graph {
598
+ type: graph
599
+ description: "Entity relationships across the codebase"
600
+ scope: global
601
+ backend: kuzu
602
+ path: ".memory/graph/"
603
+
604
+ search {
605
+ strategy: graph
606
+ top-k: 20
607
+ }
608
+ }
609
+
610
+ store cloud-vectors {
611
+ type: semantic
612
+ description: "Cloud-hosted vector store"
613
+ scope: org
614
+ backend: pinecone
615
+ connection: secret "PINECONE_URL"
616
+
617
+ embedding {
618
+ provider: openai
619
+ model: "text-embedding-3-large"
620
+ dimensions: 3072
621
+ endpoint: secret "OPENAI_EMBEDDING_URL"
622
+ }
623
+ }
624
+ }
625
+ ```
626
+
627
+ | Field | Type | Required | Default | Description |
628
+ |-------|------|----------|---------|-------------|
629
+ | `type` | store-type | yes | -- | Memory type classification |
630
+ | `description` | string | no | -- | Human-readable purpose |
631
+ | `scope` | scope-enum | no | -- | Access scope (V32 warning if omitted) |
632
+ | `isolation` | isolation-enum | no | -- | Isolation level between scopes |
633
+ | `backend` | backend-enum | yes | -- | Storage backend (V30 enum check) |
634
+ | `path` | string | no | -- | File system path for local backends |
635
+ | `connection` | secret-string | no | -- | Connection URI for remote backends (V34) |
636
+ | `extraction` | extraction-enum | no | -- | Entity/fact extraction method |
637
+ | `embedding` | embedding-block | no | -- | Embedding configuration (V31 warning if omitted for semantic/episodic/procedural) |
638
+ | `index` | index-block | no | -- | Index/collection configuration |
639
+ | `ingestion` | ingestion-block | no | -- | Data ingestion configuration |
640
+ | `search` | search-block | no | -- | Search/query configuration |
641
+ | `lifecycle` | lifecycle-block | no | -- | Memory lifecycle management |
642
+ | `backend-config` | backend-config-block | no | -- | Backend-specific passthrough |
643
+
644
+ **`connection` and secrets:** The `connection` field supports the `secret` modifier, parsed identically to `checkpoint.connection`. When `secret` is present, the string value is treated as a secret reference (e.g., `secret "PINECONE_URL"` references the `PINECONE_URL` secret).
645
+
646
+ **`backend-config`:** A passthrough block for backend-specific key-value pairs not covered by the standard fields. Stored as `Record<string, unknown>` on the AST. Same pattern as `ProviderDef.extra`.
647
+
648
+ **Local vs remote backends:** Backends `lancedb`, `sqlite-vec`, `sqlite`, `kuzu`, and `chroma` are local-first (use `path`). Backends `pinecone`, `qdrant`, `pgvector`, `neo4j`, `mongodb`, and `falkordb` are remote (require `connection`, V34).
649
+
650
+ ##### `embedding` sub-block
651
+
652
+ ```ebnf
653
+ embedding-block = 'embedding' '{' embedding-field* '}'
654
+ embedding-field = 'provider' ':' embedding-provider
655
+ | 'model' ':' string
656
+ | 'dimensions' ':' number
657
+ | 'endpoint' ':' secret-string
658
+ embedding-provider = 'ollama' | 'local' | 'voyage' | 'openai' | 'gemini'
659
+ ```
660
+
661
+ | Field | Type | Required | Default | Description |
662
+ |-------|------|----------|---------|-------------|
663
+ | `provider` | embedding-provider | no | -- | Embedding provider |
664
+ | `model` | string | no | -- | Model identifier |
665
+ | `dimensions` | number | no | -- | Vector dimensions |
666
+ | `endpoint` | secret-string | no | -- | Endpoint URL or secret reference |
667
+
668
+ ##### `index` sub-block
669
+
670
+ ```ebnf
671
+ index-block = 'index' '{' index-field* '}'
672
+ index-field = 'collection' ':' string
673
+ | 'metric' ':' metric-enum
674
+ metric-enum = 'cosine' | 'euclidean' | 'dot-product'
675
+ ```
676
+
677
+ | Field | Type | Required | Default | Description |
678
+ |-------|------|----------|---------|-------------|
679
+ | `collection` | string | no | -- | Collection or table name |
680
+ | `metric` | metric-enum | no | -- | Distance metric for vector similarity |
681
+
682
+ ##### `ingestion` sub-block
683
+
684
+ ```ebnf
685
+ ingestion-block = 'ingestion' '{' ingestion-field* '}'
686
+ ingestion-field = 'sources' ':' string-list
687
+ | 'chunking' ':' chunking-enum
688
+ | 'chunk-size' ':' number
689
+ | 'overlap' ':' number
690
+ chunking-enum = 'recursive' | 'sentence' | 'paragraph' | 'fixed'
691
+ ```
692
+
693
+ | Field | Type | Required | Default | Description |
694
+ |-------|------|----------|---------|-------------|
695
+ | `sources` | string-list | no | `[]` | Source paths or globs to ingest |
696
+ | `chunking` | chunking-enum | no | -- | Chunking strategy |
697
+ | `chunk-size` | number | no | -- | Target chunk size in tokens or characters |
698
+ | `overlap` | number | no | -- | Overlap between chunks |
699
+
700
+ ##### `search` sub-block
701
+
702
+ ```ebnf
703
+ search-block = 'search' '{' search-field* '}'
704
+ search-field = 'strategy' ':' strategy-enum
705
+ | 'rerank' ':' boolean
706
+ | 'top-k' ':' number
707
+ strategy-enum = 'vector' | 'keyword' | 'hybrid' | 'graph'
708
+ ```
709
+
710
+ | Field | Type | Required | Default | Description |
711
+ |-------|------|----------|---------|-------------|
712
+ | `strategy` | strategy-enum | no | -- | Search strategy |
713
+ | `rerank` | boolean | no | `false` | Whether to apply reranking |
714
+ | `top-k` | number | no | -- | Number of top results to return |
715
+
716
+ ##### `lifecycle` sub-block
717
+
718
+ ```ebnf
719
+ lifecycle-block = 'lifecycle' '{' lifecycle-field* '}'
720
+ lifecycle-field = 'retention' ':' duration
721
+ | 'decay-half-life' ':' duration
722
+ | 'consolidation' ':' number
723
+ | 'contradiction' ':' contradiction-enum
724
+ | 'audit-log' ':' boolean
725
+ duration = [0-9]+ ('d' | 'h' | 'm' | 'y')
726
+ contradiction-enum = 'overwrite' | 'preserve' | 'bi-temporal'
727
+ ```
728
+
729
+ | Field | Type | Required | Default | Description |
730
+ |-------|------|----------|---------|-------------|
731
+ | `retention` | duration | no | -- | Retention period (e.g., `90d`, `1y`) |
732
+ | `decay-half-life` | duration | no | -- | Half-life for memory decay scoring |
733
+ | `consolidation` | number (0-1) | no | -- | Consolidation threshold |
734
+ | `contradiction` | contradiction-enum | no | -- | Contradiction resolution strategy |
735
+ | `audit-log` | boolean | no | `false` | Whether to maintain an audit log |
736
+
737
+ ##### `backend-config` sub-block
738
+
739
+ ```ebnf
740
+ backend-config-block = 'backend-config' '{' (identifier ':' value)* '}'
741
+ ```
742
+
743
+ A passthrough block for backend-specific configuration. All key-value pairs are stored as-is in `StoreNode.backendConfig` without schema validation.
744
+
745
+ #### 4.9.2 `retrieval`
746
+
747
+ A named retrieval strategy declaration. Retrieval blocks define how an agent queries across one or more stores -- source selection, scoring weights, budget constraints, and cache-hit routing.
748
+
749
+ ```ebnf
750
+ retrieval-block = 'retrieval' identifier '{' retrieval-field* '}'
751
+ retrieval-field = 'sources' ':' name-list
752
+ | 'budget' ':' number
753
+ | 'paths' ':' name-list
754
+ | 'rerank' ':' boolean
755
+ | 'diversity' ':' boolean
756
+ | 'cache-hit-threshold' ':' number
757
+ | 'cache-hit-action' ':' cache-hit-enum
758
+ | scoring-block
759
+ cache-hit-enum = 'short-circuit' | 'augment' | 'pass-through'
760
+ scoring-block = 'scoring' '{' scoring-field* '}'
761
+ scoring-field = 'recency-weight' ':' number
762
+ | 'semantic-weight' ':' number
763
+ | 'importance-weight' ':' number
764
+ ```
765
+
766
+ ```agenttopology
767
+ memory {
768
+ retrieval balanced {
769
+ sources: [codebase-knowledge, user-preferences, knowledge-graph]
770
+ budget: 4096
771
+
772
+ scoring {
773
+ recency-weight: 0.3
774
+ semantic-weight: 0.5
775
+ importance-weight: 0.2
776
+ }
777
+
778
+ paths: [semantic, episodic, graph]
779
+ rerank: true
780
+ diversity: true
781
+
782
+ cache-hit-threshold: 0.92
783
+ cache-hit-action: short-circuit
784
+ }
785
+
786
+ retrieval fast-lookup {
787
+ sources: [session-context]
788
+ budget: 1024
789
+ rerank: false
790
+
791
+ cache-hit-threshold: 0.95
792
+ cache-hit-action: short-circuit
793
+ }
794
+ }
795
+ ```
796
+
797
+ | Field | Type | Required | Default | Description |
798
+ |-------|------|----------|---------|-------------|
799
+ | `sources` | name-list | no | `[]` | Store IDs to draw from (V33 reference check) |
800
+ | `budget` | number | no | -- | Token budget for retrieved context |
801
+ | `paths` | name-list | no | `[]` | Retrieval paths to activate |
802
+ | `rerank` | boolean | no | `false` | Whether to apply cross-source reranking |
803
+ | `diversity` | boolean | no | `false` | Whether to enforce diversity across results |
804
+ | `cache-hit-threshold` | number (0-1) | no | -- | Similarity threshold for cache-hit detection |
805
+ | `cache-hit-action` | cache-hit-enum | no | -- | Action on cache hit |
806
+ | `scoring` | scoring-block | no | -- | Scoring weight configuration |
807
+
808
+ **`scoring` sub-block:** Weights should sum to 1.0 but this is not enforced at parse time. Bindings may normalize weights at runtime.
809
+
810
+ | Scoring Field | Type | Required | Default | Description |
811
+ |---------------|------|----------|---------|-------------|
812
+ | `recency-weight` | number (0-1) | no | -- | Weight for recency in scoring |
813
+ | `semantic-weight` | number (0-1) | no | -- | Weight for semantic similarity |
814
+ | `importance-weight` | number (0-1) | no | -- | Weight for importance/frequency |
815
+
816
+ **Memory-gated routing:** The `cache-hit-threshold` and `cache-hit-action` fields enable memory-gated routing -- when a query closely matches existing memories, the retrieval strategy can short-circuit (skip the LLM call), augment (add cached answer to context), or pass through (proceed normally). This is a first-class declarative primitive for BudgetMem-style memory optimization.
817
+
818
+ #### 4.9.3 Agent-Level Memory References
819
+
820
+ Agents reference stores and retrieval strategies via two new fields on the `agent` block:
821
+
822
+ ```agenttopology
823
+ agent researcher {
824
+ model: sonnet
825
+ memory: [codebase-knowledge, session-context]
826
+ retrieval: balanced
827
+ }
828
+
829
+ agent assistant {
830
+ model: haiku
831
+ memory: [user-preferences, session-context]
832
+ retrieval: fast-lookup
833
+ }
834
+ ```
835
+
836
+ The `memory` field is a name-list of store IDs. The `retrieval` field is a single identifier referencing a retrieval strategy. Both are validated against declared stores and retrievals (V35).
503
837
 
504
838
  ---
505
839
 
@@ -1323,7 +1657,7 @@ agent expensive-thinker {
1323
1657
 
1324
1658
  ## 5. Validation Rules
1325
1659
 
1326
- See `validation.md` for the complete list of 29 validation rules with examples.
1660
+ See `validation.md` for the complete list of 35 validation rules with examples.
1327
1661
 
1328
1662
  ---
1329
1663
 
@@ -1354,7 +1688,8 @@ All optional fields and their defaults when omitted:
1354
1688
  | `agent` | `isolation` | -- (no isolation) |
1355
1689
  | `agent` | `invocation` | `auto` |
1356
1690
  | `agent` | `behavior` | `blocking` |
1357
- | `agent` | `memory` | -- (none) |
1691
+ | `agent` | `memory` | `[]` |
1692
+ | `agent` | `retrieval` | -- (none) |
1358
1693
  | `agent` | `skills` | `[]` |
1359
1694
  | `agent` | `mcp-servers` | `[]` |
1360
1695
  | `agent` | `background` | `false` |
@@ -126,6 +126,30 @@ round-robin, random, reflection, rate-limit
126
126
 
127
127
  ---
128
128
 
129
+ ## Wave 8 Keywords (Agent Memory)
130
+
131
+ ```
132
+ store, retrieval, embedding, ingestion, lifecycle, scoring, backend-config,
133
+ type, scope, backend, connection, extraction,
134
+ provider, dimensions, endpoint, collection, metric,
135
+ sources, chunking, chunk-size, overlap,
136
+ strategy, rerank, top-k,
137
+ retention, decay-half-life, consolidation, contradiction, audit-log,
138
+ budget, paths, diversity, cache-hit-threshold, cache-hit-action,
139
+ recency-weight, semantic-weight, importance-weight,
140
+ semantic, episodic, procedural, entity, graph, temporal,
141
+ lancedb, sqlite-vec, chroma, kuzu, falkordb, pinecone, qdrant, pgvector,
142
+ ollama, voyage, gemini,
143
+ cosine, euclidean, dot-product,
144
+ recursive, paragraph,
145
+ vector, keyword, hybrid,
146
+ overwrite, preserve, bi-temporal,
147
+ short-circuit, augment, pass-through,
148
+ strict, soft, org, global
149
+ ```
150
+
151
+ ---
152
+
129
153
  ## Not Reserved
130
154
 
131
155
  These are free-form values and can be used as names:
@@ -143,5 +167,5 @@ Environment block field names, custom role names, and user-defined enum values a
143
167
  Reserved for future use, not yet in grammar:
144
168
 
145
169
  ```
146
- event
170
+ event, working
147
171
  ```
@@ -2,7 +2,7 @@
2
2
 
3
3
  Created by Nadav Naveh
4
4
 
5
- These 29 rules are enforced by the `.at` compiler at parse time. A topology that violates any rule is rejected before scaffold generation.
5
+ These 35 rules are enforced by the `.at` compiler at parse time. A topology that violates any rule is rejected before scaffold generation.
6
6
 
7
7
  ---
8
8
 
@@ -408,7 +408,7 @@ memory {
408
408
 
409
409
  ## Rule 24: Unknown Memory Sub-Blocks (Warning)
410
410
 
411
- Only known sub-blocks are expected inside the `memory` section: `domains`, `references`, `external-docs`, `metrics`, and `workspace`. Any other named sub-block is parsed but flagged as a **warning**.
411
+ Only known sub-blocks are expected inside the `memory` section: `domains`, `references`, `external-docs`, `metrics`, `workspace`, `store`, and `retrieval`. Any other named sub-block is parsed but flagged as a **warning**.
412
412
 
413
413
  ```agenttopology
414
414
  # WARNING -- "custom-store" is not a recognized memory sub-block
@@ -500,6 +500,169 @@ metering {
500
500
 
501
501
  ---
502
502
 
503
+ ## Rule 30: Store Backend Enum
504
+
505
+ Every `store.backend` must be one of the allowed values: `lancedb`, `sqlite-vec`, `chroma`, `kuzu`, `falkordb`, `mongodb`, `pinecone`, `qdrant`, `pgvector`, `neo4j`, or `sqlite`. Any other value is an error.
506
+
507
+ ```agenttopology
508
+ # INVALID -- "redis" is not a recognized store backend
509
+ memory {
510
+ store cache {
511
+ type: session
512
+ backend: redis
513
+ path: ".memory/cache/"
514
+ }
515
+ }
516
+ ```
517
+
518
+ ---
519
+
520
+ ## Rule 31: Embedding Recommended (Warning)
521
+
522
+ When a store's `type` is `semantic`, `episodic`, or `procedural`, the `embedding {}` sub-block should be present. Omitting it produces a **warning** -- the topology is valid but the store may not function correctly without embedding configuration.
523
+
524
+ ```agenttopology
525
+ # WARNING -- semantic store without embedding configuration
526
+ memory {
527
+ store docs {
528
+ type: semantic
529
+ backend: lancedb
530
+ path: ".memory/docs/"
531
+ }
532
+ }
533
+
534
+ # VALID -- embedding provided
535
+ memory {
536
+ store docs {
537
+ type: semantic
538
+ backend: lancedb
539
+ path: ".memory/docs/"
540
+
541
+ embedding {
542
+ provider: ollama
543
+ model: "nomic-embed-text"
544
+ dimensions: 768
545
+ }
546
+ }
547
+ }
548
+ ```
549
+
550
+ ---
551
+
552
+ ## Rule 32: Store Scope Recommended (Warning)
553
+
554
+ Every `store` should have a `scope` field. Omitting it produces a **warning** -- the topology is valid but the store's access boundaries are undefined, which may cause issues in multi-agent or multi-user deployments.
555
+
556
+ ```agenttopology
557
+ # WARNING -- store without scope
558
+ memory {
559
+ store knowledge {
560
+ type: semantic
561
+ backend: lancedb
562
+ path: ".memory/knowledge/"
563
+ }
564
+ }
565
+
566
+ # VALID -- scope specified
567
+ memory {
568
+ store knowledge {
569
+ type: semantic
570
+ scope: agent
571
+ backend: lancedb
572
+ path: ".memory/knowledge/"
573
+ }
574
+ }
575
+ ```
576
+
577
+ ---
578
+
579
+ ## Rule 33: Retrieval Sources Resolve
580
+
581
+ Every store ID in a `retrieval.sources` list must reference a declared `store` block within the `memory` section. Referencing an undefined store is an error.
582
+
583
+ ```agenttopology
584
+ # INVALID -- "nonexistent-store" is not a declared store
585
+ memory {
586
+ store docs {
587
+ type: semantic
588
+ backend: lancedb
589
+ path: ".memory/docs/"
590
+ }
591
+
592
+ retrieval main {
593
+ sources: [docs, nonexistent-store]
594
+ budget: 4096
595
+ }
596
+ }
597
+ ```
598
+
599
+ ---
600
+
601
+ ## Rule 34: Connection Required for Remote Backends
602
+
603
+ When a store uses a remote backend (`pinecone`, `qdrant`, `pgvector`, `neo4j`, `mongodb`, or `falkordb`), the `connection` field is required. Omitting it is an error.
604
+
605
+ ```agenttopology
606
+ # INVALID -- pinecone requires a connection string
607
+ memory {
608
+ store vectors {
609
+ type: semantic
610
+ backend: pinecone
611
+
612
+ embedding {
613
+ provider: openai
614
+ model: "text-embedding-3-large"
615
+ dimensions: 3072
616
+ }
617
+ }
618
+ }
619
+
620
+ # VALID -- connection provided
621
+ memory {
622
+ store vectors {
623
+ type: semantic
624
+ backend: pinecone
625
+ connection: secret "PINECONE_URL"
626
+
627
+ embedding {
628
+ provider: openai
629
+ model: "text-embedding-3-large"
630
+ dimensions: 3072
631
+ }
632
+ }
633
+ }
634
+ ```
635
+
636
+ ---
637
+
638
+ ## Rule 35: Agent Memory/Retrieval References
639
+
640
+ Agent `memory` list entries must reference declared store IDs. Agent `retrieval` must reference a declared retrieval strategy ID. Referencing undefined stores or retrievals is an error.
641
+
642
+ ```agenttopology
643
+ # INVALID -- "ghost-store" is not a declared store, "ghost-retrieval" is not declared
644
+ memory {
645
+ store docs {
646
+ type: semantic
647
+ backend: lancedb
648
+ path: ".memory/docs/"
649
+ }
650
+
651
+ retrieval main {
652
+ sources: [docs]
653
+ budget: 4096
654
+ }
655
+ }
656
+
657
+ agent researcher {
658
+ model: sonnet
659
+ memory: [docs, ghost-store]
660
+ retrieval: ghost-retrieval
661
+ }
662
+ ```
663
+
664
+ ---
665
+
503
666
  ## Summary Table
504
667
 
505
668
  | Rule | Severity | Description |
@@ -533,3 +696,9 @@ metering {
533
696
  | V27 | warning | Agent permissions enum — should be a recognized permission mode |
534
697
  | V28 | error | Metering format enum — must be json, jsonl, or csv |
535
698
  | V29 | warning | Metering pricing enum — should be a recognized pricing model |
699
+ | V30 | error | Store backend enum — must be lancedb, sqlite-vec, chroma, kuzu, falkordb, mongodb, pinecone, qdrant, pgvector, neo4j, or sqlite |
700
+ | V31 | warning | Embedding recommended — embedding should be present for semantic, episodic, and procedural stores |
701
+ | V32 | warning | Store scope recommended — every store should have a scope field |
702
+ | V33 | error | Retrieval sources resolve — retrieval sources must reference declared store IDs |
703
+ | V34 | error | Connection required for remote backends — pinecone, qdrant, pgvector, neo4j, mongodb, falkordb require connection |
704
+ | V35 | error | Agent memory/retrieval references — agent memory and retrieval fields must reference valid IDs |