superlocalmemory 4.0.3 → 4.0.4

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 (51) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +11 -10
  3. package/package.json +1 -1
  4. package/plugin/.claude-plugin/plugin.json +1 -1
  5. package/plugin/CLAUDE.md +3 -3
  6. package/plugin/agents/slm-governance-advisor.md +1 -1
  7. package/plugin/agents/slm-loop-runner.md +1 -1
  8. package/plugin/agents/slm-memory-advisor.md +1 -1
  9. package/plugin/agents/slm-optimize-advisor.md +1 -1
  10. package/plugin/requirements.txt +1 -1
  11. package/plugin/skills/slm-cache/SKILL.md +1 -1
  12. package/plugin/skills/slm-compress/SKILL.md +1 -1
  13. package/plugin/skills/slm-governance/SKILL.md +1 -1
  14. package/plugin/skills/slm-graph/SKILL.md +1 -1
  15. package/plugin/skills/slm-loop/SKILL.md +1 -1
  16. package/plugin/skills/slm-mesh/SKILL.md +1 -1
  17. package/plugin/skills/slm-profile/SKILL.md +4 -4
  18. package/plugin/skills/slm-recall/SKILL.md +1 -1
  19. package/plugin/skills/slm-remember/SKILL.md +1 -1
  20. package/plugin/skills/slm-scope/SKILL.md +1 -1
  21. package/plugin/skills/slm-session/SKILL.md +1 -1
  22. package/plugin/skills/slm-status/SKILL.md +1 -1
  23. package/plugin-src/rules/AGENTS.md +3 -3
  24. package/plugin-src/skills/slm-cache/SKILL.md +1 -1
  25. package/plugin-src/skills/slm-compress/SKILL.md +1 -1
  26. package/plugin-src/skills/slm-governance/SKILL.md +1 -1
  27. package/plugin-src/skills/slm-graph/SKILL.md +1 -1
  28. package/plugin-src/skills/slm-loop/SKILL.md +1 -1
  29. package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
  30. package/plugin-src/skills/slm-profile/SKILL.md +4 -4
  31. package/plugin-src/skills/slm-recall/SKILL.md +1 -1
  32. package/plugin-src/skills/slm-remember/SKILL.md +1 -1
  33. package/plugin-src/skills/slm-scope/SKILL.md +1 -1
  34. package/plugin-src/skills/slm-session/SKILL.md +1 -1
  35. package/plugin-src/skills/slm-status/SKILL.md +1 -1
  36. package/pyproject.toml +1 -1
  37. package/src/superlocalmemory/__init__.py +1 -1
  38. package/src/superlocalmemory/integrations/bounded_loops_mcp.py +184 -0
  39. package/src/superlocalmemory/learning/database.py +2 -1
  40. package/src/superlocalmemory/mcp/profiles.py +11 -5
  41. package/src/superlocalmemory/mcp/server.py +5 -2
  42. package/src/superlocalmemory/mcp/tools_brain.py +89 -4
  43. package/src/superlocalmemory/server/routes/brain.py +6 -1
  44. package/src/superlocalmemory/storage/_migration_internals.py +4 -0
  45. package/src/superlocalmemory/storage/_schema_version.py +2 -2
  46. package/src/superlocalmemory/storage/agent_experience.py +26 -4
  47. package/src/superlocalmemory/storage/external_evidence.py +359 -0
  48. package/src/superlocalmemory/storage/migration_runner.py +5 -0
  49. package/src/superlocalmemory/storage/migrations/M041_external_evidence_receipts.py +189 -0
  50. package/src/superlocalmemory/storage/migrations/__init__.py +2 -0
  51. package/src/superlocalmemory/ui/js/od-brain.js +9 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ All notable changes to SuperLocalMemory will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [4.0.4] - 2026-08-15 — Optional Bounded Loops evidence bridge
9
+
10
+ ### Added
11
+ - An optional MCP 2 bridge discovers the separately installed
12
+ `bounded-loops-mcp` producer at call time and negotiates the public
13
+ `bounded-loops.dev/slm-bridge/v1` capability rather than pinning a producer
14
+ package version.
15
+ - `observe_bounded_loop_evidence(workspace)` explicitly imports sanitized
16
+ terminal graph evidence using the producer's `run_ref` address. It is
17
+ available in the default, `code`, `full`, and `power` MCP surfaces.
18
+ - M041 stores typed, profile-scoped external evidence in `learning.db`; Living
19
+ Brain displays honest Bounded Loop observation totals and documentation
20
+ explains the independent-product contract.
21
+
22
+ ### Safety
23
+ - The bridge accepts no caller-supplied executable or shell arguments, has a
24
+ five-second producer timeout, validates the exact v1 evidence shape, and
25
+ preserves `eligible_for_learning: false`.
26
+ - External receipts never enter recall, ranking, routing, reward, or automatic
27
+ learning. Profile erasure removes them and a durable tombstone blocks stale
28
+ processes from recreating them.
29
+
8
30
  ## [4.0.3] - 2026-08-15 — Consented host integration upgrades
9
31
 
10
32
  ### Added
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  </picture>
6
6
  </p>
7
7
 
8
- <h1 align="center">SuperLocalMemory V4.0.3</h1>
8
+ <h1 align="center">SuperLocalMemory V4.0.4</h1>
9
9
 
10
10
  <h2 align="center">Rent the LLM. Own the memory.</h2>
11
11
 
@@ -27,12 +27,12 @@ guarantee here is stated as a falsifiable invariant, tested under an adversarial
27
27
  negative control, and shipped with the harness that regenerates the evidence:
28
28
  <code>python benchmark/run_all.py --trials 200 --output-dir results/</code>. What each experiment
29
29
  does <em>not</em> exercise is stated too.</p>
30
- <p align="center"><code>v4.0.3</code> — one control plane: <strong>SLM-Mesh</strong> peer coordination · multi-scope memory (personal / shared / global) · profiles · Cache · Compress · 7-layer retrieval · code graph · Entity Explorer · skill evolution · Modes A/B/C · GDPR retention &amp; audit chain · bounded loops — across CLI, MCP, dashboard, the <strong>Claude plugin</strong>, the <strong>Codex add-on</strong>, and documented IDE integrations.<br/>
30
+ <p align="center"><code>v4.0.4</code> — one control plane: <strong>SLM-Mesh</strong> peer coordination · multi-scope memory (personal / shared / global) · profiles · Cache · Compress · 7-layer retrieval · code graph · Entity Explorer · skill evolution · Modes A/B/C · GDPR retention &amp; audit chain · bounded loops — across CLI, MCP, dashboard, the <strong>Claude plugin</strong>, the <strong>Codex add-on</strong>, and documented IDE integrations.<br/>
31
31
  Proxy: <code>slm wrap claude</code> &nbsp;·&nbsp; MCP: add <code>slm_compress</code> to your config &nbsp;·&nbsp; Skill: zero-config</p>
32
32
  <p align="center"><strong>Four public arXiv preprints</strong> · V4: <a href="https://arxiv.org/abs/2608.08253">arXiv:2608.08253</a> · companion archive: <a href="https://zenodo.org/records/21853302">Zenodo 21853302</a> (<a href="https://doi.org/10.5281/zenodo.21853302">DOI 10.5281/zenodo.21853302</a>) · prior preprints: <a href="https://arxiv.org/abs/2603.02240">2603.02240</a> · <a href="https://arxiv.org/abs/2603.14588">2603.14588</a> · <a href="https://arxiv.org/abs/2604.04514">2604.04514</a>.</p>
33
33
 
34
34
  <p align="center">
35
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/v4.0.3-Current_Release-2ea44f?style=for-the-badge&logo=checkmarx&logoColor=white" alt="v4.0.3 — Current Release"/></a>
35
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/v4.0.4-Current_Release-2ea44f?style=for-the-badge&logo=checkmarx&logoColor=white" alt="v4.0.4 — Current Release"/></a>
36
36
  <a href="https://arxiv.org/abs/2608.08253"><img src="https://img.shields.io/badge/arXiv-2608.08253-b31b1b?style=for-the-badge&logo=arxiv&logoColor=white" alt="SuperLocalMemory 4.0 paper on arXiv:2608.08253"/></a>
37
37
  <a href="https://zenodo.org/records/21853302"><img src="https://img.shields.io/badge/Zenodo-10.5281%2Fzenodo.21853302-1682D4?style=for-the-badge&logo=zenodo&logoColor=white" alt="V4 paper on Zenodo: 10.5281/zenodo.21853302"/></a>
38
38
  <a href="https://arxiv.org/abs/2603.14588"><img src="https://img.shields.io/badge/arXiv-2603.14588-b31b1b?style=for-the-badge&logo=arxiv&logoColor=white" alt="arXiv Paper"/></a>
@@ -62,7 +62,7 @@ SuperLocalMemory V4 combines conventional dense and lexical retrieval with graph
62
62
 
63
63
  **Memory with a sense of time.** SLM does not only store *what* an agent learned — it records *when*. Every fact carries ingestion timing and provenance; recall runs a dedicated temporal candidate channel alongside semantic, lexical, and associative retrieval; scenes and entity timelines reconstruct sequence; and the lifecycle lets neglected memory decay and self-archive instead of growing without bound. Time is a first-class ranking and lifecycle signal rather than a timestamp column an agent never reads — which is what lets a long-lived agent reason about how its context changed, not only what it currently holds.
64
64
 
65
- **What V4.0.3 ships.** Existing users can now preview and explicitly refresh SLM-owned host integrations with `slm upgrade-hosts`, while package installation and MCP startup no longer silently modify host hook configuration. V4 remains a *governed* local-first control plane — every canonical write is admitted, policy-authorized, tracked as durable per-store obligations (lexical, temporal, vector), and sealed by a completion manifest, so it is either fully applied or explicitly marked degraded, never silently half-done. Flagship surfaces in this release:
65
+ **What V4.0.4 ships.** Existing users can now preview and explicitly refresh SLM-owned host integrations with `slm upgrade-hosts`, while package installation and MCP startup no longer silently modify host hook configuration. V4.0.4 also adds an optional, capability-negotiated Bounded Loops observation bridge: compatible terminal graph receipts enter a separate, profile-scoped `learning.db` table and appear in Living Brain, without affecting recall, ranking, routing, or automatic learning. V4 remains a *governed* local-first control plane — every canonical write is admitted, policy-authorized, tracked as durable per-store obligations (lexical, temporal, vector), and sealed by a completion manifest, so it is either fully applied or explicitly marked degraded, never silently half-done. Flagship surfaces in this release:
66
66
 
67
67
  - **[SLM-Mesh](#slm-mesh-cross-session--cross-machine-coordination)** — authenticated cross-session and cross-machine peer coordination (messages, locks, shared state, inbox/outbox, optional discovery). Coordination only — not automatic replicated memory.
68
68
  - **Multi-scope memory & profiles** — workspaces (profiles) plus `personal` / `shared` / `global` scopes; cross-profile recall is default-deny.
@@ -71,7 +71,7 @@ SuperLocalMemory V4 combines conventional dense and lexical retrieval with graph
71
71
  - **Modes A / B / C** — local-only (A), on-device LLM enrichment (B), provider-assisted (C). An operating mode records technical locality facts; it does **not** determine EU AI Act legal compliance (that is deployment-context assessment — see [Privacy controls](#privacy-controls-and-operating-modes)).
72
72
  - **GDPR posture, retention & audit chain** — export, fail-closed cross-store erasure, retention policies, and a hash-chained audit trail. Engineering controls for compliance programs, not a legal certification.
73
73
  - **7-layer retrieval/recall stack & code graph** — multi-channel candidates (semantic, BM25, temporal, Hopfield, spreading activation) plus optional code-graph tools for blast radius and review context.
74
- - **MCP profiles** — `code` exposes **28** tools for installed coding agents; `full` **46**; `power` **58**; `whole` **91** (all registered). Also `core` (14), `mesh` (8), and the unrestricted default surface (46 with mesh enabled).
74
+ - **MCP profiles** — `code` exposes **29** tools for installed coding agents; `full` **47**; `power` **59**; `whole` **92** (all registered). Also `core` (14), `mesh` (8), and the unrestricted default surface (47 with mesh enabled).
75
75
  - **Governed write path & verifiable transactions** — admission + policy control, a per-owner obligation ledger, and a hash-sealed completion manifest with a reconciler that redrives unmet obligations.
76
76
  - **Self-healing lifecycle & admin remediation** — stale locks cleared on restart; list/resolve stuck operations from CLI, MCP, or the dashboard.
77
77
 
@@ -436,11 +436,11 @@ Control tool surface via `SLM_MCP_PROFILE`:
436
436
  | Profile | Tools | Use case |
437
437
  |:--------|:-----:|:---------|
438
438
  | `core` | 14 | Memory, session, and optimize core |
439
- | `code` | 28 | Core + portable Brain evidence + code-graph tools + profile switching + bounded loops |
439
+ | `code` | 29 | Core + portable Brain evidence + code-graph tools + profile switching + bounded loops |
440
440
  | `mesh` | 8 | SLM-Mesh only — multi-session / multi-machine coordination |
441
- | `full` | 46 | Memory + portable Brain evidence + optimize + evolution + mesh + bounded loops |
442
- | `power` | 58 | Full + administration, lifecycle, and diagnostics |
443
- | `whole` | 91 | Every registered MCP tool |
441
+ | `full` | 47 | Memory + portable Brain evidence + optimize + evolution + mesh + bounded loops |
442
+ | `power` | 59 | Full + administration, lifecycle, and diagnostics |
443
+ | `whole` | 92 | Every registered MCP tool |
444
444
 
445
445
  **Precedence:** `ALL` > `TOOLS` > `PROFILE` > `default`
446
446
 
@@ -451,7 +451,7 @@ slm mcp
451
451
 
452
452
  For a predictable small surface, set `core` explicitly. Leaving the variable
453
453
  unset retains the compatibility default, whose mesh tools follow the local
454
- mesh setting. Count-suffixed aliases remain for backward compatibility and emit a migration warning: `core14`, `code20`, `code21`, `code24`, `code28`, `mesh8`, `full38`, `full39`, `full42`, `full46`, `power50`, `power51`, `power54`, `power58`, `whole81`, `whole84`, `whole91`. Unknown names stop startup instead of silently selecting another tool set.
454
+ mesh setting. Count-suffixed aliases remain for backward compatibility and emit a migration warning: `core14`, `code20`, `code21`, `code24`, `code28`, `code29`, `mesh8`, `full38`, `full39`, `full42`, `full46`, `full47`, `power50`, `power51`, `power54`, `power58`, `power59`, `whole81`, `whole84`, `whole91`, `whole92`. Unknown names stop startup instead of silently selecting another tool set.
455
455
 
456
456
  Per-IDE configs available for Claude Code, Cursor, Windsurf, VS Code Copilot, Continue, Gemini CLI, JetBrains, Zed, and more (15 configs in `ide/configs/`). See [docs/ide-setup.md](docs/ide-setup.md).
457
457
 
@@ -684,6 +684,7 @@ interface.
684
684
  | Published benchmark evidence | [docs/benchmarks.md](docs/benchmarks.md) |
685
685
  | CLI reference | [docs/cli-reference.md](docs/cli-reference.md) |
686
686
  | MCP tools reference | [docs/mcp-tools.md](docs/mcp-tools.md) |
687
+ | Optional Bounded Loops bridge | [docs/bounded-loops-bridge.md](docs/bounded-loops-bridge.md) |
687
688
  | Getting started | [docs/getting-started.md](docs/getting-started.md) |
688
689
  | IDE setup (15 configs) | [docs/ide-setup.md](docs/ide-setup.md) |
689
690
  | Teams, users, and RBAC | [docs/rbac-teams.md](docs/rbac-teams.md) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superlocalmemory",
3
- "version": "4.0.3",
3
+ "version": "4.0.4",
4
4
  "description": "Local-first agent memory with MCP and an agent-native CLI. Documented clients include Claude Code, Cursor, and Windsurf.",
5
5
  "keywords": [
6
6
  "ai-memory",
@@ -15,5 +15,5 @@
15
15
  "mcpServers": "./.mcp.json",
16
16
  "name": "superlocalmemory",
17
17
  "repository": "https://github.com/qualixar/superlocalmemory",
18
- "version": "4.0.3"
18
+ "version": "4.0.4"
19
19
  }
package/plugin/CLAUDE.md CHANGED
@@ -1,4 +1,4 @@
1
- <!-- BEGIN SuperLocalMemory v4.0.3 -->
1
+ <!-- BEGIN SuperLocalMemory v4.0.4 -->
2
2
 
3
3
  ## SuperLocalMemory (SLM) — Agent Rules
4
4
 
@@ -39,6 +39,6 @@ slm-recall · slm-remember · slm-session · slm-status · slm-cache · slm-comp
39
39
  ### Subagents
40
40
  slm-memory-advisor (memory decisions, session hygiene, scope/profile guidance) · slm-optimize-advisor (context compression + KV cache) · slm-governance-advisor (scope/roles/compliance/GDPR)
41
41
 
42
- <!-- END SuperLocalMemory v4.0.3 -->
42
+ <!-- END SuperLocalMemory v4.0.4 -->
43
43
 
44
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
44
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -77,4 +77,4 @@ slm-scope · slm-governance · slm-profile · slm-remember · slm-recall
77
77
  # What NOT to do
78
78
  Never session_init twice; never forget without dry-run preview; never store secrets; never bypass role checks; never claim an erasure succeeded without verifying via recall.
79
79
 
80
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
80
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -68,4 +68,4 @@ assessment. The gate is the authority.
68
68
 
69
69
  ---
70
70
 
71
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
71
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -46,4 +46,4 @@ slm-recall · slm-remember · slm-session · slm-scope · slm-profile · slm-gov
46
46
  # What NOT to do
47
47
  Never session_init twice; never forget dry_run=False without reporting preview; never dump a whole file into remember; never invent a memory; never claim "saved" without success:true / clean CLI exit; never bypass scope or governance restrictions.
48
48
 
49
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
49
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -41,4 +41,4 @@ slm-compress · slm-cache · slm-status · slm-profile
41
41
  # What NOT to do
42
42
  Never compress code-for-edit/JSON-to-parse/<500 chars; never store secrets/ccr_ids; never let optimize failure block/alter the task; never claim a specific savings %; never carry ccr_ids across profile switches.
43
43
 
44
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
44
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -1 +1 @@
1
- superlocalmemory==4.0.3
1
+ superlocalmemory==4.0.4
@@ -145,4 +145,4 @@ These subcommands control daemon-level cache settings. They do not read or write
145
145
 
146
146
  ---
147
147
 
148
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
148
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -147,4 +147,4 @@ Content over 1 MB (1 000 000 bytes UTF-8) is processed but `reversible` is force
147
147
 
148
148
  ---
149
149
 
150
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
150
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -245,4 +245,4 @@ Before running any destructive operation (`forget`, `compact_memories`):
245
245
 
246
246
  ---
247
247
 
248
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
248
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -311,4 +311,4 @@ profile. See `slm-profile` for the full profile switching workflow.
311
311
 
312
312
  ---
313
313
 
314
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
314
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -96,4 +96,4 @@ paused, name the approval needed; when errored, quote the short detail.
96
96
 
97
97
  ---
98
98
 
99
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
99
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -279,4 +279,4 @@ mesh availability.
279
279
 
280
280
  ---
281
281
 
282
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
282
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -31,9 +31,9 @@ project, a client engagement, a production vs staging environment.
31
31
  | Profile | Tools | When to use |
32
32
  |---------|-------|-------------|
33
33
  | `core` | 14 tools — remember, recall, search, session, optimize | Minimal footprint, no code tools |
34
- | `code` | 28 tools — core + portable Brain evidence + code graph + profile switching + bounded loops | Default for IDE/coding agents |
35
- | `full` | 46 tools — code + all memory ops + mesh + bounded loops | Multi-session, team workflows |
36
- | `power` | 58 tools — full + governance + behavioral tools | Enterprise, admin, audit use cases |
34
+ | `code` | 29 tools — core + portable Brain evidence + code graph + profile switching + bounded loops | Default for IDE/coding agents |
35
+ | `full` | 47 tools — code + all memory ops + mesh + bounded loops | Multi-session, team workflows |
36
+ | `power` | 59 tools — full + governance + behavioral tools | Enterprise, admin, audit use cases |
37
37
  | `mesh` | 8 tools — mesh coordination only | Lightweight cross-session signalling |
38
38
 
39
39
  The profile is set at MCP server startup via `SLM_MCP_PROFILE` in the MCP config.
@@ -145,4 +145,4 @@ Name them differently in your MCP config (e.g. `superlocalmemory-personal` and
145
145
 
146
146
  ---
147
147
 
148
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
148
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -236,4 +236,4 @@ before recalling, then switch back. See `slm-profile` for workspace switching.
236
236
 
237
237
  ---
238
238
 
239
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
239
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -238,4 +238,4 @@ different workspace, use `switch_profile` first. See `slm-profile`.
238
238
 
239
239
  ---
240
240
 
241
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
241
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -173,4 +173,4 @@ to review the impact. See `slm-remember` for the full deletion discipline.
173
173
 
174
174
  ---
175
175
 
176
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
176
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -227,4 +227,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
227
227
 
228
228
  ---
229
229
 
230
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
230
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -163,4 +163,4 @@ multi-profile setup. To switch the active profile, see `slm-profile`.
163
163
 
164
164
  ---
165
165
 
166
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
166
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -82,10 +82,10 @@ When the SLM MCP server is unavailable, use these CLI equivalents:
82
82
 
83
83
  ## Tool reference (core profile — 14 tools)
84
84
 
85
- > The MCP config ships `SLM_MCP_PROFILE=code` (28 tools): the 14 core memory tools below
85
+ > The MCP config ships `SLM_MCP_PROFILE=code` (29 tools): the 14 core memory tools below
86
86
  > **plus** 6 code-graph tools (`build_code_graph`, `get_blast_radius`, `query_graph`,
87
87
  > `semantic_search_code`, `get_review_context`, `detect_changes`) and `switch_profile`.
88
- > Use `full` (46 tools) to add mesh coordination. Use `power` (58 tools) for governance
88
+ > Use `full` (47 tools) to add mesh coordination. Use `power` (59 tools) for governance
89
89
  > and audit tools. See slm-profile for profile switching.
90
90
 
91
91
  | Tool | Signature (key params) | Notes |
@@ -128,4 +128,4 @@ When the SLM MCP server is unavailable, use these CLI equivalents:
128
128
  - **slm-optimize-advisor** — context compression and KV cache
129
129
  - **slm-governance-advisor** — scope/role compliance, retention policies, GDPR
130
130
 
131
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
131
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -145,4 +145,4 @@ These subcommands control daemon-level cache settings. They do not read or write
145
145
 
146
146
  ---
147
147
 
148
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
148
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -147,4 +147,4 @@ Content over 1 MB (1 000 000 bytes UTF-8) is processed but `reversible` is force
147
147
 
148
148
  ---
149
149
 
150
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
150
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -245,4 +245,4 @@ Before running any destructive operation (`forget`, `compact_memories`):
245
245
 
246
246
  ---
247
247
 
248
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
248
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -311,4 +311,4 @@ profile. See `slm-profile` for the full profile switching workflow.
311
311
 
312
312
  ---
313
313
 
314
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
314
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -96,4 +96,4 @@ paused, name the approval needed; when errored, quote the short detail.
96
96
 
97
97
  ---
98
98
 
99
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
99
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
@@ -279,4 +279,4 @@ mesh availability.
279
279
 
280
280
  ---
281
281
 
282
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
282
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -31,9 +31,9 @@ project, a client engagement, a production vs staging environment.
31
31
  | Profile | Tools | When to use |
32
32
  |---------|-------|-------------|
33
33
  | `core` | 14 tools — remember, recall, search, session, optimize | Minimal footprint, no code tools |
34
- | `code` | 28 tools — core + portable Brain evidence + code graph + profile switching + bounded loops | Default for IDE/coding agents |
35
- | `full` | 46 tools — code + all memory ops + mesh + bounded loops | Multi-session, team workflows |
36
- | `power` | 58 tools — full + governance + behavioral tools | Enterprise, admin, audit use cases |
34
+ | `code` | 29 tools — core + portable Brain evidence + code graph + profile switching + bounded loops | Default for IDE/coding agents |
35
+ | `full` | 47 tools — code + all memory ops + mesh + bounded loops | Multi-session, team workflows |
36
+ | `power` | 59 tools — full + governance + behavioral tools | Enterprise, admin, audit use cases |
37
37
  | `mesh` | 8 tools — mesh coordination only | Lightweight cross-session signalling |
38
38
 
39
39
  The profile is set at MCP server startup via `SLM_MCP_PROFILE` in the MCP config.
@@ -145,4 +145,4 @@ Name them differently in your MCP config (e.g. `superlocalmemory-personal` and
145
145
 
146
146
  ---
147
147
 
148
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
148
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -236,4 +236,4 @@ before recalling, then switch back. See `slm-profile` for workspace switching.
236
236
 
237
237
  ---
238
238
 
239
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
239
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -238,4 +238,4 @@ different workspace, use `switch_profile` first. See `slm-profile`.
238
238
 
239
239
  ---
240
240
 
241
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
241
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -173,4 +173,4 @@ to review the impact. See `slm-remember` for the full deletion discipline.
173
173
 
174
174
  ---
175
175
 
176
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
176
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -227,4 +227,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
227
227
 
228
228
  ---
229
229
 
230
- *SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later*
230
+ *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
@@ -163,4 +163,4 @@ multi-profile setup. To switch the active profile, see `slm-profile`.
163
163
 
164
164
  ---
165
165
 
166
- SuperLocalMemory v4.0.3 · Qualixar · AGPL-3.0-or-later
166
+ SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "superlocalmemory"
3
- version = "4.0.3"
3
+ version = "4.0.4"
4
4
  description = "Local-first agent memory with auditable hybrid retrieval"
5
5
  readme = "README.md"
6
6
  license = "AGPL-3.0-or-later"
@@ -32,7 +32,7 @@ if "OMP_NUM_THREADS" not in os.environ:
32
32
  os.environ["OMP_NUM_THREADS"] = "2"
33
33
  # ---------------------------------------------------------------------------
34
34
 
35
- __version__ = "4.0.3"
35
+ __version__ = "4.0.4"
36
36
 
37
37
  _REQUIRED_VERSIONS = {
38
38
  "sentence_transformers": "5.3.0",
@@ -0,0 +1,184 @@
1
+ """Versioned, read-only contract boundary for Bounded Loops MCP evidence."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ import os
8
+ import shutil
9
+ import stat
10
+ from collections.abc import Awaitable, Callable
11
+ from copy import deepcopy
12
+ from datetime import timedelta
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ CONTRACT_ID = "bounded-loops.dev/slm-bridge/v1"
17
+ _OBSERVATION_TIMEOUT_SECONDS = 5.0
18
+ _MAX_MCP_TEXT_BYTES = 2 * 1024 * 1024
19
+ _ADVERTISEMENT = {
20
+ "id": CONTRACT_ID,
21
+ "tool": "bl_graph_evidence",
22
+ "operation": "observe_terminal_run",
23
+ }
24
+
25
+
26
+ class BridgeUnavailable(ValueError):
27
+ """The installed producer does not advertise a compatible bridge contract."""
28
+
29
+
30
+ def supports_bridge(capabilities: dict[str, Any]) -> bool:
31
+ """Negotiate on the declared public contract, never producer semver."""
32
+ advertised = capabilities.get("evidence_contracts")
33
+ return isinstance(advertised, list) and any(
34
+ isinstance(item, dict)
35
+ and all(item.get(key) == value for key, value in _ADVERTISEMENT.items())
36
+ for item in advertised
37
+ )
38
+
39
+
40
+ def bridge_payload(evidence: dict[str, Any], *, profile_id: str) -> dict[str, Any]:
41
+ """Attach active-profile identity after refusing incompatible evidence."""
42
+ if evidence.get("contract") != CONTRACT_ID:
43
+ raise BridgeUnavailable("unsupported bounded-loops evidence contract")
44
+ if evidence.get("eligible_for_learning") is not False:
45
+ raise BridgeUnavailable("bounded-loops evidence is not observation-only")
46
+ # The producer has organisation/project metadata for its own control plane.
47
+ # SLM stores only the v1 observation receipt needed by its profile-scoped
48
+ # learning database; retaining arbitrary producer extensions would turn a
49
+ # versioned contract into an unbounded schema sink.
50
+ fields = (
51
+ "contract",
52
+ "workspace_id",
53
+ "run_ref",
54
+ "run_id",
55
+ "outcome",
56
+ "run_state",
57
+ "demonstration",
58
+ "eligible_for_learning",
59
+ "terminal_at",
60
+ "graph_digest",
61
+ "plan_digest",
62
+ "policy_digest",
63
+ "receipt",
64
+ "nodes",
65
+ )
66
+ if any(field not in evidence for field in fields):
67
+ raise BridgeUnavailable("bounded-loops evidence is missing required v1 fields")
68
+ return {field: deepcopy(evidence[field]) for field in fields} | {"profile_id": profile_id}
69
+
70
+
71
+ async def observe_terminal_runs(
72
+ call_tool: Callable[[str, dict[str, Any]], Awaitable[dict[str, Any]]], *, profile_id: str
73
+ ) -> list[dict[str, Any]]:
74
+ """Collect only producer-advertised terminal evidence over an injected MCP transport."""
75
+ discovery = await call_tool("bl_capabilities", {})
76
+ if discovery.get("status") != "ok" or not supports_bridge(discovery.get("capabilities", {})):
77
+ raise BridgeUnavailable("bounded-loops does not advertise slm-bridge/v1")
78
+ listing = await call_tool("bl_graph_terminal_runs", {"limit": 100})
79
+ if listing.get("status") != "ok" or listing.get("contract") != CONTRACT_ID:
80
+ raise BridgeUnavailable("bounded-loops terminal listing is unavailable")
81
+ runs = listing.get("runs")
82
+ if not isinstance(runs, list):
83
+ raise BridgeUnavailable("bounded-loops terminal listing is malformed")
84
+ # The producer's limit is advisory. Keep this explicit operation bounded
85
+ # even against a compatible but faulty/malicious producer.
86
+ runs = runs[:100]
87
+ observed: list[dict[str, Any]] = []
88
+ for run in runs:
89
+ if not isinstance(run, dict) or not isinstance(run.get("run_ref"), str):
90
+ raise BridgeUnavailable("bounded-loops terminal listing is malformed")
91
+ response = await call_tool("bl_graph_evidence", {"run_ref": run["run_ref"]})
92
+ if response.get("status") == "unavailable":
93
+ continue
94
+ if response.get("status") != "ok" or not isinstance(response.get("evidence"), dict):
95
+ raise BridgeUnavailable("bounded-loops evidence response is malformed")
96
+ observed.append(bridge_payload(response["evidence"], profile_id=profile_id))
97
+ return observed
98
+
99
+
100
+ async def observe_from_stdio(*, command: str, cwd: str, profile_id: str) -> list[dict[str, Any]]:
101
+ """Run one bounded, explicit MCP 2 observation; never call from recall or remember."""
102
+ executable, workspace = Path(command), Path(cwd)
103
+ if (
104
+ not executable.is_absolute()
105
+ or not executable.is_file()
106
+ or not workspace.is_absolute()
107
+ or not workspace.is_dir()
108
+ or workspace.is_symlink()
109
+ ):
110
+ raise BridgeUnavailable(
111
+ "bounded-loops bridge requires an approved executable and workspace"
112
+ )
113
+ try:
114
+ executable = executable.resolve(strict=True)
115
+ workspace = workspace.resolve(strict=True)
116
+ mode = executable.stat().st_mode
117
+ except OSError as exc:
118
+ raise BridgeUnavailable("bounded-loops bridge path is unavailable") from exc
119
+ if not stat.S_ISREG(mode) or (
120
+ os.name != "nt" and mode & (stat.S_IWGRP | stat.S_IWOTH)
121
+ ):
122
+ raise BridgeUnavailable("bounded-loops executable is not a trusted regular file")
123
+ if executable.stat().st_uid not in {0, os.geteuid()}:
124
+ raise BridgeUnavailable("bounded-loops executable owner is not trusted")
125
+
126
+ from mcp import ClientSession, StdioServerParameters
127
+ from mcp.client.stdio import stdio_client
128
+
129
+ try:
130
+ parameters = StdioServerParameters(
131
+ command=str(executable), args=[], cwd=str(workspace)
132
+ )
133
+ async with stdio_client(parameters) as (read, write):
134
+ async with ClientSession(
135
+ read,
136
+ write,
137
+ read_timeout_seconds=timedelta(seconds=_OBSERVATION_TIMEOUT_SECONDS),
138
+ ) as session:
139
+ async def observe() -> list[dict[str, Any]]:
140
+ await session.initialize()
141
+
142
+ async def call(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
143
+ result = await session.call_tool(name, arguments)
144
+ if result.isError:
145
+ raise BridgeUnavailable(
146
+ "bounded-loops rejected the observation request"
147
+ )
148
+ texts = [item.text for item in result.content if hasattr(item, "text")]
149
+ if len(texts) != 1 or len(texts[0].encode("utf-8")) > _MAX_MCP_TEXT_BYTES:
150
+ raise BridgeUnavailable("bounded-loops returned an invalid MCP payload")
151
+ try:
152
+ payload = json.loads(texts[0])
153
+ except json.JSONDecodeError as exc:
154
+ raise BridgeUnavailable("bounded-loops returned invalid JSON") from exc
155
+ if not isinstance(payload, dict):
156
+ raise BridgeUnavailable("bounded-loops returned an invalid MCP payload")
157
+ return payload
158
+
159
+ return await observe_terminal_runs(call, profile_id=profile_id)
160
+ return await asyncio.wait_for(observe(), timeout=_OBSERVATION_TIMEOUT_SECONDS)
161
+ except BridgeUnavailable:
162
+ raise
163
+ except Exception as exc:
164
+ raise BridgeUnavailable("bounded-loops observation timed out or could not start") from exc
165
+
166
+
167
+ async def observe_installed(*, workspace: str, profile_id: str) -> list[dict[str, Any]]:
168
+ """Observe a user-installed producer without accepting an agent command.
169
+
170
+ Discovery deliberately resolves exactly the public ``bounded-loops-mcp``
171
+ executable. It does not accept a command, shell fragment, or arguments
172
+ from an MCP caller; the only caller-supplied value is the existing project
173
+ workspace whose Bounded Loops state is to be read.
174
+ """
175
+ command = shutil.which("bounded-loops-mcp")
176
+ if command is None:
177
+ raise BridgeUnavailable("bounded-loops-mcp is not installed")
178
+ if not Path(command).is_absolute():
179
+ raise BridgeUnavailable("bounded-loops-mcp discovery returned an unsafe path")
180
+ if Path(command).resolve().name not in {"bounded-loops-mcp", "bounded-loops-mcp.exe"}:
181
+ raise BridgeUnavailable("bounded-loops-mcp discovery returned an unsafe executable")
182
+ return await observe_from_stdio(
183
+ command=str(Path(command).resolve()), cwd=workspace, profile_id=profile_id
184
+ )
@@ -613,7 +613,8 @@ class LearningDatabase:
613
613
  row[0]
614
614
  for row in conn.execute(
615
615
  "SELECT name FROM sqlite_master WHERE type='table' "
616
- "AND name IN ('agent_experiences', 'cognitive_turn_receipts')"
616
+ "AND name IN ('agent_experiences', 'cognitive_turn_receipts', "
617
+ "'external_evidence_receipts')"
617
618
  )
618
619
  }
619
620
  if profile_id is None: