@shadowforge0/aquifer-memory 1.5.9 → 1.6.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 (65) hide show
  1. package/.env.example +23 -0
  2. package/README.md +96 -73
  3. package/README_CN.md +659 -0
  4. package/README_TW.md +680 -0
  5. package/aquifer.config.example.json +34 -0
  6. package/consumers/claude-code.js +11 -11
  7. package/consumers/cli.js +374 -39
  8. package/consumers/codex-handoff.js +152 -0
  9. package/consumers/codex.js +1549 -0
  10. package/consumers/default/daily-entries.js +23 -4
  11. package/consumers/default/index.js +2 -2
  12. package/consumers/default/prompts/summary.js +6 -6
  13. package/consumers/mcp.js +131 -7
  14. package/consumers/openclaw-ext/index.js +0 -1
  15. package/consumers/openclaw-plugin.js +44 -4
  16. package/consumers/shared/config.js +28 -0
  17. package/consumers/shared/factory.js +2 -0
  18. package/consumers/shared/ingest.js +1 -1
  19. package/consumers/shared/normalize.js +14 -3
  20. package/consumers/shared/recall-format.js +53 -0
  21. package/consumers/shared/summary-parser.js +151 -0
  22. package/core/aquifer.js +384 -18
  23. package/core/finalization-review.js +319 -0
  24. package/core/insights.js +210 -58
  25. package/core/mcp-manifest.js +69 -2
  26. package/core/memory-bootstrap.js +188 -0
  27. package/core/memory-consolidation.js +1236 -0
  28. package/core/memory-promotion.js +544 -0
  29. package/core/memory-recall.js +247 -0
  30. package/core/memory-records.js +581 -0
  31. package/core/memory-safety-gate.js +224 -0
  32. package/core/session-finalization.js +350 -0
  33. package/core/storage.js +456 -2
  34. package/docs/getting-started.md +99 -0
  35. package/docs/postprocess-contract.md +2 -2
  36. package/docs/setup.md +51 -2
  37. package/package.json +31 -9
  38. package/pipeline/normalize/adapters/codex.js +106 -0
  39. package/pipeline/normalize/detect.js +3 -2
  40. package/schema/001-base.sql +3 -0
  41. package/schema/007-v1-foundation.sql +273 -0
  42. package/schema/008-session-finalizations.sql +50 -0
  43. package/schema/009-v1-assertion-plane.sql +193 -0
  44. package/schema/010-v1-finalization-review.sql +160 -0
  45. package/schema/011-v1-compaction-claim.sql +46 -0
  46. package/schema/012-v1-compaction-lease.sql +39 -0
  47. package/schema/013-v1-compaction-lineage.sql +193 -0
  48. package/scripts/backfill-canonical-key.js +250 -0
  49. package/scripts/codex-recovery.js +532 -0
  50. package/consumers/miranda/context-inject.js +0 -119
  51. package/consumers/miranda/daily-entries.js +0 -224
  52. package/consumers/miranda/index.js +0 -364
  53. package/consumers/miranda/instance.js +0 -55
  54. package/consumers/miranda/llm.js +0 -99
  55. package/consumers/miranda/profile.json +0 -145
  56. package/consumers/miranda/prompts/summary.js +0 -303
  57. package/consumers/miranda/recall-format.js +0 -76
  58. package/consumers/miranda/render-daily-md.js +0 -186
  59. package/consumers/miranda/workspace-files.js +0 -91
  60. package/scripts/drop-entity-state-history.sql +0 -17
  61. package/scripts/drop-insights.sql +0 -12
  62. package/scripts/install-openclaw.sh +0 -59
  63. package/scripts/queries.json +0 -45
  64. package/scripts/retro-recall-bench.js +0 -409
  65. package/scripts/sample-bench-queries.sql +0 -75
package/.env.example ADDED
@@ -0,0 +1,23 @@
1
+ DATABASE_URL=postgresql://aquifer:aquifer@localhost:5432/aquifer
2
+ AQUIFER_SCHEMA=aquifer
3
+ AQUIFER_TENANT_ID=default
4
+
5
+ # Legacy is the default for backward compatibility. Use curated only after
6
+ # finalization and scoped serving have been verified for your host.
7
+ AQUIFER_MEMORY_SERVING_MODE=legacy
8
+ # AQUIFER_MEMORY_SERVING_MODE=curated
9
+ # AQUIFER_MEMORY_ACTIVE_SCOPE_KEY=project:example
10
+ # AQUIFER_MEMORY_ACTIVE_SCOPE_PATH=global,project:example
11
+
12
+ AQUIFER_EMBED_BASE_URL=http://localhost:11434/v1
13
+ AQUIFER_EMBED_MODEL=bge-m3
14
+ # EMBED_PROVIDER=ollama
15
+ # EMBED_PROVIDER=openai
16
+ # OPENAI_API_KEY=sk-...
17
+
18
+ # Optional built-in summarization.
19
+ # AQUIFER_LLM_BASE_URL=http://localhost:11434/v1
20
+ # AQUIFER_LLM_MODEL=llama3.1
21
+
22
+ # Startup migration behavior: apply | check | off.
23
+ AQUIFER_MIGRATIONS_MODE=apply
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  # 🌊 Aquifer
4
4
 
5
- **PG-native long-term memory for AI agents**
5
+ **Long-term memory for AI agents, backed by PostgreSQL.**
6
6
 
7
- *Turn-level embedding, hybrid RRF ranking, trust scoring, entity intersection, knowledge graph, entity scopingall on PostgreSQL + pgvector.*
7
+ *Store sessions, enrich them, and recall the exact turn where a decision happened without adding a separate vector database.*
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/@shadowforge0/aquifer-memory)](https://www.npmjs.com/package/@shadowforge0/aquifer-memory)
10
10
  [![PostgreSQL 15+](https://img.shields.io/badge/PostgreSQL-15%2B-336791)](https://www.postgresql.org/)
@@ -17,6 +17,73 @@
17
17
 
18
18
  ---
19
19
 
20
+ ## Start Here
21
+
22
+ Aquifer is designed to have a short default path: start PostgreSQL + embeddings, run `quickstart`, then point your MCP client at `aquifer mcp`.
23
+
24
+ For library API usage, skip to [API Reference](#api-reference). For a slightly more guided first run, see [docs/getting-started.md](docs/getting-started.md).
25
+
26
+ ### 1. Start the local stack
27
+
28
+ ```bash
29
+ docker compose up -d
30
+ # PostgreSQL 16 + pgvector and Ollama with bge-m3 (auto-pulled).
31
+ # First run pulls the model — `docker compose logs -f ollama-pull` to watch.
32
+ ```
33
+
34
+ Already running PostgreSQL + pgvector and an embedding endpoint? Skip this step. `quickstart` picks up `DATABASE_URL` and embed settings from your environment if you already have them.
35
+
36
+ ### 2. Verify end-to-end
37
+
38
+ ```bash
39
+ npx --yes @shadowforge0/aquifer-memory quickstart
40
+ ```
41
+
42
+ `quickstart` autodetects `localhost:5432` PostgreSQL and `localhost:11434` Ollama (from step 1 or your own), runs migrations, embeds a test session, recalls it, and cleans up. If it prints `✓ Aquifer is working`, you're done.
43
+
44
+ For ongoing use, install it into your project so you skip the `npx` resolution cost: `npm install @shadowforge0/aquifer-memory` then `npx aquifer quickstart`.
45
+
46
+ Using OpenAI instead of Ollama? `export EMBED_PROVIDER=openai` + `OPENAI_API_KEY=sk-...` before `quickstart` — model defaults to `text-embedding-3-small`.
47
+
48
+ ### 3. Connect your MCP client
49
+
50
+ Claude Code, Claude Desktop, or any MCP-capable client — drop this into `.mcp.json` (project-level) or `claude_desktop_config.json`:
51
+
52
+ ```jsonc
53
+ {
54
+ "mcpServers": {
55
+ "aquifer": {
56
+ "command": "npx",
57
+ "args": ["--yes", "@shadowforge0/aquifer-memory", "mcp"],
58
+ "env": {
59
+ "DATABASE_URL": "postgresql://aquifer:aquifer@localhost:5432/aquifer",
60
+ "EMBED_PROVIDER": "ollama",
61
+ "AQUIFER_MEMORY_SERVING_MODE": "legacy"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ Or run it directly: `DATABASE_URL=... EMBED_PROVIDER=ollama npx aquifer mcp`. The MCP server itself stays strict about env; `quickstart` autodetect is the try-it path, not the production one.
69
+
70
+ Keep `AQUIFER_MEMORY_SERVING_MODE=legacy` for first rollout. Switch to `curated` only when you want `session_recall` and `session_bootstrap` to serve active curated memory; `evidence_recall` stays the explicit audit/debug lane. Rollback is just flipping env or config back to `legacy`.
71
+
72
+ ### Common commands
73
+
74
+ | Goal | Command |
75
+ |---|---|
76
+ | Verify setup | `npx aquifer quickstart` |
77
+ | Start the MCP server | `npx aquifer mcp` |
78
+ | Search memory manually | `npx aquifer recall "auth middleware"` |
79
+ | Plan curated memory compaction | `npx aquifer compact --cadence daily --period-start 2026-04-27T00:00:00Z --period-end 2026-04-28T00:00:00Z` |
80
+ | Inspect storage health | `npx aquifer stats` |
81
+ | Enrich pending sessions | `npx aquifer backfill` |
82
+
83
+ Need LLM summarization, the knowledge graph, OpenAI embeddings, reranking, or operations details? See [docs/setup.md](docs/setup.md) and [Environment Variables](#environment-variables).
84
+
85
+ ---
86
+
20
87
  ## Why Aquifer?
21
88
 
22
89
  Most AI memory systems bolt a vector DB on the side. Aquifer takes a different approach: **PostgreSQL is the memory**.
@@ -61,57 +128,6 @@ Sessions, summaries, turn-level embeddings, entity graph — all live in one dat
61
128
 
62
129
  ---
63
130
 
64
- ## Quick Start (MCP Server)
65
-
66
- Two commands from zero to a working MCP memory server — no env vars to set. For library API usage, see [API Reference](#api-reference) below.
67
-
68
- ### 1. Start the stack
69
-
70
- ```bash
71
- docker compose up -d
72
- # PostgreSQL 16 + pgvector and Ollama with bge-m3 (auto-pulled).
73
- # First run pulls the model — `docker compose logs -f ollama-pull` to watch.
74
- ```
75
-
76
- Already running PostgreSQL + pgvector and an embedding endpoint? Skip this step — `quickstart` picks up `DATABASE_URL` / `EMBED_PROVIDER` from your environment if you've set them.
77
-
78
- ### 2. Verify
79
-
80
- ```bash
81
- npx --yes @shadowforge0/aquifer-memory quickstart
82
- ```
83
-
84
- That's it. `quickstart` autodetects `localhost:5432` PostgreSQL and `localhost:11434` Ollama (from step 1 or your own), runs migrations, embeds a test session, recalls it, and cleans up. If it prints `✓ Aquifer is working`, you're done.
85
-
86
- For ongoing use, install it into your project so you skip the `npx` resolution cost: `npm install @shadowforge0/aquifer-memory` then `npx aquifer quickstart`.
87
-
88
- Using OpenAI instead of Ollama? `export EMBED_PROVIDER=openai` + `OPENAI_API_KEY=sk-...` before `quickstart` — model defaults to `text-embedding-3-small`.
89
-
90
- ### 3. Wire into your MCP client
91
-
92
- Claude Code, Claude Desktop, or any MCP-capable client — drop this into `.mcp.json` (project-level) or `claude_desktop_config.json`:
93
-
94
- ```jsonc
95
- {
96
- "mcpServers": {
97
- "aquifer": {
98
- "command": "npx",
99
- "args": ["--yes", "@shadowforge0/aquifer-memory", "mcp"],
100
- "env": {
101
- "DATABASE_URL": "postgresql://aquifer:aquifer@localhost:5432/aquifer",
102
- "EMBED_PROVIDER": "ollama"
103
- }
104
- }
105
- }
106
- }
107
- ```
108
-
109
- Or run it directly: `DATABASE_URL=... EMBED_PROVIDER=ollama npx aquifer mcp`. (MCP server itself stays strict about env — `quickstart`'s autodetect is the try-it path, not the production one.)
110
-
111
- Need LLM summarization, the knowledge graph, OpenAI embeddings, or the reranker? See [Environment Variables](#environment-variables) below and [docs/setup.md](docs/setup.md).
112
-
113
- ---
114
-
115
131
  ## Environment Variables
116
132
 
117
133
  | Variable | Required? | Purpose | Example |
@@ -132,16 +148,39 @@ Need LLM summarization, the knowledge graph, OpenAI embeddings, or the reranker?
132
148
  | `AQUIFER_RERANK_PROVIDER` | No | Reranker provider: `tei`, `jina`, `openrouter` | `tei` |
133
149
  | `AQUIFER_RERANK_BASE_URL` | No | Reranker endpoint | `http://localhost:8080` |
134
150
  | `AQUIFER_AGENT_ID` | No | Default agent ID | `main` |
151
+ | `AQUIFER_MEMORY_SERVING_MODE` | No | Public serving mode: `legacy` default, or opt-in `curated` | `curated` |
152
+ | `AQUIFER_MEMORY_ACTIVE_SCOPE_KEY` | No | Default active curated scope for recall/bootstrap | `project:aquifer` |
153
+ | `AQUIFER_MEMORY_ACTIVE_SCOPE_PATH` | No | Ordered curated scope path for inheritance | `global,project:aquifer` |
135
154
  | `AQUIFER_MIGRATIONS_MODE` | No | Startup handshake mode: `apply` (default), `check`, `off` | `apply` |
136
155
  | `AQUIFER_MIGRATION_LOCK_TIMEOUT_MS` | No | Advisory-lock wait before `AQ_MIGRATION_LOCK_TIMEOUT` (default 30000) | `30000` |
156
+ | `AQUIFER_INSIGHTS_DEDUP_MODE` | No | Insights semantic dedup mode: `off` (default), `shadow`, `enforce` — env wins over code for this field only, so operators can kill-switch without redeploy | `shadow` |
157
+ | `AQUIFER_INSIGHTS_DEDUP_COSINE` | No | Cosine threshold for semantic merge (default `0.88`; warn outside `[0.75, 0.95]`) | `0.90` |
158
+ | `AQUIFER_INSIGHTS_DEDUP_CLOSE_BAND_FROM` | No | Lower bound for close-band logging (`dedupNear`); must be below threshold (default `0.85`) | `0.82` |
137
159
 
138
160
  Full env-to-config mapping is in [consumers/shared/config.js](consumers/shared/config.js).
139
161
 
162
+ Curated serving is opt-in. If a host needs rollback during rollout, set `AQUIFER_MEMORY_SERVING_MODE=legacy` and restart the MCP/CLI process; no destructive DB rollback is required.
163
+
164
+ ### Insights semantic dedup (1.5.10)
165
+
166
+ When a cron extractor (`scripts/extract-insights-from-recent-sessions.js`) or any other caller writes insights via `commitInsight`, the canonical-key layer (1.5.3+) dedupes rows whose `canonicalClaim + entities` hash to the same value. But LLMs don't always produce the same `canonicalClaim` across runs, so 1.5.10 adds a second tier: `title + body` are embedded, matched against `(tenant, agent, type)`-scoped active rows, and a top cosine above `AQUIFER_INSIGHTS_DEDUP_COSINE` triggers supersede (enforce) or metadata-only would-merge logging (shadow). Close-band hits (`closeBandFrom ≤ cos < threshold`) write `metadata.dedupNear` without supersede so operators can tune thresholds without committing.
167
+
168
+ Recommended rollout: `shadow` for one weekly cycle, inspect `SELECT metadata->>'shadowMatch' FROM insights WHERE metadata ? 'shadowMatch'`, then flip to `enforce`. Kill-switch: `AQUIFER_INSIGHTS_DEDUP_MODE=off` and restart.
169
+
170
+ Pre-1.5.3 rows with `canonical_key_v2 IS NULL` are caught by the semantic tier but skip the canonical path; a startup warn points at the one-shot backfill:
171
+
172
+ ```bash
173
+ DATABASE_URL=... \
174
+ node scripts/backfill-canonical-key.js --schema <schema> --agent <id>
175
+ ```
176
+
177
+ The script is idempotent (`WHERE canonical_key_v2 IS NULL` guard) and race-safe with live writers.
178
+
140
179
  ---
141
180
 
142
181
  ## Host Integration
143
182
 
144
- MCP is the primary integration surface. Agent hosts connect to the Aquifer MCP server, which exposes five tools: `session_recall`, `session_feedback`, `session_bootstrap`, `memory_stats`, `memory_pending`.
183
+ MCP is the primary integration surface. Agent hosts connect to the Aquifer MCP server, which exposes eight tools: `session_recall`, `evidence_recall`, `session_feedback`, `memory_feedback`, `feedback_stats`, `session_bootstrap`, `memory_stats`, `memory_pending`.
145
184
 
146
185
  | Integration | Route | Status | When to use |
147
186
  |-------------|-------|--------|-------------|
@@ -172,7 +211,7 @@ Add to your project's `.claude.json` or user-level MCP config:
172
211
  }
173
212
  ```
174
213
 
175
- Tools appear as `mcp__aquifer__session_recall`, `mcp__aquifer__session_feedback`, `mcp__aquifer__session_bootstrap`, etc.
214
+ Tools appear as `mcp__aquifer__session_recall`, `mcp__aquifer__evidence_recall`, `mcp__aquifer__session_bootstrap`, `mcp__aquifer__session_feedback`, `mcp__aquifer__memory_feedback`, `mcp__aquifer__feedback_stats`, `mcp__aquifer__memory_stats`, `mcp__aquifer__memory_pending`.
176
215
 
177
216
  ### OpenClaw
178
217
 
@@ -196,7 +235,7 @@ Add to `openclaw.json` under `mcp.servers`:
196
235
  }
197
236
  ```
198
237
 
199
- Tools materialize as `aquifer__session_recall`, `aquifer__session_feedback`, `aquifer__session_bootstrap`, `aquifer__memory_stats`, `aquifer__memory_pending` (server name prefix added by the host).
238
+ Tools materialize as `aquifer__session_recall`, `aquifer__evidence_recall`, `aquifer__session_feedback`, `aquifer__memory_feedback`, `aquifer__feedback_stats`, `aquifer__session_bootstrap`, `aquifer__memory_stats`, `aquifer__memory_pending` (server name prefix added by the host).
200
239
 
201
240
  The OpenClaw plugin (`consumers/openclaw-plugin.js`) is retained for session capture via `before_reset` but is **not** the recommended tool delivery path. Use MCP.
202
241
 
@@ -331,22 +370,6 @@ Built-in entity extraction and relationship tracking:
331
370
  - **Entity-session mapping**: which entities appear in which sessions
332
371
  - **Entity boost in ranking**: sessions with relevant entities score higher
333
372
 
334
- ---
335
-
336
- ## Benchmark: LongMemEval
337
-
338
- We tested Aquifer's retrieval pipeline on [LongMemEval_S](https://github.com/xiaowu0162/LongMemEval) — 470 questions across 19,195 sessions with 98,795 turn embeddings. Per-question haystack scoping (matching the official protocol), bge-m3 embeddings via OpenRouter.
339
-
340
- | Pipeline | R@1 | R@3 | R@5 | R@10 |
341
- |----------|-----|-----|-----|------|
342
- | Turn-only (cosine) | 89.5% | 96.6% | 98.1% | 98.9% |
343
- | Three-way hybrid (FTS + session_emb + turn_emb → RRF) | 79.2% | 94.0% | 97.7% | 98.9% |
344
- | **Hybrid + Cohere Rerank v3.5 (top-30)** | **96.0%** | **98.5%** | **99.3%** | **99.8%** |
345
-
346
- Measured 2026-04-19 on Aquifer 1.2.1.
347
-
348
- **Key findings.** Turn-level embedding alone beats session-level (26.8% → 89.5% R@1, a 3× improvement). Hybrid fusion adds robustness at R@3-R@10 but trades R@1 because FTS + session-level signals spread the top candidate across adjacent sessions. Re-ranking the hybrid top-30 with a cross-encoder (Cohere Rerank v3.5) wins back the top-1 precision and then some — +16.9pt R@1 over hybrid baseline, and 6.5pt above pure turn-level cosine. That's the production pipeline Aquifer ships by default when a reranker is configured.
349
-
350
373
  ### Multi-Tenant
351
374
 
352
375
  Every table includes `tenant_id` (default: `'default'`). Isolation is enforced at the query level — no cross-tenant data leakage by design.
@@ -400,7 +423,7 @@ The MCP consumer (`consumers/mcp.js`) already wires `aquifer.init()` before `ser
400
423
 
401
424
  #### `aquifer.listPendingMigrations()` / `aquifer.getMigrationStatus()`
402
425
 
403
- Returns `{ required, applied, pending, lastRunAt }` via a `pg_tables` signature probe. No DDL runs. Use it from a health check or from a consumer that wants to surface drift before calling `init()`.
426
+ Returns `{ required, applied, pending, lastRunAt }` via table and column signature probes (`pg_tables` plus `information_schema.columns` for alter-only migrations). No DDL runs. Use it from a health check or from a consumer that wants to surface drift before calling `init()`.
404
427
 
405
428
  #### `aquifer.migrate()`
406
429