@gmickel/gno 1.40.0 → 1.41.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 (46) hide show
  1. package/README.md +1 -0
  2. package/assets/skill/SKILL.md +17 -0
  3. package/assets/skill/cli-reference.md +48 -0
  4. package/assets/skill/mcp-reference.md +24 -0
  5. package/browser-extension/artifacts/{gno-browser-clipper-v1.40.0.zip → gno-browser-clipper-v1.41.0.zip} +0 -0
  6. package/browser-extension/artifacts/gno-browser-clipper-v1.41.0.zip.sha256 +1 -0
  7. package/browser-extension/dist/manifest.json +1 -1
  8. package/package.json +1 -1
  9. package/spec/cli.md +146 -7
  10. package/spec/db/schema.sql +17 -0
  11. package/spec/mcp.md +194 -0
  12. package/spec/output-schemas/memory-recall.schema.json +159 -0
  13. package/spec/output-schemas/memory-remember.schema.json +164 -0
  14. package/spec/output-schemas/status.schema.json +269 -54
  15. package/src/cli/commands/memory.ts +491 -0
  16. package/src/cli/commands/status.ts +23 -4
  17. package/src/cli/options.ts +4 -0
  18. package/src/cli/program.ts +127 -0
  19. package/src/config/types.ts +7 -0
  20. package/src/core/audit-provenance.ts +91 -0
  21. package/src/core/audit-workspace.ts +17 -0
  22. package/src/core/memory-diagnostics.ts +144 -0
  23. package/src/core/memory-fence.ts +239 -0
  24. package/src/core/memory-recall.ts +269 -0
  25. package/src/core/memory-record.ts +435 -0
  26. package/src/core/memory-remember.ts +425 -0
  27. package/src/core/memory-types.ts +211 -0
  28. package/src/core/memory.ts +87 -0
  29. package/src/ingestion/sync.ts +17 -0
  30. package/src/mcp/http-egress.ts +2 -0
  31. package/src/mcp/tools/index.ts +43 -0
  32. package/src/mcp/tools/memory-recall.ts +122 -0
  33. package/src/mcp/tools/memory-remember.ts +177 -0
  34. package/src/mcp/tools/memory-shared.ts +80 -0
  35. package/src/pipeline/search.ts +2 -0
  36. package/src/pipeline/types.ts +8 -0
  37. package/src/sdk/client.ts +94 -1
  38. package/src/sdk/index.ts +13 -0
  39. package/src/sdk/types.ts +28 -0
  40. package/src/serve/routes/api.ts +167 -0
  41. package/src/serve/server.ts +26 -0
  42. package/src/store/migrations/027-memory-scopes.ts +37 -0
  43. package/src/store/migrations/index.ts +2 -0
  44. package/src/store/sqlite/adapter.ts +127 -3
  45. package/src/store/types.ts +54 -0
  46. package/browser-extension/artifacts/gno-browser-clipper-v1.40.0.zip.sha256 +0 -1
package/README.md CHANGED
@@ -725,6 +725,7 @@ Open `http://localhost:3000` to:
725
725
  - **Create in place**: New notes in the current folder/collection with presets and command-palette flows
726
726
  - **Capture with provenance**: `gno capture` and Web UI Quick Capture write quick notes to an editable collection with structured `source:` metadata, typed preset scaffolds, and a receipt that separates write, sync, and embed state
727
727
  - **Same capture contract everywhere**: CLI, MCP `gno_capture`, REST `/api/capture`, SDK `client.capture()`, and Web UI Quick Capture return the same provenance receipt shape
728
+ - **Agent memory**: `gno remember` / `gno recall` (also MCP, REST, SDK) store single facts with explicit scopes and supersession, and return budgeted, cited recall with a fencing receipt. See [Memory](docs/MEMORY.md).
728
729
  - **Browser clipper**: npm-distributed unpacked Chromium extension for explicit
729
730
  visible selection or Reader capture through a local preview/confirm flow.
730
731
  See [Browser Clipper](docs/integrations/browser-clipper.md).
@@ -88,6 +88,7 @@ Recipe rules:
88
88
  | **Models** | `models list/use/pull/clear/path` | Manage local AI models |
89
89
  | **Serve** | `serve`, `daemon` | One resident Web/headless gateway and watcher |
90
90
  | **Publish** | `publish export` | Export gno.sh publish artifacts |
91
+ | **Memory** | `remember`, `recall` | Fact-granular agent memory with explicit scopes and supersession |
91
92
  | **MCP** | `mcp`, `mcp install/uninstall/status` | AI assistant integration |
92
93
  | **Skill** | `skill install/uninstall/show/paths` | Install skill for AI agents |
93
94
  | **Admin** | `peek`, `status`, `doctor`, `cleanup`, `reset`, `vec`, `completion` | Snapshot, maintenance, and diagnostics |
@@ -552,6 +553,22 @@ search must include the new note. Browser provenance fields are
552
553
  `extractionHash`, `finalBodyHash`, `clipIdentity`, and `previewDigest`—do not
553
554
  invent `sourceHash`.
554
555
 
556
+ ## Memory (remember/recall)
557
+
558
+ Use `gno remember` / `gno recall` (MCP `gno_remember` / `gno_recall`) for one
559
+ fact that may later change, not for documents (`gno capture`) or edits to
560
+ existing notes. They work only on a collection with `memoryManaged: true`.
561
+
562
+ - Explicit `--scope` is required on every call (repeatable, 1-8); there is no
563
+ implicit global scope.
564
+ - `recall` returns current facts with `gno://` cites plus a content-free
565
+ receipt; pass it back as `remember --receipt` so recalled text is not
566
+ re-stored as a new fact.
567
+ - `remember` without a decision returns candidates and writes nothing; decide
568
+ with `--add` or `--supersede <uri> --predecessor-hash <hash>` from recall.
569
+ - Details, error codes, and the fence's paraphrase limit: `docs/MEMORY.md`,
570
+ [cli-reference.md](cli-reference.md), [mcp-reference.md](mcp-reference.md).
571
+
555
572
  ## Reference-Safe Rename and Move
556
573
 
557
574
  When MCP writes are enabled and the user asks to rename or move an editable
@@ -190,6 +190,54 @@ Important behavior:
190
190
  - Capture syncs the file into FTS but does not imply embedding unless
191
191
  `embed.status` is `completed`.
192
192
 
193
+ ## Memory
194
+
195
+ Fact-granular agent memory in a collection configured with
196
+ `memoryManaged: true`. Use `gno capture` for documents; use `remember` for one
197
+ fact that may later be superseded. Full contract: `docs/MEMORY.md`.
198
+
199
+ ### gno remember
200
+
201
+ Store one fact, or propose candidates without writing.
202
+
203
+ ```bash
204
+ gno remember "Prod deploys from main only" --scope project:gno # candidates only, no write
205
+ gno remember "Prod deploys from main only" --scope project:gno --add # write a new fact
206
+ gno remember "Prod deploys from release/*" --scope project:gno \
207
+ --supersede gno://memory/facts/... --predecessor-hash <hash> --json # replace a fact
208
+ gno remember "..." --scope family --scope shared --collection memory --add --source "standup 2026-09-03"
209
+ ```
210
+
211
+ - `--scope` is required and repeatable (1-8); there is no implicit global
212
+ scope. `--collection` may be omitted only when exactly one memory-managed
213
+ collection exists.
214
+ - No decision flag returns `outcome: "candidates"` (likely matches in scope)
215
+ and writes nothing. Decide with `--add`, or `--supersede <uri>` plus
216
+ `--predecessor-hash <hash>` taken from `gno recall`.
217
+ - An exact duplicate returns `existing`; a lost supersede race exits 4
218
+ (`MEMORY_SUPERSEDE_CONFLICT`): recall again and decide again.
219
+ - `--receipt <recall.json>` fences replays of recalled spans;
220
+ `--derived-from gno://...` is rejected. `--source <text>` stores evidence.
221
+ - `--caller` / `--session` default from `$GNO_MEMORY_CALLER` /
222
+ `$GNO_MEMORY_SESSION`, then `cli:<user>` / `ppid:<pid>`.
223
+
224
+ ### gno recall
225
+
226
+ Budgeted, cited recall of current facts (superseded ones excluded).
227
+
228
+ ```bash
229
+ gno recall "deploy branch" --scope project:gno
230
+ gno recall "kindergarten" --scope family --max-facts 3 --max-tokens 256 --json > receipt.json
231
+ ```
232
+
233
+ - Same `--scope` / `--collection` / identity rules as `remember`.
234
+ - Each fact carries its `gno://` URI, `contentHash`, scopes, identity, and
235
+ egress lineage; the result includes a content-free `receipt` to hand back
236
+ to `remember --receipt`.
237
+ - Retrieval is hybrid when the embedding model is already cached, else
238
+ lexical with the reason; recall never downloads a model.
239
+ - Nothing in scope prints the self-teaching line naming `gno remember`.
240
+
193
241
  ## Search Commands
194
242
 
195
243
  ### gno search
@@ -161,6 +161,30 @@ non-overwrite captures fail instead of replacing a late-arriving file. MCP
161
161
  capture syncs the file for FTS but does not auto-embed; run `gno_embed` or
162
162
  `gno_index` afterward when vector search should include it.
163
163
 
164
+ ## Memory
165
+
166
+ `gno_recall` (read set) and `gno_remember` (write set, needs `--enable-write`)
167
+ are the fact-granular memory contract over a collection configured with
168
+ `memoryManaged: true`. Both require `collection` and explicit `scopes`
169
+ (1-8, any-intersection visibility, no implicit global scope). Identity
170
+ (`caller` / `session`) is mapped server-side from the MCP client name and
171
+ transport session, never from tool arguments.
172
+
173
+ - `gno_recall` with `query`, `collection`, `scopes`, optional `maxFacts` /
174
+ `maxTokens` returns current facts (superseded ones excluded), each with a
175
+ `gno://` cite, `contentHash`, and egress lineage, plus a content-free
176
+ `receipt`. Empty scope returns a `hint` naming `gno remember`.
177
+ - `gno_remember` with `text`, `collection`, `scopes` and no `decision` returns
178
+ `outcome: "candidates"` and writes nothing. Pass `decision: "add"` for a
179
+ new fact or `decision: "supersede"` with `predecessorUri` +
180
+ `predecessorHash` from a recall. An exact duplicate returns `existing`; a
181
+ lost supersede race returns `MEMORY_SUPERSEDE_CONFLICT` (recall, decide
182
+ again).
183
+ - Pass the recall `receipt` back on `gno_remember` so a recalled span cannot
184
+ be re-stored (`MEMORY_FENCED_REPLAY`); a `gno://` entry in `derivedFrom` is
185
+ rejected (`MEMORY_FENCED_DERIVED`). Optional `source` stores evidence.
186
+ - Writes sync for FTS before returning and do not auto-embed.
187
+
164
188
  ## Uninstall
165
189
 
166
190
  ```bash
@@ -0,0 +1 @@
1
+ f4a45361cbd92fe4639a8ee69d3cc965290d120e6aae0fbc57752ae9aeb31f94 gno-browser-clipper-v1.41.0.zip
@@ -21,5 +21,5 @@
21
21
  "content_security_policy": {
22
22
  "extension_pages": "script-src 'self'; object-src 'none'; connect-src http://127.0.0.1:*"
23
23
  },
24
- "version": "1.40.0"
24
+ "version": "1.41.0"
25
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmickel/gno",
3
- "version": "1.40.0",
3
+ "version": "1.41.0",
4
4
  "description": "Local semantic search for your documents. Index Markdown, PDF, and Office files with hybrid BM25 + vector search.",
5
5
  "keywords": [
6
6
  "embeddings",
package/spec/cli.md CHANGED
@@ -9,13 +9,13 @@ This document specifies the command-line interface for GNO, a local knowledge in
9
9
 
10
10
  ### Exit Codes
11
11
 
12
- | Code | Name | Description |
13
- | ---- | ----------- | ------------------------------------------------------------- |
14
- | 0 | SUCCESS | Command completed successfully |
15
- | 1 | VALIDATION | Validation or usage error (bad args, missing required params) |
16
- | 2 | RUNTIME | Runtime failure (IO, DB, conversion, model, network) |
17
- | 3 | NOT_RUNNING | `--status`/`--stop` found no live matching process |
18
- | 4 | BUSY | Write-lease contention on `index` / `update` / `embed` |
12
+ | Code | Name | Description |
13
+ | ---- | ----------- | ------------------------------------------------------------------------------------------ |
14
+ | 0 | SUCCESS | Command completed successfully |
15
+ | 1 | VALIDATION | Validation or usage error (bad args, missing required params) |
16
+ | 2 | RUNTIME | Runtime failure (IO, DB, conversion, model, network) |
17
+ | 3 | NOT_RUNNING | `--status`/`--stop` found no live matching process |
18
+ | 4 | BUSY | Write-lease contention on `index` / `update` / `embed`; a lost `remember --supersede` race |
19
19
 
20
20
  ### Global Flags
21
21
 
@@ -84,6 +84,8 @@ equivalent files fail closed as ambiguous.
84
84
  | bench | yes | no | no | no | no | terminal |
85
85
  | ask | yes | no | no | yes | no | terminal |
86
86
  | capture | yes | no | no | no | no | terminal |
87
+ | remember | yes | no | no | no | no | terminal |
88
+ | recall | yes | no | no | no | no | terminal |
87
89
  | get | yes | no | no | yes | no | terminal |
88
90
  | multi-get | yes | yes | no | yes | no | terminal |
89
91
  | ls | yes | yes | no | yes | no | terminal |
@@ -1613,6 +1615,141 @@ gno capture "meeting note" --quiet
1613
1615
 
1614
1616
  ---
1615
1617
 
1618
+ ### gno remember
1619
+
1620
+ Store one fact in a memory-managed collection. Thin adapter over the core
1621
+ memory service (`src/core/memory.ts`): the CLI never touches the store or the
1622
+ write lease directly.
1623
+
1624
+ **Synopsis:**
1625
+
1626
+ ```bash
1627
+ gno remember <text> --scope <scope> [--scope <scope>...] [--collection <name>] [--decision add|supersede | --add | --supersede <uri>] [--predecessor <uri>] [--predecessor-hash <hash>] [--receipt <path>] [--derived-from <uri>...] [--source <text>] [--caller <id>] [--session <id>] [--json]
1628
+ ```
1629
+
1630
+ **Scope and collection (fail-closed):**
1631
+
1632
+ - `--scope` is required and repeatable (1..8 scopes, normalized: trim,
1633
+ lowercase, NFC, dedupe). A missing scope exits `VALIDATION` with a message
1634
+ naming `--scope`. There is no implicit global scope.
1635
+ - `--collection` names a collection with `memoryManaged: true`. It may be
1636
+ omitted only when exactly one memory-managed collection is configured. A
1637
+ collection without the flag exits `VALIDATION` (`MEMORY_COLLECTION_UNMANAGED`).
1638
+
1639
+ **Identity:**
1640
+
1641
+ - `--caller` defaults to `$GNO_MEMORY_CALLER`, then `cli:<os user>`.
1642
+ - `--session` defaults to `$GNO_MEMORY_SESSION`, then `ppid:<parent pid>`.
1643
+ - Both are recorded in the fact frontmatter and bound into recall receipts.
1644
+
1645
+ **Decision:**
1646
+
1647
+ - No decision flag: candidate proposal only. Same-scope current facts are
1648
+ matched (BM25 pool of 16; cosine >= 0.83 when an embedding model is cached,
1649
+ else token Jaccard >= 0.5); nothing is written, `outcome: "candidates"`.
1650
+ - `--decision add` / `--add`: write a new fact file. An exact duplicate
1651
+ (same content hash) returns the existing record idempotently
1652
+ (`outcome: "existing"`).
1653
+ - `--decision supersede --predecessor <uri>` / `--supersede <uri>`: requires
1654
+ `--predecessor-hash <hash>` (the predecessor's `contentHash` from recall).
1655
+ Under the shared write lease the service verifies the predecessor exists,
1656
+ is current, matches the hash, and has no successor; the successor carries
1657
+ `supersedes: [<uri>]`. `--add` and `--supersede` are mutually exclusive.
1658
+ - A write returns success only after the file exists and lexical sync
1659
+ completed; the fact is retrievable before the command exits.
1660
+
1661
+ **Context fencing:**
1662
+
1663
+ - `--receipt <path>` presents a recall receipt (the `recall --json` output or
1664
+ its `receipt` object). Text whose normalized hash matches a receipted span
1665
+ is rejected (`MEMORY_FENCED_REPLAY`).
1666
+ - `--derived-from <uri>` declares origins; any `gno://` origin is rejected
1667
+ (`MEMORY_FENCED_DERIVED`).
1668
+ - Paraphrases that carry neither a receipt nor a lineage declaration cannot
1669
+ be fenced.
1670
+
1671
+ **Evidence:**
1672
+
1673
+ - `--source <text>` records free-text evidence for the fact. It is written to
1674
+ the fact frontmatter (`memory.source`) and echoed as `record.source` /
1675
+ `source` on every fact in remember and recall results.
1676
+
1677
+ **Output:**
1678
+
1679
+ - `--json` prints the shared `RememberResult` (`outcome` of `existing` |
1680
+ `candidates` | `added` | `superseded`, with `record` / `candidates`,
1681
+ `absPath`, `sync`, and `matching`). `--json` wins over global `--quiet`;
1682
+ quiet prints the record URI (or one candidate URI per line).
1683
+ - Terminal output states the outcome, URI, record id, content hash, scopes,
1684
+ `Supersedes:` when present, sync state, and the matching mode.
1685
+
1686
+ **Exit codes:** `VALIDATION` (1) for flag, scope, collection, identity,
1687
+ predecessor, and fence errors; `BUSY` (4) when another writer holds the lease
1688
+ (`MEMORY_WRITE_LEASE_BUSY`) or already superseded the predecessor
1689
+ (`MEMORY_SUPERSEDE_CONFLICT`); `RUNTIME` (2) when the file was written but
1690
+ lexical sync failed (`MEMORY_SYNC_FAILED`) or the successor's `supersedes`
1691
+ edge did not project (`MEMORY_SUPERSEDE_PROJECTION_FAILED`). The JSON envelope carries the core code in
1692
+ `details.memoryCode`.
1693
+
1694
+ **Examples:**
1695
+
1696
+ ```bash
1697
+ gno remember "Finn's kindergarten starts at 08:30" --scope family --add
1698
+ gno remember "Prod deploys from main only" --scope project:gno --scope ops
1699
+ gno remember "Prod deploys from release/*" --scope project:gno --supersede gno://memory/facts/2026/... --predecessor-hash <hash> --json
1700
+ ```
1701
+
1702
+ ---
1703
+
1704
+ ### gno recall
1705
+
1706
+ Recall current facts from a memory-managed collection under a budget.
1707
+
1708
+ **Synopsis:**
1709
+
1710
+ ```bash
1711
+ gno recall <query> --scope <scope> [--scope <scope>...] [--collection <name>] [--max-facts <n>] [--max-tokens <n>] [--caller <id>] [--session <id>] [--json]
1712
+ ```
1713
+
1714
+ **Behavior:**
1715
+
1716
+ - `--scope`, `--collection`, `--caller`, and `--session` follow the
1717
+ `gno remember` rules (fail-closed scope, memory-managed collection only).
1718
+ - Retrieval is BM25 plus a vector leg when the configured embedding model is
1719
+ cached and vectors exist (`retrieval.mode` = `hybrid`, else `lexical` with
1720
+ `retrieval.semanticUnavailable` explaining why). Query expansion, graph
1721
+ expansion, and reranking are disabled. Scope and supersession filtering run
1722
+ inside the retrieval query; superseded facts are never returned.
1723
+ - Budget: at most `--max-facts` facts (default 8) under `--max-tokens`
1724
+ (default 512). Both must be positive integers. Recall never downloads a
1725
+ model.
1726
+ - Every fact carries `uri` (`gno://`), `text`, `scopes`, `caller`,
1727
+ `session`, `createdAt`, `contentHash`, `spanHash`, `supersedes`, `score`,
1728
+ and `egressLineage`; the response carries a content-free `receipt`
1729
+ (`caller`, `session`, `issuedAt`, `memoryIds`, `spanHashes`, `digest`) plus
1730
+ `budget` and `retrieval`. Derived output inherits the strictest source
1731
+ egress policy (`egressLineage`).
1732
+ - Empty recall prints the self-teaching line naming `gno remember`
1733
+ (`hint` in JSON) and exits 0.
1734
+
1735
+ **Output:** `--json` prints the shared `RecallResult`. Terminal output lists
1736
+ numbered facts with URI, text, scopes, hash, and identity, then `Budget:`,
1737
+ `Retrieval:`, and `Receipt:` lines. Quiet prints one URI per line.
1738
+
1739
+ **Exit codes:** `VALIDATION` (1) for scope, collection, identity, and budget
1740
+ errors (a bad `--max-facts` / `--max-tokens` carries
1741
+ `details.memoryCode: MEMORY_BUDGET_INVALID` in the JSON envelope);
1742
+ `RUNTIME` (2) on retrieval failure.
1743
+
1744
+ **Examples:**
1745
+
1746
+ ```bash
1747
+ gno recall "deploy branch" --scope project:gno
1748
+ gno recall "kindergarten" --scope family --max-facts 3 --json > receipt.json
1749
+ ```
1750
+
1751
+ ---
1752
+
1616
1753
  ### gno get
1617
1754
 
1618
1755
  Retrieve a single document by reference.
@@ -3851,6 +3988,8 @@ Write-lease contention on `index` / `update` / `embed` does not use the generic
3851
3988
  | `NO_COLOR` | Disable colored output (standard) |
3852
3989
  | `PAGER` | Pager for long output (default: less -R on Unix, built-in on Windows) |
3853
3990
  | `GNO_SKILLS_HOME_OVERRIDE` | Override home dir for skill user scope (testing) |
3991
+ | `GNO_MEMORY_CALLER` | Default `--caller` identity for `gno remember` / `gno recall` |
3992
+ | `GNO_MEMORY_SESSION` | Default `--session` identity for `gno remember` / `gno recall` |
3854
3993
  | `CLAUDE_SKILLS_DIR` | Override Claude skills directory |
3855
3994
  | `CODEX_SKILLS_DIR` | Override Codex skills directory |
3856
3995
  | `CLAUDE_CONFIG_DIR` | Claude Code config dir; `gno agents` resolves Claude's instruction file under it (suppressed by an explicit home override) |
@@ -15,6 +15,7 @@
15
15
  -- doc_tags - Document tags (frontmatter and user-added)
16
16
  -- doc_links - Wiki and markdown links between documents
17
17
  -- doc_edges - Derived semantic document relationships
18
+ -- doc_memory_scopes - Indexed scopes for managed memory records
18
19
  -- activation_receipts - Bounded per-collection retrieval proof receipts
19
20
  -- retrieval_traces - Opt-in private retrieval trace headers
20
21
  -- retrieval_trace_runs/events/judgments - Bounded trace outcome records
@@ -533,6 +534,22 @@ CREATE TABLE IF NOT EXISTS doc_edges (
533
534
  CREATE INDEX IF NOT EXISTS idx_doc_edges_src_type ON doc_edges(src_doc_id, edge_type);
534
535
  CREATE INDEX IF NOT EXISTS idx_doc_edges_dst_type ON doc_edges(dst_doc_id, edge_type);
535
536
 
537
+ -- ─────────────────────────────────────────────────────────────────────────────
538
+ -- Memory Scopes (indexed, filtered inside retrieval queries)
539
+ -- ─────────────────────────────────────────────────────────────────────────────
540
+
541
+ -- One row per (managed memory record, normalized scope). Rows exist only for
542
+ -- records that pass the memory-record validator; malformed files carry no
543
+ -- scopes and therefore never enter managed recall.
544
+ CREATE TABLE IF NOT EXISTS doc_memory_scopes (
545
+ document_id INTEGER NOT NULL,
546
+ scope TEXT NOT NULL,
547
+ PRIMARY KEY (document_id, scope),
548
+ FOREIGN KEY (document_id) REFERENCES documents(id) ON DELETE CASCADE
549
+ );
550
+
551
+ CREATE INDEX IF NOT EXISTS idx_doc_memory_scopes_scope ON doc_memory_scopes(scope, document_id);
552
+
536
553
  -- ─────────────────────────────────────────────────────────────────────────────
537
554
  -- Saved Context Capsules (metadata only)
538
555
  -- ─────────────────────────────────────────────────────────────────────────────
package/spec/mcp.md CHANGED
@@ -63,6 +63,11 @@ All write tools acquire an OS-backed advisory lock at `.mcp-write.lock` under th
63
63
  If another process holds the lock, tools return `LOCKED`.
64
64
  For async jobs, the lock is held for the full job duration.
65
65
 
66
+ `gno_remember` is the one write tool whose adapter takes no lock of its own:
67
+ the core memory service acquires the same `.mcp-write.lock` lease for every
68
+ memory write, so an MCP remember and a CLI writer serialise on one lease. A
69
+ lease that stays busy past the wait window returns `MEMORY_WRITE_LEASE_BUSY`.
70
+
66
71
  ### Resident Streamable HTTP boundary
67
72
 
68
73
  `gno serve` and `gno daemon` mount the same stateful MCP surface at `/mcp`.
@@ -1173,6 +1178,85 @@ returned.
1173
1178
 
1174
1179
  ---
1175
1180
 
1181
+ ### gno_recall
1182
+
1183
+ Budgeted, cited, current-state recall from a memory-managed collection
1184
+ (read set; registered without `--enable-write`).
1185
+
1186
+ **Input Schema:**
1187
+
1188
+ ```json
1189
+ {
1190
+ "type": "object",
1191
+ "properties": {
1192
+ "query": {
1193
+ "type": "string",
1194
+ "description": "What you need to know, phrased as the fact would be stated"
1195
+ },
1196
+ "collection": {
1197
+ "type": "string",
1198
+ "description": "Memory-managed collection to recall from"
1199
+ },
1200
+ "scopes": {
1201
+ "type": "array",
1202
+ "items": { "type": "string" },
1203
+ "minItems": 1,
1204
+ "maxItems": 8,
1205
+ "description": "Explicit scopes; visibility is any-intersection, no implicit global scope"
1206
+ },
1207
+ "maxFacts": {
1208
+ "type": "integer",
1209
+ "minimum": 1,
1210
+ "maximum": 64,
1211
+ "description": "Fact budget (default 8)"
1212
+ },
1213
+ "maxTokens": {
1214
+ "type": "integer",
1215
+ "minimum": 1,
1216
+ "maximum": 8192,
1217
+ "description": "Payload token budget (default 512)"
1218
+ }
1219
+ },
1220
+ "required": ["query", "collection", "scopes"]
1221
+ }
1222
+ ```
1223
+
1224
+ **Response (`structuredContent`):** the shared `RecallResult` contract from
1225
+ `src/core/memory.ts` (one schema across CLI, MCP, REST, SDK):
1226
+
1227
+ - `facts[]` — current facts only (superseded records excluded), each with
1228
+ `uri` (`gno://` cite), `docid`, `recordId`, `text`, `scopes`, `caller`,
1229
+ `session`, `createdAt`, `contentHash`, `supersedes`, `score`, `spanHash`,
1230
+ `egressLineage`
1231
+ - `receipt` — content-free fencing receipt: `caller`, `session`, `issuedAt`,
1232
+ `memoryIds`, `spanHashes`, `digest`
1233
+ - `budget` — `maxFacts`, `maxTokens`, `usedTokens`, `omitted`
1234
+ - `retrieval` — `mode` (`lexical` | `hybrid`) and `semanticUnavailable` when
1235
+ the vector leg did not run
1236
+ - `egressLineage` — strictest source policy across returned facts (absent when
1237
+ empty)
1238
+ - `hint` — self-teaching line naming `gno remember`, present only when no fact
1239
+ was returned
1240
+
1241
+ **Identity:** `caller` is the MCP client implementation name from the
1242
+ `initialize` handshake (`mcp` when absent); `session` is the Streamable HTTP
1243
+ session id, or the per-process server instance id on stdio. Tool arguments
1244
+ never carry identity.
1245
+
1246
+ **Notes:**
1247
+
1248
+ - Scope filtering executes inside the retrieval query, before any limit
1249
+ - The MCP adapter runs the lexical leg; `retrieval.mode` reports `lexical`
1250
+ and `retrieval.semanticUnavailable` states why
1251
+ - Annotations: `readOnlyHint: true`, `idempotentHint: true`
1252
+
1253
+ **Errors:** `MEMORY_QUERY_REQUIRED`, `MEMORY_BUDGET_INVALID`,
1254
+ `MEMORY_COLLECTION_REQUIRED`, `MEMORY_COLLECTION_NOT_FOUND`,
1255
+ `MEMORY_COLLECTION_UNMANAGED`, `MEMORY_SCOPES_REQUIRED`,
1256
+ `MEMORY_SCOPES_INVALID`, `MEMORY_QUERY_FAILED`.
1257
+
1258
+ ---
1259
+
1176
1260
  ### gno_capture
1177
1261
 
1178
1262
  Create a new document in a collection (write-enabled).
@@ -1311,6 +1395,112 @@ shared `gno://schemas/capture-receipt@1.0` contract.
1311
1395
 
1312
1396
  ---
1313
1397
 
1398
+ ### gno_remember
1399
+
1400
+ Store one fact with supersession semantics in a memory-managed collection
1401
+ (write-enabled). Remember is fact-granular: `gno_capture` creates documents,
1402
+ file edits update existing notes, `gno_remember` upserts a fact.
1403
+
1404
+ **Input Schema:**
1405
+
1406
+ ```json
1407
+ {
1408
+ "type": "object",
1409
+ "properties": {
1410
+ "text": {
1411
+ "type": "string",
1412
+ "description": "One fact, stated in full (single statement, not a document)"
1413
+ },
1414
+ "collection": {
1415
+ "type": "string",
1416
+ "description": "Memory-managed collection to write into"
1417
+ },
1418
+ "scopes": {
1419
+ "type": "array",
1420
+ "items": { "type": "string" },
1421
+ "minItems": 1,
1422
+ "maxItems": 8,
1423
+ "description": "Explicit scopes; no implicit global scope"
1424
+ },
1425
+ "decision": {
1426
+ "type": "string",
1427
+ "enum": ["add", "supersede"],
1428
+ "description": "Omit to receive candidates without writing"
1429
+ },
1430
+ "predecessorUri": {
1431
+ "type": "string",
1432
+ "description": "gno:// URI of the fact being superseded (supersede only)"
1433
+ },
1434
+ "predecessorHash": {
1435
+ "type": "string",
1436
+ "description": "contentHash of the predecessor as returned by gno_recall (supersede only)"
1437
+ },
1438
+ "receipt": {
1439
+ "type": "object",
1440
+ "description": "Receipt from the gno_recall response the fact derives from",
1441
+ "properties": {
1442
+ "caller": { "type": "string" },
1443
+ "session": { "type": "string" },
1444
+ "issuedAt": { "type": "string" },
1445
+ "memoryIds": { "type": "array", "items": { "type": "string" } },
1446
+ "spanHashes": { "type": "array", "items": { "type": "string" } },
1447
+ "digest": { "type": "string" }
1448
+ }
1449
+ },
1450
+ "derivedFrom": {
1451
+ "type": "array",
1452
+ "items": { "type": "string" },
1453
+ "description": "Declared origins; any gno:// origin is rejected"
1454
+ },
1455
+ "source": {
1456
+ "type": "string",
1457
+ "description": "Free-text evidence for the fact"
1458
+ }
1459
+ },
1460
+ "required": ["text", "collection", "scopes"]
1461
+ }
1462
+ ```
1463
+
1464
+ **Response (`structuredContent`):** the shared `RememberResult` contract:
1465
+
1466
+ - `outcome: "existing"` — exact duplicate in scope; `record` is the stored
1467
+ fact, nothing written
1468
+ - `outcome: "candidates"` — likely matches and no `decision`; `candidates[]`
1469
+ carry `similarity` and `match` (`exact` | `likely` | `weak`), nothing written
1470
+ - `outcome: "added" | "superseded"` — `record`, `absPath`, and
1471
+ `sync.status` (`completed` before the call returns; the fact is lexically
1472
+ searchable)
1473
+ - `matching` — `mode` (`semantic` | `lexical`), `threshold`, and
1474
+ `semanticUnavailable` when lexical matching was used
1475
+
1476
+ **Notes:**
1477
+
1478
+ - `supersede` requires `predecessorUri` + `predecessorHash`; the predecessor
1479
+ must be current in the same collection with a matching hash and no existing
1480
+ successor, otherwise `MEMORY_PREDECESSOR_*` or `MEMORY_SUPERSEDE_CONFLICT`
1481
+ - Context fencing: text whose normalized hash matches a `spanHashes` entry on
1482
+ the presented `receipt` returns `MEMORY_FENCED_REPLAY`; a `derivedFrom`
1483
+ entry starting with `gno://` returns `MEMORY_FENCED_DERIVED`. A paraphrase
1484
+ that carries neither is indistinguishable from an original fact and is not
1485
+ fenced
1486
+ - The core service holds the shared write lease for the write and lexical
1487
+ sync; the MCP adapter takes no lock of its own
1488
+ - Identity mapping is the same as `gno_recall`
1489
+ - Annotations: `readOnlyHint: false`, `destructiveHint: false`,
1490
+ `idempotentHint: false`
1491
+
1492
+ **Errors:** `WRITE_DISABLED`, `MEMORY_TEXT_REQUIRED`,
1493
+ `MEMORY_TEXT_TOO_LARGE`, `MEMORY_COLLECTION_REQUIRED`,
1494
+ `MEMORY_COLLECTION_NOT_FOUND`, `MEMORY_COLLECTION_UNMANAGED`,
1495
+ `MEMORY_SCOPES_REQUIRED`, `MEMORY_SCOPES_INVALID`,
1496
+ `MEMORY_DECISION_INVALID`, `MEMORY_PREDECESSOR_REQUIRED`,
1497
+ `MEMORY_PREDECESSOR_NOT_FOUND`, `MEMORY_PREDECESSOR_HASH_MISMATCH`,
1498
+ `MEMORY_SUPERSEDE_CONFLICT`, `MEMORY_FENCED_REPLAY`, `MEMORY_FENCED_DERIVED`,
1499
+ `MEMORY_WRITE_LEASE_BUSY`, `MEMORY_SYNC_FAILED`,
1500
+ `MEMORY_SUPERSEDE_PROJECTION_FAILED`, `MEMORY_QUERY_FAILED`.
1501
+
1502
+ ---
1503
+
1314
1504
  ### gno_list_tags
1315
1505
 
1316
1506
  List all tags with document counts.
@@ -2402,6 +2592,10 @@ Resource errors use standard MCP error responses.
2402
2592
  - `PATH_NOT_FOUND` — Path does not exist
2403
2593
  - `JOB_CONFLICT` — Another job is already running
2404
2594
  - `LOCKED` — Another MCP process holds the write lock
2595
+ - `WRITE_DISABLED` — Write tool dispatched while writes are disabled
2596
+ - `MEMORY_*` — Memory contract errors from `gno_recall` / `gno_remember`;
2597
+ the stable code set is `MemoryErrorCode` in `src/core/memory.ts` and each
2598
+ tool section above lists the codes it returns
2405
2599
 
2406
2600
  ---
2407
2601