superlocalmemory 4.0.9 → 4.1.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 (165) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +245 -0
  3. package/README.md +7 -7
  4. package/package.json +4 -2
  5. package/plugin/.claude-plugin/plugin.json +2 -2
  6. package/plugin/CLAUDE.md +3 -3
  7. package/plugin/agents/slm-governance-advisor.md +1 -1
  8. package/plugin/agents/slm-loop-runner.md +4 -4
  9. package/plugin/agents/slm-memory-advisor.md +1 -1
  10. package/plugin/agents/slm-optimize-advisor.md +1 -1
  11. package/plugin/requirements.txt +1 -1
  12. package/plugin/skills/slm-cache/SKILL.md +1 -1
  13. package/plugin/skills/slm-compress/SKILL.md +1 -1
  14. package/plugin/skills/slm-governance/SKILL.md +1 -1
  15. package/plugin/skills/slm-graph/SKILL.md +1 -1
  16. package/plugin/skills/slm-loop/SKILL.md +2 -2
  17. package/plugin/skills/slm-mesh/SKILL.md +1 -1
  18. package/plugin/skills/slm-profile/SKILL.md +5 -5
  19. package/plugin/skills/slm-recall/SKILL.md +102 -15
  20. package/plugin/skills/slm-remember/SKILL.md +35 -3
  21. package/plugin/skills/slm-scope/SKILL.md +1 -1
  22. package/plugin/skills/slm-session/SKILL.md +29 -3
  23. package/plugin/skills/slm-status/SKILL.md +1 -1
  24. package/plugin-src/rules/AGENTS.md +16 -8
  25. package/plugin-src/skills/slm-cache/SKILL.md +1 -1
  26. package/plugin-src/skills/slm-compress/SKILL.md +1 -1
  27. package/plugin-src/skills/slm-governance/SKILL.md +1 -1
  28. package/plugin-src/skills/slm-graph/SKILL.md +1 -1
  29. package/plugin-src/skills/slm-loop/SKILL.md +2 -2
  30. package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
  31. package/plugin-src/skills/slm-profile/SKILL.md +5 -5
  32. package/plugin-src/skills/slm-recall/SKILL.md +102 -15
  33. package/plugin-src/skills/slm-remember/SKILL.md +35 -3
  34. package/plugin-src/skills/slm-scope/SKILL.md +1 -1
  35. package/plugin-src/skills/slm-session/SKILL.md +29 -3
  36. package/plugin-src/skills/slm-status/SKILL.md +1 -1
  37. package/pyproject.toml +1 -1
  38. package/src/superlocalmemory/__init__.py +1 -1
  39. package/src/superlocalmemory/cli/commands.py +308 -20
  40. package/src/superlocalmemory/cli/daemon.py +30 -0
  41. package/src/superlocalmemory/cli/db_migrate.py +71 -1
  42. package/src/superlocalmemory/cli/gdpr_cmd.py +15 -2
  43. package/src/superlocalmemory/cli/main.py +26 -4
  44. package/src/superlocalmemory/code_graph/bridge/maintenance.py +8 -0
  45. package/src/superlocalmemory/code_graph/database.py +44 -0
  46. package/src/superlocalmemory/compliance/gdpr.py +449 -39
  47. package/src/superlocalmemory/core/admission.py +231 -11
  48. package/src/superlocalmemory/core/backend_orchestrator.py +190 -84
  49. package/src/superlocalmemory/core/config.py +90 -11
  50. package/src/superlocalmemory/core/consolidation_engine.py +34 -0
  51. package/src/superlocalmemory/core/engine.py +140 -11
  52. package/src/superlocalmemory/core/fact_consolidator.py +316 -125
  53. package/src/superlocalmemory/core/graph_analyzer.py +76 -112
  54. package/src/superlocalmemory/core/graph_metrics.py +597 -0
  55. package/src/superlocalmemory/core/graph_pruner.py +121 -0
  56. package/src/superlocalmemory/core/maintenance.py +44 -6
  57. package/src/superlocalmemory/core/maintenance_scheduler.py +205 -0
  58. package/src/superlocalmemory/core/memory_health.py +266 -0
  59. package/src/superlocalmemory/core/mode_capability.py +111 -0
  60. package/src/superlocalmemory/core/ollama_validator.py +315 -0
  61. package/src/superlocalmemory/core/operation_policy_registry.py +1 -1
  62. package/src/superlocalmemory/core/operation_request.py +1 -1
  63. package/src/superlocalmemory/core/ops_remediation.py +2 -2
  64. package/src/superlocalmemory/core/projection_drain.py +380 -0
  65. package/src/superlocalmemory/core/recall_pipeline.py +390 -3
  66. package/src/superlocalmemory/core/recall_worker.py +6 -3
  67. package/src/superlocalmemory/core/scale_autopromote.py +196 -0
  68. package/src/superlocalmemory/core/scale_engine.py +16 -2
  69. package/src/superlocalmemory/core/score_contract.py +21 -1
  70. package/src/superlocalmemory/core/session_identity.py +85 -0
  71. package/src/superlocalmemory/core/status_contract.py +108 -0
  72. package/src/superlocalmemory/core/store_pipeline.py +78 -3
  73. package/src/superlocalmemory/core/worker_pool.py +4 -4
  74. package/src/superlocalmemory/core/working_memory.py +288 -0
  75. package/src/superlocalmemory/encoding/cognitive_consolidator.py +51 -7
  76. package/src/superlocalmemory/encoding/context_generator.py +1 -1
  77. package/src/superlocalmemory/encoding/entity_resolver.py +38 -0
  78. package/src/superlocalmemory/encoding/fact_extractor.py +18 -14
  79. package/src/superlocalmemory/encoding/prospective_markers.py +262 -0
  80. package/src/superlocalmemory/encoding/type_router.py +12 -12
  81. package/src/superlocalmemory/evolution/mutation_generator.py +30 -4
  82. package/src/superlocalmemory/graph/cozo_adjacency.py +122 -0
  83. package/src/superlocalmemory/graph/cozo_backend.py +103 -138
  84. package/src/superlocalmemory/hooks/portable_kit.py +10 -2
  85. package/src/superlocalmemory/learning/bandit.py +43 -0
  86. package/src/superlocalmemory/learning/consolidation_worker.py +54 -0
  87. package/src/superlocalmemory/learning/database.py +60 -3
  88. package/src/superlocalmemory/learning/entity_compiler.py +21 -58
  89. package/src/superlocalmemory/learning/feedback.py +3 -1
  90. package/src/superlocalmemory/learning/outcomes.py +47 -16
  91. package/src/superlocalmemory/learning/pattern_miner.py +28 -3
  92. package/src/superlocalmemory/learning/pattern_miner_constants.py +43 -0
  93. package/src/superlocalmemory/learning/pcos.py +291 -0
  94. package/src/superlocalmemory/learning/reward_from_outcomes.py +365 -0
  95. package/src/superlocalmemory/learning/reward_proxy.py +100 -10
  96. package/src/superlocalmemory/learning/signal_kinds.py +79 -0
  97. package/src/superlocalmemory/mcp/profiles.py +14 -2
  98. package/src/superlocalmemory/mcp/server.py +1 -1
  99. package/src/superlocalmemory/mcp/session_binding.py +92 -0
  100. package/src/superlocalmemory/mcp/tools_active.py +2 -1
  101. package/src/superlocalmemory/mcp/tools_core.py +71 -42
  102. package/src/superlocalmemory/mcp/tools_ops.py +2 -2
  103. package/src/superlocalmemory/mcp/tools_v28.py +20 -1
  104. package/src/superlocalmemory/parameterization/pattern_extractor.py +14 -1
  105. package/src/superlocalmemory/parameterization/soft_prompt_generator.py +98 -0
  106. package/src/superlocalmemory/retrieval/bm25_channel.py +68 -11
  107. package/src/superlocalmemory/retrieval/channel_status.py +117 -0
  108. package/src/superlocalmemory/retrieval/engine.py +106 -11
  109. package/src/superlocalmemory/retrieval/entity_channel.py +217 -257
  110. package/src/superlocalmemory/retrieval/graph_adjacency.py +219 -0
  111. package/src/superlocalmemory/retrieval/scope_policy.py +42 -1
  112. package/src/superlocalmemory/retrieval/semantic_channel.py +47 -5
  113. package/src/superlocalmemory/retrieval/spreading.py +288 -0
  114. package/src/superlocalmemory/retrieval/temporal_channel.py +13 -1
  115. package/src/superlocalmemory/retrieval/vector_store.py +63 -0
  116. package/src/superlocalmemory/server/api.py +26 -2
  117. package/src/superlocalmemory/server/asset_versions.py +171 -0
  118. package/src/superlocalmemory/server/bandit_loops.py +17 -1
  119. package/src/superlocalmemory/server/rbac_enforce.py +26 -6
  120. package/src/superlocalmemory/server/recall_serializer.py +9 -0
  121. package/src/superlocalmemory/server/routes/abstraction.py +201 -0
  122. package/src/superlocalmemory/server/routes/behavioral.py +75 -10
  123. package/src/superlocalmemory/server/routes/compliance.py +98 -18
  124. package/src/superlocalmemory/server/routes/config_api.py +186 -4
  125. package/src/superlocalmemory/server/routes/data_io.py +29 -1
  126. package/src/superlocalmemory/server/routes/entity.py +13 -1
  127. package/src/superlocalmemory/server/routes/evolution.py +178 -0
  128. package/src/superlocalmemory/server/routes/ingest.py +8 -0
  129. package/src/superlocalmemory/server/routes/learning_telemetry.py +2 -1
  130. package/src/superlocalmemory/server/routes/memories.py +49 -7
  131. package/src/superlocalmemory/server/routes/mesh.py +1 -1
  132. package/src/superlocalmemory/server/routes/timeline.py +4 -0
  133. package/src/superlocalmemory/server/routes/v3_api.py +193 -17
  134. package/src/superlocalmemory/server/ui.py +24 -1
  135. package/src/superlocalmemory/server/unified_daemon.py +292 -9
  136. package/src/superlocalmemory/storage/_migration_internals.py +35 -0
  137. package/src/superlocalmemory/storage/_schema_version.py +24 -3
  138. package/src/superlocalmemory/storage/database.py +598 -82
  139. package/src/superlocalmemory/storage/embedding_codec.py +71 -0
  140. package/src/superlocalmemory/storage/lineage_retention.py +236 -0
  141. package/src/superlocalmemory/storage/logical_edges.py +43 -2
  142. package/src/superlocalmemory/storage/migration_runner.py +130 -0
  143. package/src/superlocalmemory/storage/migrations/M043_quarantine_display_summaries.py +488 -0
  144. package/src/superlocalmemory/storage/migrations/M044_play_carries_its_own_evidence.py +127 -0
  145. package/src/superlocalmemory/storage/migrations/M045_fact_outcome_score.py +158 -0
  146. package/src/superlocalmemory/storage/migrations/M046_prospective_memory_has_its_own_name.py +620 -0
  147. package/src/superlocalmemory/storage/migrations/M047_fisher_vectors_are_stored_like_every_other_vector.py +306 -0
  148. package/src/superlocalmemory/storage/migrations/M048_upcoming_holds_only_what_is_upcoming.py +207 -0
  149. package/src/superlocalmemory/storage/migrations/M049_a_schema_version_marker_is_one_row.py +201 -0
  150. package/src/superlocalmemory/storage/migrations.py +18 -2
  151. package/src/superlocalmemory/storage/models.py +40 -1
  152. package/src/superlocalmemory/storage/projection_outbox.py +346 -0
  153. package/src/superlocalmemory/storage/retention_policy.py +860 -0
  154. package/src/superlocalmemory/storage/schema.py +110 -1
  155. package/src/superlocalmemory/storage/write_coordinator.py +19 -2
  156. package/src/superlocalmemory/summaries/base.py +1 -1
  157. package/src/superlocalmemory/summaries/non_answer.py +223 -0
  158. package/src/superlocalmemory/trust/scorer.py +43 -1
  159. package/src/superlocalmemory/ui/index.html +10 -19
  160. package/src/superlocalmemory/ui/js/event-delegation.js +12 -1
  161. package/src/superlocalmemory/ui/js/od-health.js +28 -6
  162. package/src/superlocalmemory/ui/js/od-memories.js +209 -1
  163. package/src/superlocalmemory/ui/js/od-ops-health.js +1 -1
  164. package/src/superlocalmemory/ui/js/od-settings.js +87 -1
  165. package/src/superlocalmemory/ui/js/recall-lab.js +78 -3
@@ -50,7 +50,7 @@ recall(
50
50
  query="authentication strategy decision",
51
51
  limit=20, # default 20; reduce to 5 for quick pre-task checks
52
52
  session_id="<sid>", # pass the session_id returned by session_init
53
- fast=False, # default False; True enables faster, reduced-channel retrieval
53
+ fast=None, # leave unset; see "Fast mode" below for what it controls
54
54
  )
55
55
  ```
56
56
 
@@ -65,12 +65,12 @@ Real response shape (`--json` equivalent):
65
65
  "score": 0.87,
66
66
  "confidence": 0.91,
67
67
  "trust_score": 0.84,
68
- "fact_type": "decision",
68
+ "fact_type": "semantic",
69
69
  "channel_scores": {
70
70
  "semantic": 0.88,
71
- "lexical": 0.61,
71
+ "bm25": 0.61,
72
72
  "temporal": 0.72,
73
- "structural": 0.55
73
+ "hopfield": 0.55
74
74
  }
75
75
  }
76
76
  ],
@@ -78,32 +78,92 @@ Real response shape (`--json` equivalent):
78
78
  "query_type": "semantic",
79
79
  "channel_weights": {
80
80
  "semantic": 0.4,
81
- "lexical": 0.2,
81
+ "bm25": 0.2,
82
82
  "temporal": 0.2,
83
- "structural": 0.2
83
+ "hopfield": 0.2
84
84
  },
85
+ "channel_status": {
86
+ "semantic": "ok",
87
+ "bm25": "ok",
88
+ "temporal": "empty",
89
+ "hopfield": "ok",
90
+ "spreading_activation": "no_candidates",
91
+ "entity_graph": "no_embedding",
92
+ "profile": "disabled"
93
+ },
94
+ "incomplete_channels": [],
85
95
  "retrieval_time_ms": 134,
86
96
  "no_confident_match": false
87
97
  }
88
98
  ```
89
99
 
100
+ **Read `channel_status` before concluding that nothing is stored.** It reports
101
+ what each retrieval channel did on this query. `channel_weights` says how much
102
+ each channel counts; `channel_status` says whether it ran at all.
103
+
104
+ | status | meaning |
105
+ |---|---|
106
+ | `ok` | the channel ran and contributed candidates |
107
+ | `empty` | it ran and there was genuinely nothing to return |
108
+ | `no_candidates` | it ran but nothing survived fusion |
109
+ | `error` | it raised — **its results are missing from this answer** |
110
+ | `timeout` | it exceeded its guard — **results missing** |
111
+ | `no_embedding` | the query could not be embedded, so it could not run |
112
+ | `disabled` | switched off by configuration |
113
+ | `not_configured` | the backing service is not set up |
114
+
115
+ `semantic`, `bm25`, `temporal`, `hopfield` and `spreading_activation` each
116
+ search and return their own candidates. `profile` is a shortcut that runs before
117
+ them and can answer directly. `entity_graph` produces nothing of its own — it
118
+ re-scores what the others found, by how well each result connects to the
119
+ entities in your question, which is why it reports `no_candidates` when the
120
+ rest come back empty.
121
+
122
+ `empty`, `no_candidates`, `disabled` and `not_configured` are normal. `error`,
123
+ `timeout` and `no_embedding` mean the answer is **incomplete, not negative** —
124
+ say so to the user rather than reporting "no memories found". `incomplete_channels`
125
+ carries the same warning as a plain list.
126
+
90
127
  **Refine on low confidence.** `recall` returns confidence signals with every result. If `no_confident_match` is `true` (or `answer_confidence` is low / `abstained` is `true`), do NOT invent a memory — rewrite the query into 1–3 more specific sub-queries (split multi-hop questions; try entity names, synonyms, or broader phrasing) and call `recall` again before concluding nothing was found. A confident match → use it directly. SLM returns fast local results (~1–2s, no server-side LLM round on the hot path) and lets you, the calling model, drive this refinement.
91
128
 
92
129
  ### 2. Passing session_id
93
130
 
94
- Pass the `session_id` returned by `session_init`. It threads engagement signals
95
- through to the ranker so each recall contributes to improving retrieval for
96
- your project over time. Omitting it degrades the learning loop — recall works
97
- correctly, but feedback is not attributed to the session.
131
+ Pass the `session_id` returned by `session_init`, on **every** recall in that
132
+ session. It does two things.
133
+
134
+ 1. **It carries the conversation forward.** Each recall offers its five
135
+ best-ranked results to a small per-session working set of seven slots. A
136
+ memory that keeps coming back is reinforced rather than duplicated, and the
137
+ least-activated slot is the one evicted, so something referenced across
138
+ several turns is hard to lose. Later recalls in the same session rank the
139
+ held memories higher, and turn three is not as cold as turn one. The bias is
140
+ deliberately small — it nudges the order, it never overrides an exact match.
141
+ 2. **It attributes engagement to the session**, so a later `report_outcome`
142
+ can close the loop on the right recall.
143
+
144
+ Omitting it costs both: recall still returns correct results, but every turn
145
+ starts cold and no feedback is attributable.
146
+
147
+ **Use the real id, not a made-up one.** An id beginning `http:`, `mcp:`, `cli:`
148
+ or `probe:` is treated as a synthetic per-request label, not a conversation, and
149
+ is excluded from the working set — inventing one per call would otherwise fill
150
+ the registry and evict genuine conversations.
98
151
 
99
152
  ### 3. Fast mode
100
153
 
101
- Use `fast=True` for pre-tool-call checks where sub-second response matters.
102
- This enables a faster, reduced-channel mode. Core semantic and keyword channels
103
- always run; additional graph and contextual channels are skipped.
154
+ `fast` controls **one** thing: whether the server runs its own internal LLM
155
+ reformulation round. It does **not** disable any retrieval channel every
156
+ channel and the reranker run either way. There are four always: meaning,
157
+ keyword, entity graph and time. Spreading activation and Hopfield register as a
158
+ fifth and sixth when their prerequisites are present, so a store sees up to six.
159
+
160
+ Leave it unset. Unset resolves to "skip the internal round", because you are the
161
+ reasoner: you refine the query yourself using the confidence signals above, and
162
+ you do it better than a local model would. Pass `fast=False` only when SLM is
163
+ deployed with no capable client in front of it.
104
164
 
105
165
  ```
106
- recall(query="rate limiting approach", limit=5, session_id="<sid>", fast=True)
166
+ recall(query="rate limiting approach", limit=5, session_id="<sid>")
107
167
  ```
108
168
 
109
169
  ### 4. Keyword fallback via search
@@ -138,6 +198,33 @@ once you have the `fact_id` for full content.
138
198
 
139
199
  ---
140
200
 
201
+ ### 7. Close the loop — say which memories helped
202
+
203
+ Retrieval ranks a memory partly on whether it has actually been useful before.
204
+ That evidence only exists if you supply it.
205
+
206
+ ```
207
+ report_outcome(
208
+ memory_ids="f8a2bc91,c31d0f77", # the ids you actually used
209
+ outcome="success", # "success" | "failure" | "partial"
210
+ context="used the JWT expiry decision to write the refresh handler",
211
+ )
212
+ ```
213
+
214
+ Call it when a recall visibly changed what you did: you applied the decision,
215
+ followed the convention, or avoided the gotcha. Report `failure` when a
216
+ confidently-returned memory turned out to be wrong or stale — a negative signal
217
+ is worth as much as a positive one, and it is the only way a stale memory stops
218
+ being promoted.
219
+
220
+ Report only ids you genuinely used. Reporting every returned id marks the
221
+ irrelevant ones useful and trains the ranker toward noise.
222
+
223
+ `report_feedback(fact_id, feedback, query)` is the finer-grained form for a
224
+ single fact and the query that surfaced it.
225
+
226
+ ---
227
+
141
228
  ## How multi-channel retrieval works
142
229
 
143
230
  `recall` runs multiple candidate producers in parallel — semantic vector similarity,
@@ -236,4 +323,4 @@ before recalling, then switch back. See `slm-profile` for workspace switching.
236
323
 
237
324
  ---
238
325
 
239
- *SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later*
326
+ *SuperLocalMemory v4.1.0 · Qualixar · AGPL-3.0-or-later*
@@ -103,8 +103,10 @@ remember(
103
103
  project: str = "", # project scope, e.g. "superlocalmemory"
104
104
  importance: int = 5,# 1–10; see scale below
105
105
  session_id: str = "",# from session_init; attributes the write to this session
106
+ session_date: str = "",# when the memory is ABOUT, if not today
106
107
  scope: str = None, # v3.6.15 multi-scope: "personal" (default) | "shared" | "global"
107
108
  shared_with: str = "",# comma-separated profile_ids for scope="shared"
109
+ idempotency_key: str = "",# replaying the same key will not store a second copy
108
110
  )
109
111
  ```
110
112
 
@@ -121,14 +123,44 @@ remember(
121
123
 
122
124
  Use 7–10 only for facts that would cause real damage if forgotten.
123
125
 
124
- ### 4. One fact per call
126
+ ### 4. Date a memory to when it happened
127
+
128
+ `session_date` says **when the memory is about**, as distinct from when you
129
+ wrote it. Omit it and the memory is dated today.
130
+
131
+ ```
132
+ remember(
133
+ content="The outage on the payments queue was caused by a stale DNS entry",
134
+ tags="incident,payments,postmortem",
135
+ project="platform",
136
+ session_date="2026-08-14", # YYYY-MM-DD, or a full ISO 8601 timestamp
137
+ session_id="<sid>",
138
+ )
139
+ ```
140
+
141
+ Use it whenever you are writing something down after the fact — a postmortem, a
142
+ decision taken in a meeting last week, a migration that ran on a known date.
143
+ Time-filtered recall (`window="7d"`, `window="2026-07-01..2026-07-31"`) reads
144
+ event time, so a mis-dated memory is one a time-scoped question cannot find.
145
+
146
+ `session_date` does not change what **kind** of memory it is. A memory that
147
+ describes something planned — "the migration is scheduled for Tuesday", "the
148
+ certificate expires on 2026-09-01" — is stored as a **prospective** memory, and
149
+ recall reports it as `"fact_type": "prospective"`. That is inferred from how the
150
+ content reads, not from the date you pass. Stores written before 4.1.0 spelled
151
+ this type `"temporal"`; that value still reads correctly and needs nothing from
152
+ you.
153
+
154
+ ---
155
+
156
+ ### 5. One fact per call
125
157
 
126
158
  Store one atomic fact per `remember` call. Do not concatenate multiple unrelated
127
159
  points into a single content string — they will be hard to update individually
128
160
  and harder to retrieve cleanly. If you have three separate decisions, make three
129
161
  calls.
130
162
 
131
- ### 5. Always set tags and project
163
+ ### 6. Always set tags and project
132
164
 
133
165
  Untagged, unscoped facts are harder to retrieve and harder to manage. Minimum:
134
166
  set `tags` to one or two relevant terms and `project` to the repo/product name.
@@ -238,4 +270,4 @@ different workspace, use `switch_profile` first. See `slm-profile`.
238
270
 
239
271
  ---
240
272
 
241
- *SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later*
273
+ *SuperLocalMemory v4.1.0 · 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.9 · Qualixar · AGPL-3.0-or-later*
176
+ *SuperLocalMemory v4.1.0 · Qualixar · AGPL-3.0-or-later*
@@ -121,7 +121,14 @@ remember(content="...", session_id=session_id, tags="auth,decision", project="my
121
121
  ```
122
122
 
123
123
  This attribution is what allows the ranker to learn which recalls led to useful
124
- outcomes for this project.
124
+ outcomes for this project. It also gives the session a small working set, so
125
+ successive recalls in one conversation build on what the earlier ones surfaced
126
+ instead of each starting cold.
127
+
128
+ **Do not synthesise a session id.** An id beginning `http:`, `mcp:`, `cli:` or
129
+ `probe:` is read as a synthetic per-request label rather than a conversation and
130
+ is deliberately excluded from that working set. Use the one `session_init`
131
+ returned, unchanged, for the whole session.
125
132
 
126
133
  ---
127
134
 
@@ -177,6 +184,23 @@ never attributed to a project or agent. Over many sessions this compounds:
177
184
  projects where lifecycle is respected have measurably better retrieval quality
178
185
  than projects where session_init is skipped.
179
186
 
187
+ Within a single session it compounds faster. The session's working set holds the
188
+ memories its recalls have already surfaced, and later recalls rank those higher,
189
+ so a long conversation converges on the material it is actually about.
190
+
191
+ ### Closing the loop explicitly
192
+
193
+ Engagement signals say a memory was *shown*. `report_outcome` says it was
194
+ *right*:
195
+
196
+ ```
197
+ report_outcome(memory_ids="<ids you actually used>", outcome="success")
198
+ ```
199
+
200
+ Send it when a recalled memory changed what you did, and send `failure` when a
201
+ confidently-returned memory turned out to be wrong — that is the only signal
202
+ that stops a stale memory from being promoted. See the `slm-recall` skill.
203
+
180
204
  ---
181
205
 
182
206
  ## CLI fallback (when MCP is unavailable)
@@ -198,7 +222,9 @@ slm doctor [--json] # preflight check including daemon and embedding worker
198
222
  | Mistake | Consequence | Fix |
199
223
  |---------|-------------|-----|
200
224
  | Calling `session_init` twice in one session | Two session IDs; signals split across them | Call once; store the returned ID |
201
- | Omitting `session_id` from `recall` / `remember` | No learning attribution | Always pass the stored `session_id` |
225
+ | Omitting `session_id` from `recall` / `remember` | No learning attribution, and every turn starts cold | Always pass the stored `session_id` |
226
+ | Inventing a `session_id` such as `mcp:agent` or `http:1234` | Read as synthetic, excluded from the working set | Use the id `session_init` returned |
227
+ | Never reporting an outcome | Ranking cannot tell a useful memory from a merely returned one | `report_outcome` after a recall that changed what you did |
202
228
  | Never calling `close_session` | Temporal summaries not written | Call at end of each meaningful work unit |
203
229
  | Calling `close_session` without a `session_id` when no prior writes exist | Returns error "No session_id found" | Pass the explicit `session_id` from `session_init` |
204
230
 
@@ -227,4 +253,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
227
253
 
228
254
  ---
229
255
 
230
- *SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later*
256
+ *SuperLocalMemory v4.1.0 · 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.9 · Qualixar · AGPL-3.0-or-later
166
+ SuperLocalMemory v4.1.0 · Qualixar · AGPL-3.0-or-later
@@ -80,13 +80,17 @@ When the SLM MCP server is unavailable, use these CLI equivalents:
80
80
 
81
81
  ---
82
82
 
83
- ## Tool reference (core profile — 16 tools)
84
-
85
- > The MCP config ships `SLM_MCP_PROFILE=code` and `SLM_AGENT_ID=claude_code` (31 tools):
86
- > the 16 core memory and correction-review tools below **plus** 6 code-graph tools
87
- > (`build_code_graph`, `get_blast_radius`, `query_graph`, `semantic_search_code`,
88
- > `get_review_context`, `detect_changes`) and `switch_profile`.
89
- > Use `full` (49 tools) to add mesh coordination. Use `power` (61 tools) for governance
83
+ ## Tool reference (core profile — 18 tools)
84
+
85
+ > The MCP config ships `SLM_MCP_PROFILE=code` and `SLM_AGENT_ID=claude_code` (34 tools):
86
+ > the 18 core tools below **plus** 6 code-graph tools (`build_code_graph`,
87
+ > `get_blast_radius`, `query_graph`, `semantic_search_code`, `get_review_context`,
88
+ > `detect_changes`), 5 portable-evidence tools (`get_brain_evidence_status`,
89
+ > `record_agent_experience`, `record_cognitive_turn`, `finalize_cognitive_turn`,
90
+ > `observe_bounded_loop_evidence`), 3 bounded-loop tools (`slm_loop_run`,
91
+ > `slm_loop_history`, `slm_loop_show`) and 2 usefulness reports (`report_outcome`,
92
+ > `report_feedback`).
93
+ > Use `full` (50 tools) to add mesh coordination. Use `power` (62 tools) for governance
90
94
  > and audit tools. See slm-profile for profile switching.
91
95
 
92
96
  | Tool | Signature (key params) | Notes |
@@ -105,6 +109,10 @@ When the SLM MCP server is unavailable, use these CLI equivalents:
105
109
  | `slm_cache_set` | `key, value, ttl_seconds=86400` | KV cache set |
106
110
  | `slm_cache_get` | `key` | KV cache get; returns hit, value |
107
111
  | `slm_optimize_stats` | `()` | Returns compress_runs, tokens_saved_compress, cache_kv_hits |
112
+ | `review_correction` | `case_id, action, expected_version, event_valid_until=None` | Apply, reject or roll back a review-gated correction |
113
+ | `list_corrections` | `limit=100` | Correction cases awaiting review, active profile |
114
+ | `get_memory_summary` | `kind="day", target=""` | Readable summary of a day, project or session |
115
+ | `switch_profile` | `profile_id` | Change the active memory profile; every later call scopes to it |
108
116
 
109
117
  ## Skills
110
118
 
@@ -129,4 +137,4 @@ When the SLM MCP server is unavailable, use these CLI equivalents:
129
137
  - **slm-optimize-advisor** — context compression and KV cache
130
138
  - **slm-governance-advisor** — scope/role compliance, retention policies, GDPR
131
139
 
132
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
140
+ SuperLocalMemory v4.1.0 · 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.4 · Qualixar · AGPL-3.0-or-later
148
+ SuperLocalMemory v4.1.0 · 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.4 · Qualixar · AGPL-3.0-or-later
150
+ SuperLocalMemory v4.1.0 · 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.4 · Qualixar · AGPL-3.0-or-later*
248
+ *SuperLocalMemory v4.1.0 · Qualixar · AGPL-3.0-or-later*
@@ -312,4 +312,4 @@ profile. See `slm-profile` for the full profile switching workflow.
312
312
 
313
313
  ---
314
314
 
315
- SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later
315
+ SuperLocalMemory v4.1.0 · Qualixar · AGPL-3.0-or-later
@@ -2,7 +2,7 @@
2
2
  name: slm-loop
3
3
  description: Run gate-verified bounded loops with SuperLocalMemory as the durable ledger. Use when a task has a checkable acceptance condition (tests, schema, lint, reconciliation) and you must iterate until an INDEPENDENT gate passes — never stopping just because the agent believes it is done. `slm loop demo` runs a keyless convergence demo; `slm loop history` and `slm loop show <run_id>` inspect past runs whose every lap is persisted as queryable SLM memory (tag `loop:<name>`). Terminal statuses are DONE / HALT / PAUSE / KILLED / ERROR — report them exactly, never converting HALT/PAUSE/ERROR into success.
4
4
  when_to_use: "run a bounded loop, gate-verified task, iterate until tests pass, verify against an independent gate, don't trust the agent's own done claim, slm loop, convergence loop, loop until green, loop ledger, resume a loop"
5
- allowed-tools: Bash, slm_recall
5
+ allowed-tools: Bash, recall
6
6
  ---
7
7
 
8
8
  # slm-loop — Bounded, gate-verified agent loops
@@ -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.4 · Qualixar · AGPL-3.0-or-later
99
+ SuperLocalMemory v4.1.0 · Qualixar · AGPL-3.0-or-later
@@ -279,4 +279,4 @@ mesh availability.
279
279
 
280
280
  ---
281
281
 
282
- *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
282
+ *SuperLocalMemory v4.1.0 · Qualixar · AGPL-3.0-or-later*
@@ -30,10 +30,10 @@ project, a client engagement, a production vs staging environment.
30
30
 
31
31
  | Profile | Tools | When to use |
32
32
  |---------|-------|-------------|
33
- | `core` | 14 tools — remember, recall, search, session, optimize | Minimal footprint, no code tools |
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 |
33
+ | `core` | 18 tools — remember, recall, search, session, optimize | Minimal footprint, no code tools |
34
+ | `code` | 34 tools — core + portable Brain evidence + code graph + profile switching + bounded loops | Default for IDE/coding agents |
35
+ | `full` | 50 tools — code + all memory ops + mesh + bounded loops | Multi-session, team workflows |
36
+ | `power` | 62 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.
@@ -146,4 +146,4 @@ Name them differently in your MCP config (e.g. `superlocalmemory-personal` and
146
146
 
147
147
  ---
148
148
 
149
- *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
149
+ *SuperLocalMemory v4.1.0 · Qualixar · AGPL-3.0-or-later*
@@ -50,7 +50,7 @@ recall(
50
50
  query="authentication strategy decision",
51
51
  limit=20, # default 20; reduce to 5 for quick pre-task checks
52
52
  session_id="<sid>", # pass the session_id returned by session_init
53
- fast=False, # default False; True enables faster, reduced-channel retrieval
53
+ fast=None, # leave unset; see "Fast mode" below for what it controls
54
54
  )
55
55
  ```
56
56
 
@@ -65,12 +65,12 @@ Real response shape (`--json` equivalent):
65
65
  "score": 0.87,
66
66
  "confidence": 0.91,
67
67
  "trust_score": 0.84,
68
- "fact_type": "decision",
68
+ "fact_type": "semantic",
69
69
  "channel_scores": {
70
70
  "semantic": 0.88,
71
- "lexical": 0.61,
71
+ "bm25": 0.61,
72
72
  "temporal": 0.72,
73
- "structural": 0.55
73
+ "hopfield": 0.55
74
74
  }
75
75
  }
76
76
  ],
@@ -78,32 +78,92 @@ Real response shape (`--json` equivalent):
78
78
  "query_type": "semantic",
79
79
  "channel_weights": {
80
80
  "semantic": 0.4,
81
- "lexical": 0.2,
81
+ "bm25": 0.2,
82
82
  "temporal": 0.2,
83
- "structural": 0.2
83
+ "hopfield": 0.2
84
84
  },
85
+ "channel_status": {
86
+ "semantic": "ok",
87
+ "bm25": "ok",
88
+ "temporal": "empty",
89
+ "hopfield": "ok",
90
+ "spreading_activation": "no_candidates",
91
+ "entity_graph": "no_embedding",
92
+ "profile": "disabled"
93
+ },
94
+ "incomplete_channels": [],
85
95
  "retrieval_time_ms": 134,
86
96
  "no_confident_match": false
87
97
  }
88
98
  ```
89
99
 
100
+ **Read `channel_status` before concluding that nothing is stored.** It reports
101
+ what each retrieval channel did on this query. `channel_weights` says how much
102
+ each channel counts; `channel_status` says whether it ran at all.
103
+
104
+ | status | meaning |
105
+ |---|---|
106
+ | `ok` | the channel ran and contributed candidates |
107
+ | `empty` | it ran and there was genuinely nothing to return |
108
+ | `no_candidates` | it ran but nothing survived fusion |
109
+ | `error` | it raised — **its results are missing from this answer** |
110
+ | `timeout` | it exceeded its guard — **results missing** |
111
+ | `no_embedding` | the query could not be embedded, so it could not run |
112
+ | `disabled` | switched off by configuration |
113
+ | `not_configured` | the backing service is not set up |
114
+
115
+ `semantic`, `bm25`, `temporal`, `hopfield` and `spreading_activation` each
116
+ search and return their own candidates. `profile` is a shortcut that runs before
117
+ them and can answer directly. `entity_graph` produces nothing of its own — it
118
+ re-scores what the others found, by how well each result connects to the
119
+ entities in your question, which is why it reports `no_candidates` when the
120
+ rest come back empty.
121
+
122
+ `empty`, `no_candidates`, `disabled` and `not_configured` are normal. `error`,
123
+ `timeout` and `no_embedding` mean the answer is **incomplete, not negative** —
124
+ say so to the user rather than reporting "no memories found". `incomplete_channels`
125
+ carries the same warning as a plain list.
126
+
90
127
  **Refine on low confidence.** `recall` returns confidence signals with every result. If `no_confident_match` is `true` (or `answer_confidence` is low / `abstained` is `true`), do NOT invent a memory — rewrite the query into 1–3 more specific sub-queries (split multi-hop questions; try entity names, synonyms, or broader phrasing) and call `recall` again before concluding nothing was found. A confident match → use it directly. SLM returns fast local results (~1–2s, no server-side LLM round on the hot path) and lets you, the calling model, drive this refinement.
91
128
 
92
129
  ### 2. Passing session_id
93
130
 
94
- Pass the `session_id` returned by `session_init`. It threads engagement signals
95
- through to the ranker so each recall contributes to improving retrieval for
96
- your project over time. Omitting it degrades the learning loop — recall works
97
- correctly, but feedback is not attributed to the session.
131
+ Pass the `session_id` returned by `session_init`, on **every** recall in that
132
+ session. It does two things.
133
+
134
+ 1. **It carries the conversation forward.** Each recall offers its five
135
+ best-ranked results to a small per-session working set of seven slots. A
136
+ memory that keeps coming back is reinforced rather than duplicated, and the
137
+ least-activated slot is the one evicted, so something referenced across
138
+ several turns is hard to lose. Later recalls in the same session rank the
139
+ held memories higher, and turn three is not as cold as turn one. The bias is
140
+ deliberately small — it nudges the order, it never overrides an exact match.
141
+ 2. **It attributes engagement to the session**, so a later `report_outcome`
142
+ can close the loop on the right recall.
143
+
144
+ Omitting it costs both: recall still returns correct results, but every turn
145
+ starts cold and no feedback is attributable.
146
+
147
+ **Use the real id, not a made-up one.** An id beginning `http:`, `mcp:`, `cli:`
148
+ or `probe:` is treated as a synthetic per-request label, not a conversation, and
149
+ is excluded from the working set — inventing one per call would otherwise fill
150
+ the registry and evict genuine conversations.
98
151
 
99
152
  ### 3. Fast mode
100
153
 
101
- Use `fast=True` for pre-tool-call checks where sub-second response matters.
102
- This enables a faster, reduced-channel mode. Core semantic and keyword channels
103
- always run; additional graph and contextual channels are skipped.
154
+ `fast` controls **one** thing: whether the server runs its own internal LLM
155
+ reformulation round. It does **not** disable any retrieval channel every
156
+ channel and the reranker run either way. There are four always: meaning,
157
+ keyword, entity graph and time. Spreading activation and Hopfield register as a
158
+ fifth and sixth when their prerequisites are present, so a store sees up to six.
159
+
160
+ Leave it unset. Unset resolves to "skip the internal round", because you are the
161
+ reasoner: you refine the query yourself using the confidence signals above, and
162
+ you do it better than a local model would. Pass `fast=False` only when SLM is
163
+ deployed with no capable client in front of it.
104
164
 
105
165
  ```
106
- recall(query="rate limiting approach", limit=5, session_id="<sid>", fast=True)
166
+ recall(query="rate limiting approach", limit=5, session_id="<sid>")
107
167
  ```
108
168
 
109
169
  ### 4. Keyword fallback via search
@@ -138,6 +198,33 @@ once you have the `fact_id` for full content.
138
198
 
139
199
  ---
140
200
 
201
+ ### 7. Close the loop — say which memories helped
202
+
203
+ Retrieval ranks a memory partly on whether it has actually been useful before.
204
+ That evidence only exists if you supply it.
205
+
206
+ ```
207
+ report_outcome(
208
+ memory_ids="f8a2bc91,c31d0f77", # the ids you actually used
209
+ outcome="success", # "success" | "failure" | "partial"
210
+ context="used the JWT expiry decision to write the refresh handler",
211
+ )
212
+ ```
213
+
214
+ Call it when a recall visibly changed what you did: you applied the decision,
215
+ followed the convention, or avoided the gotcha. Report `failure` when a
216
+ confidently-returned memory turned out to be wrong or stale — a negative signal
217
+ is worth as much as a positive one, and it is the only way a stale memory stops
218
+ being promoted.
219
+
220
+ Report only ids you genuinely used. Reporting every returned id marks the
221
+ irrelevant ones useful and trains the ranker toward noise.
222
+
223
+ `report_feedback(fact_id, feedback, query)` is the finer-grained form for a
224
+ single fact and the query that surfaced it.
225
+
226
+ ---
227
+
141
228
  ## How multi-channel retrieval works
142
229
 
143
230
  `recall` runs multiple candidate producers in parallel — semantic vector similarity,
@@ -236,4 +323,4 @@ before recalling, then switch back. See `slm-profile` for workspace switching.
236
323
 
237
324
  ---
238
325
 
239
- *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
326
+ *SuperLocalMemory v4.1.0 · Qualixar · AGPL-3.0-or-later*
@@ -103,8 +103,10 @@ remember(
103
103
  project: str = "", # project scope, e.g. "superlocalmemory"
104
104
  importance: int = 5,# 1–10; see scale below
105
105
  session_id: str = "",# from session_init; attributes the write to this session
106
+ session_date: str = "",# when the memory is ABOUT, if not today
106
107
  scope: str = None, # v3.6.15 multi-scope: "personal" (default) | "shared" | "global"
107
108
  shared_with: str = "",# comma-separated profile_ids for scope="shared"
109
+ idempotency_key: str = "",# replaying the same key will not store a second copy
108
110
  )
109
111
  ```
110
112
 
@@ -121,14 +123,44 @@ remember(
121
123
 
122
124
  Use 7–10 only for facts that would cause real damage if forgotten.
123
125
 
124
- ### 4. One fact per call
126
+ ### 4. Date a memory to when it happened
127
+
128
+ `session_date` says **when the memory is about**, as distinct from when you
129
+ wrote it. Omit it and the memory is dated today.
130
+
131
+ ```
132
+ remember(
133
+ content="The outage on the payments queue was caused by a stale DNS entry",
134
+ tags="incident,payments,postmortem",
135
+ project="platform",
136
+ session_date="2026-08-14", # YYYY-MM-DD, or a full ISO 8601 timestamp
137
+ session_id="<sid>",
138
+ )
139
+ ```
140
+
141
+ Use it whenever you are writing something down after the fact — a postmortem, a
142
+ decision taken in a meeting last week, a migration that ran on a known date.
143
+ Time-filtered recall (`window="7d"`, `window="2026-07-01..2026-07-31"`) reads
144
+ event time, so a mis-dated memory is one a time-scoped question cannot find.
145
+
146
+ `session_date` does not change what **kind** of memory it is. A memory that
147
+ describes something planned — "the migration is scheduled for Tuesday", "the
148
+ certificate expires on 2026-09-01" — is stored as a **prospective** memory, and
149
+ recall reports it as `"fact_type": "prospective"`. That is inferred from how the
150
+ content reads, not from the date you pass. Stores written before 4.1.0 spelled
151
+ this type `"temporal"`; that value still reads correctly and needs nothing from
152
+ you.
153
+
154
+ ---
155
+
156
+ ### 5. One fact per call
125
157
 
126
158
  Store one atomic fact per `remember` call. Do not concatenate multiple unrelated
127
159
  points into a single content string — they will be hard to update individually
128
160
  and harder to retrieve cleanly. If you have three separate decisions, make three
129
161
  calls.
130
162
 
131
- ### 5. Always set tags and project
163
+ ### 6. Always set tags and project
132
164
 
133
165
  Untagged, unscoped facts are harder to retrieve and harder to manage. Minimum:
134
166
  set `tags` to one or two relevant terms and `project` to the repo/product name.
@@ -238,4 +270,4 @@ different workspace, use `switch_profile` first. See `slm-profile`.
238
270
 
239
271
  ---
240
272
 
241
- *SuperLocalMemory v4.0.4 · Qualixar · AGPL-3.0-or-later*
273
+ *SuperLocalMemory v4.1.0 · Qualixar · AGPL-3.0-or-later*