Gateway Module — `agent-memory/`
1. Purpose
The agent-memory module owns each CyberShinobi agent's private domain memory — the four learning tiers (task_blueprint, skill_heuristic, domain_pattern, lesson_learned) committed post-dispatch. It provides hybrid retrieval (Voyage vector search + FTS5 BM25 fused by reciprocal rank fusion), embedding-based deduplication on commit, a composite relevance score, and two distinct peer-read surfaces: queryPeer (literal single-agent equality) and queryPeerAny (cross-agent scan, caller-excluded upstream). Agents write only their own namespace; peer reads never increment access counts. It is consumed by the context module's assembly fanout.
2. File Inventory
| File | Lines | Responsibility |
|---|---|---|
repository.ts | 493 | SQLite + agent_memory_vec + agent_memory_fts; commit/dedup, hybrid query, peer scoring |
types.ts | 162 | Zod input schemas + record/result interfaces + repository contract |
routes.ts | 136 | 5 endpoints; activity logging on query/peer-query |
index.ts | 22 | createAgentMemoryLayer factory |
| Total | 813 |
3. Public API Surface
REST Endpoints (mount: index.ts:737 → /api/agent-memory)
| Method | Path | Auth | Body Schema | Response | Side Effects |
|---|---|---|---|---|---|
| POST | /api/agent-memory/commit | Bearer | CommitAgentMemoryInputSchema | {memory_id, duplicate_detected} | Embeds content; dedup vs same-agent neighbours (distance <0.2) → UPDATE, else INSERT + vec + FTS |
| POST | /api/agent-memory/query | Bearer | QueryAgentMemoryInputSchema | {memories: AgentMemoryResult[]} | Hybrid search, own-agent filter, increments access_count; logs agent_memory_query activity |
| POST | /api/agent-memory/list | Bearer | ListAgentMemoryInputSchema | {memories: AgentMemoryRecord[]} | Paginated list by agent (no scoring) |
| POST | /api/agent-memory/peer-query | Bearer | QueryPeerMemoryInputSchema | {memories: PeerMemoryResult[]} | Read-only literal-target_agent scan; logs peer_memory_query; no access-count increment |
| GET | /api/agent-memory/pending-review?since= | Bearer | since = JSON map {agent: ISO-datetime} | {counts} | Per-agent count of memories since timestamps |
MCP Tools
commit_agent_memory / commit_agent_memory_raw (→ /commit, raw canonical); read-path served by peer/query calls surfaced through assemble_context fanout, not a dedicated MCP tool. queryPeerAny has no REST/MCP surface of its own — it is invoked internally by the context module adapter.
4. Internal API
createAgentMemoryLayer(db, embeddingProvider, activityRepo?) → {repo, router}. AgentMemoryRepository methods: commit, query, queryPeer, queryPeerAny, listByAgent, countMemoriesSince. queryPeerAny is exported on the repo interface but has no route — consumed by context/index.ts:createPeerMemorySearchAdapter.
5. Background Services
None.
6. Data Contracts
Zod schemas in types.ts:
CommitAgentMemoryInputSchema:agent_name,content,memory_type(4-enum),task_context?({task_description, skills_used[], outcome: success|partial|failed, mission_namespace?}).QueryAgentMemoryInputSchema:agent_name,query,memory_type?,limit?(≤20).QueryPeerMemoryInputSchema:target_agent(min 1 — literal equality, D27 retired the'*'wildcard),query,memory_type?,limit?(≤10).QueryPeerAnyMemoryInputSchema:query,memory_type?,limit?(≤10) — notarget_agent.ListAgentMemoryInputSchema:agent_name,memory_type?,limit?(≤100 — D1422 tightened from 500),offset?.PendingReviewQuerySchema:z.record(string, ISO-datetime).
Composite relevance score (D23 PR-2 rebalance): normalizedRrf*0.625 + accessFrequencyBoost*0.25 + outcomeBoost*0.125 — recency term removed (owned by the context uniform scoring funnel). PeerMemoryResult retains created_at (D23 PR-5 fix — its absence poisoned context/index.ts recency to NaN).
7. Dependencies
- Gateway modules:
memory/types.js(EmbeddingProvider),activity/repository.js(ActivityRepository, optional),shared/search-utils.js(buildFts5Query,reciprocalRankFusion),shared/zod-error.js. - External:
better-sqlite3(+sqlite-vecvirtual tableagent_memory_vec, FTS5agent_memory_fts),express,zod. Voyage embeddings via the injected provider. - Environment variables: none directly (Voyage key loaded by the
memorymodule's provider).
8. Test Coverage
| Layer | File | Notes |
|---|---|---|
| Repository | test/agent-memory-repository.test.ts | Commit/dedup, hybrid query, peer scoring, queryPeerAny |
| Routes | test/agent-memory-routes.test.ts | 5 endpoints; D1422 limit-100 regression (test.skip at :556) |
| Pending review | test/agent-memory-pending-review.test.ts | since parse, per-agent counts |
9. Known Limitations
safeEmbeddegrades silently to keyword/FTS-only when the Voyage provider throws (console.warn+ null). Commit still proceeds without a vector — dedup is skipped that call.- Dedup only fires within the same agent (
row.agent_name === input.agent_nameguard) and only against the top-5 nearest neighbours with distance <0.2; near-duplicates just past the threshold or beyond k=5 are stored as new rows. commit_agent_memorystill exposes a legacy permissive-flat MCP tool alongside the raw twin (seemcpmodule) — nestedtask_contextis exposed to the client-cache staleness class on the legacy path.
10. Change History
| Date | Dispatch | Summary |
|---|---|---|
| 2026-07-04 | 2296 | Module spec authored (Phase 4 §5.2), smoke-clone-2, HEAD 5c9a304 |
| — | 23 | Score-divergence fixes (recency double-count, peer created_at NaN) |
| — | 27 | queryPeer literal split; queryPeerAny cross-agent scan |
| — | 1422 | AK-348 list limit tightened .max(500)→.max(100) |