superlocalmemory 4.1.3 → 4.1.5

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 (47) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +64 -0
  3. package/README.md +3 -3
  4. package/package.json +1 -1
  5. package/plugin/.claude-plugin/plugin.json +1 -1
  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 +1 -1
  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 +1 -1
  17. package/plugin/skills/slm-mesh/SKILL.md +2 -2
  18. package/plugin/skills/slm-profile/SKILL.md +1 -1
  19. package/plugin/skills/slm-recall/SKILL.md +1 -1
  20. package/plugin/skills/slm-remember/SKILL.md +1 -1
  21. package/plugin/skills/slm-scope/SKILL.md +1 -1
  22. package/plugin/skills/slm-session/SKILL.md +2 -2
  23. package/plugin/skills/slm-status/SKILL.md +1 -1
  24. package/plugin-src/rules/AGENTS.md +1 -1
  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 +1 -1
  30. package/plugin-src/skills/slm-mesh/SKILL.md +2 -2
  31. package/plugin-src/skills/slm-profile/SKILL.md +1 -1
  32. package/plugin-src/skills/slm-recall/SKILL.md +1 -1
  33. package/plugin-src/skills/slm-remember/SKILL.md +1 -1
  34. package/plugin-src/skills/slm-scope/SKILL.md +1 -1
  35. package/plugin-src/skills/slm-session/SKILL.md +2 -2
  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 +8 -2
  40. package/src/superlocalmemory/cli/daemon.py +69 -6
  41. package/src/superlocalmemory/cli/diagnostics_cmd.py +74 -1
  42. package/src/superlocalmemory/cli/main.py +12 -0
  43. package/src/superlocalmemory/core/remember_runtime.py +12 -1
  44. package/src/superlocalmemory/reliability/__init__.py +45 -0
  45. package/src/superlocalmemory/reliability/join_liveness.py +301 -0
  46. package/src/superlocalmemory/reliability/prior_distance.py +243 -0
  47. package/src/superlocalmemory/server/routes/backup.py +1 -1
@@ -21,7 +21,7 @@
21
21
  "license": "AGPL-3.0-or-later",
22
22
  "name": "superlocalmemory",
23
23
  "source": "./plugin",
24
- "version": "4.1.3"
24
+ "version": "4.1.5"
25
25
  },
26
26
  {
27
27
  "author": {
@@ -39,7 +39,7 @@
39
39
  "license": "AGPL-3.0-or-later",
40
40
  "name": "superlocalmemory-codex",
41
41
  "source": "./codex-plugin",
42
- "version": "4.1.3"
42
+ "version": "4.1.5"
43
43
  }
44
44
  ]
45
45
  }
package/CHANGELOG.md CHANGED
@@ -5,6 +5,70 @@ 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.1.5] — Ask whether a feature is working, not whether it is present
9
+
10
+ ### Added
11
+ - **`slm diagnostics reliability`** — two read-only checks that answer a question
12
+ neither a code search nor a call trace can: is a wired feature actually taking
13
+ effect on *your* store?
14
+ - The first asks whether the retrieval learner's posteriors have moved away from
15
+ where they started. A learner that records activity and never shifts its
16
+ distribution looks healthy from every counter it keeps; this reports it plainly.
17
+ - The second asks whether a feature gated on a table or column can run here at
18
+ all, and when it cannot, searches the rest of the store for the data it needs
19
+ and tells you where it is, how much of what is needed it covers, and whether
20
+ connecting it would need a backfill.
21
+ Both are read-only, run in a second, and print what they found in plain language.
22
+ Run it on your own store: it may tell you something you assumed was on is not.
23
+
24
+ ### Fixed
25
+ - **A refused write said only "temporarily unavailable."** Three unrelated causes
26
+ produced that one message — a busy write journal, another process holding the
27
+ writer, and a rejected stale write. They call for different responses, and one of
28
+ them is not a fault at all. The cause is now named, so a log line says which
29
+ happened instead of leaving you to guess.
30
+ - **The Codex and Antigravity plugins still advertised 4.1.3 after 4.1.4 shipped.**
31
+ Their manifests are generated from a shared source and were not regenerated during
32
+ that release, so both surfaces reported a version they were not. All four editor
33
+ surfaces are regenerated and checked against the package version on every release.
34
+ - **The benchmark instructions could not be followed.** The documented command named
35
+ a directory that does not exist and an interpreter path pointing outside the
36
+ project, and the environment it suggested carries an older release — so anyone
37
+ following it measured the wrong code. Corrected, with a check that the interpreter
38
+ you choose has the version you mean to test.
39
+
40
+ ## [4.1.4] — A busy daemon is not a dead daemon
41
+
42
+ ### Fixed
43
+ - **`slm serve stop` and `slm restart` could report a live daemon as "not
44
+ running."** `daemon_request()` preflights every call with `GET /health`,
45
+ which needs the daemon's single-threaded event loop to be free to answer.
46
+ `/maintenance/run` and `/consolidate/cognitive` run multi-second (sometimes
47
+ multi-minute) work directly inline in their handlers with no thread
48
+ offload — unlike `/recall`, fixed for exactly this in v3.4.52 — so every
49
+ request blocks for as long as they run, health included. Reproduced live: a
50
+ genuine `/maintenance/run` call held the loop long enough that 15 of 15
51
+ health polls in that window timed out at the 2s cap while the process never
52
+ stopped listening. `stop_daemon()` now checks process liveness directly
53
+ (PID plus a clock-independent start token, no HTTP) before deciding whether
54
+ a stop is owed, and sends the stop itself without the health preflight —
55
+ the daemon still authenticates the request by its capability header on
56
+ arrival, so no ownership guarantee is lost, only the stall-prone round
57
+ trip. `cmd_restart`'s Step 1 uses the same liveness check, so a busy-but-alive
58
+ daemon is no longer skipped as "already stopped," which previously cascaded
59
+ into Step 3 refusing to start a second daemon and failing the whole restart.
60
+ The underlying blocking handlers are unchanged by this fix — recall and
61
+ remember calls can still be delayed by a running maintenance pass, just not
62
+ misreported as a dead daemon.
63
+
64
+ ### Added
65
+ - **A one-command Antigravity plugin install.** `agy`'s plugin installer only
66
+ accepts a local directory — there is no marketplace or remote-install
67
+ path — so Claude Code and Codex had a real one-liner and Antigravity did
68
+ not. `scripts/install-antigravity-plugin.sh` closes the gap: a shallow,
69
+ sparse checkout of `antigravity-plugin/` into a local cache, then
70
+ `agy plugin install` against it. Idempotent; safe to re-run to update.
71
+
8
72
  ## [4.1.3] — Your install, your tools, your memories
9
73
 
10
74
  ### Fixed
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  </picture>
6
6
  </p>
7
7
 
8
- <h1 align="center">SuperLocalMemory V4.1.3</h1>
8
+ <h1 align="center">SuperLocalMemory V4.1.5</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.1.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.1.5</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.1.3-Current_Release-2ea44f?style=for-the-badge&logo=checkmarx&logoColor=white" alt="v4.1.3 — Current Release"/></a>
35
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/v4.1.5-Current_Release-2ea44f?style=for-the-badge&logo=checkmarx&logoColor=white" alt="v4.1.5 — 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>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superlocalmemory",
3
- "version": "4.1.3",
3
+ "version": "4.1.5",
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.1.3"
18
+ "version": "4.1.5"
19
19
  }
package/plugin/CLAUDE.md CHANGED
@@ -1,4 +1,4 @@
1
- <!-- BEGIN SuperLocalMemory v4.1.3 -->
1
+ <!-- BEGIN SuperLocalMemory v4.1.5 -->
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.1.3 -->
42
+ <!-- END SuperLocalMemory v4.1.5 -->
43
43
 
44
- SuperLocalMemory v4.1.3 · Qualixar · AGPL-3.0-or-later
44
+ SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
80
+ SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
71
+ SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
49
+ SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
44
+ SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later
@@ -1 +1 @@
1
- superlocalmemory==4.1.3
1
+ superlocalmemory==4.1.5
@@ -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.1.3 · Qualixar · AGPL-3.0-or-later
148
+ SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
150
+ SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later*
248
+ *SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
315
+ SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
99
+ SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later
@@ -97,7 +97,7 @@ mesh_send(to=target_id, message="I'm starting work on auth/handler.py — please
97
97
  mesh_send(to="broadcast", message="Deploying to staging in 5 minutes")
98
98
 
99
99
  # Message all sessions working in the same project
100
- mesh_send(to="project:/Users/me/myproject", message="Tests are green on main")
100
+ mesh_send(to="project:~/myproject", message="Tests are green on main")
101
101
  ```
102
102
 
103
103
  **4 KB message cap.** For large payloads (diffs, file contents), write to a file
@@ -279,4 +279,4 @@ mesh availability.
279
279
 
280
280
  ---
281
281
 
282
- *SuperLocalMemory v4.1.3 · Qualixar · AGPL-3.0-or-later*
282
+ *SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later*
@@ -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.1.3 · Qualixar · AGPL-3.0-or-later*
149
+ *SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later*
@@ -323,4 +323,4 @@ before recalling, then switch back. See `slm-profile` for workspace switching.
323
323
 
324
324
  ---
325
325
 
326
- *SuperLocalMemory v4.1.3 · Qualixar · AGPL-3.0-or-later*
326
+ *SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later*
@@ -270,4 +270,4 @@ different workspace, use `switch_profile` first. See `slm-profile`.
270
270
 
271
271
  ---
272
272
 
273
- *SuperLocalMemory v4.1.3 · Qualixar · AGPL-3.0-or-later*
273
+ *SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later*
176
+ *SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later*
@@ -52,7 +52,7 @@ recalls or remembers in that session.
52
52
 
53
53
  ```
54
54
  session_init(
55
- project_path: str = "", # working directory path, e.g. "/Users/me/projects/foo"
55
+ project_path: str = "", # working directory path, e.g. "~/projects/foo"
56
56
  query: str = "", # topic override; if omitted, derived from project_path
57
57
  max_results: int = 10, # max memories to return (default: 10)
58
58
  max_age_days: int = 30, # suppress memories older than N days unless score >= 0.7
@@ -253,4 +253,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
253
253
 
254
254
  ---
255
255
 
256
- *SuperLocalMemory v4.1.3 · Qualixar · AGPL-3.0-or-later*
256
+ *SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
166
+ SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later
@@ -137,4 +137,4 @@ When the SLM MCP server is unavailable, use these CLI equivalents:
137
137
  - **slm-optimize-advisor** — context compression and KV cache
138
138
  - **slm-governance-advisor** — scope/role compliance, retention policies, GDPR
139
139
 
140
- SuperLocalMemory v4.1.3 · Qualixar · AGPL-3.0-or-later
140
+ SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
148
+ SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
150
+ SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later*
248
+ *SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
315
+ SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
99
+ SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later
@@ -97,7 +97,7 @@ mesh_send(to=target_id, message="I'm starting work on auth/handler.py — please
97
97
  mesh_send(to="broadcast", message="Deploying to staging in 5 minutes")
98
98
 
99
99
  # Message all sessions working in the same project
100
- mesh_send(to="project:/Users/me/myproject", message="Tests are green on main")
100
+ mesh_send(to="project:~/myproject", message="Tests are green on main")
101
101
  ```
102
102
 
103
103
  **4 KB message cap.** For large payloads (diffs, file contents), write to a file
@@ -279,4 +279,4 @@ mesh availability.
279
279
 
280
280
  ---
281
281
 
282
- *SuperLocalMemory v4.1.3 · Qualixar · AGPL-3.0-or-later*
282
+ *SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later*
@@ -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.1.3 · Qualixar · AGPL-3.0-or-later*
149
+ *SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later*
@@ -323,4 +323,4 @@ before recalling, then switch back. See `slm-profile` for workspace switching.
323
323
 
324
324
  ---
325
325
 
326
- *SuperLocalMemory v4.1.3 · Qualixar · AGPL-3.0-or-later*
326
+ *SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later*
@@ -270,4 +270,4 @@ different workspace, use `switch_profile` first. See `slm-profile`.
270
270
 
271
271
  ---
272
272
 
273
- *SuperLocalMemory v4.1.3 · Qualixar · AGPL-3.0-or-later*
273
+ *SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later*
176
+ *SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later*
@@ -52,7 +52,7 @@ recalls or remembers in that session.
52
52
 
53
53
  ```
54
54
  session_init(
55
- project_path: str = "", # working directory path, e.g. "/Users/me/projects/foo"
55
+ project_path: str = "", # working directory path, e.g. "~/projects/foo"
56
56
  query: str = "", # topic override; if omitted, derived from project_path
57
57
  max_results: int = 10, # max memories to return (default: 10)
58
58
  max_age_days: int = 30, # suppress memories older than N days unless score >= 0.7
@@ -253,4 +253,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
253
253
 
254
254
  ---
255
255
 
256
- *SuperLocalMemory v4.1.3 · Qualixar · AGPL-3.0-or-later*
256
+ *SuperLocalMemory v4.1.5 · 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.1.3 · Qualixar · AGPL-3.0-or-later
166
+ SuperLocalMemory v4.1.5 · Qualixar · AGPL-3.0-or-later
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "superlocalmemory"
3
- version = "4.1.3"
3
+ version = "4.1.5"
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.1.3"
35
+ __version__ = "4.1.5"
36
36
 
37
37
  _REQUIRED_VERSIONS = {
38
38
  "sentence_transformers": "5.3.0",
@@ -754,13 +754,19 @@ def cmd_restart(args: Namespace) -> None:
754
754
  # Step 1: stop only the descriptor-owned daemon. Its graceful shutdown
755
755
  # owns worker termination; process-name-wide scans are forbidden.
756
756
  from superlocalmemory.cli.daemon import (
757
- is_daemon_running,
757
+ owned_daemon_process_alive,
758
758
  read_descriptor,
759
759
  stop_daemon,
760
760
  wait_for_owned_daemon_shutdown,
761
761
  )
762
762
 
763
- was_running = is_daemon_running()
763
+ # v4.1.4: process liveness, not HTTP health-readiness, decides whether a
764
+ # stop is owed. is_daemon_running() additionally requires a live /health
765
+ # response, which a daemon busy inside /maintenance/run or
766
+ # /consolidate/cognitive cannot give for the duration of that call even
767
+ # though it is fully alive and holding the port — see stop_daemon()'s
768
+ # docstring for the reproduced failure this fixes.
769
+ was_running = owned_daemon_process_alive()
764
770
  owned_descriptor = read_descriptor() if was_running else None
765
771
  stopped = stop_daemon() if was_running else True
766
772
  if stopped and was_running:
@@ -254,6 +254,29 @@ def is_daemon_running() -> bool:
254
254
  return legacy is not None
255
255
 
256
256
 
257
+ def owned_daemon_process_alive() -> bool:
258
+ """Return whether an owned daemon *process* is alive, HTTP aside.
259
+
260
+ ``is_daemon_running()`` additionally requires a live, matching
261
+ ``/health`` response, which conflates two different questions: "is there
262
+ a process I need to stop" and "is it ready to serve requests right now."
263
+ A daemon whose event loop is synchronously blocked by a long-running
264
+ handler (``/maintenance/run``, ``/consolidate/cognitive``) cannot answer
265
+ the second for the duration of that call, but the answer to the first is
266
+ still yes. Callers that only need to decide whether a stop is owed
267
+ (``slm restart`` Step 1) should use this instead, so a transiently busy
268
+ daemon is not skipped as "already stopped" while it keeps running and
269
+ holding the port — which then made Step 3 refuse to start a second
270
+ daemon on the still-occupied port and fail the whole restart.
271
+ """
272
+ descriptor = read_descriptor()
273
+ if descriptor is not None:
274
+ return _descriptor_process_is_alive(descriptor)
275
+ if descriptor_path().exists():
276
+ return False
277
+ return _verified_legacy_health() is not None
278
+
279
+
257
280
  def _fetch_health(port: int) -> dict | None:
258
281
  """Fetch loopback health without following cross-namespace discovery."""
259
282
  try:
@@ -349,8 +372,25 @@ def daemon_request(
349
372
  timeout_seconds: float = 30.0,
350
373
  expected_descriptor=_EXPECTED_DESCRIPTOR_UNSET,
351
374
  expected_legacy: dict | None = None,
375
+ verify_health: bool = True,
352
376
  ) -> dict | None:
353
- """Send a request only after validating the owned daemon identity."""
377
+ """Send a request only after validating the owned daemon identity.
378
+
379
+ ``verify_health`` — when True (the default), a ``GET /health`` preflight
380
+ must succeed and match the descriptor before the real request is sent.
381
+ That preflight needs the daemon's event loop to be free to answer HTTP,
382
+ which is a *readiness* question, not a *liveness* one: a daemon whose
383
+ loop is synchronously blocked by a long-running handler (e.g.
384
+ ``/maintenance/run``, ``/consolidate/cognitive``, neither of which is
385
+ offloaded to a thread the way ``/recall`` was for exactly this reason in
386
+ v3.4.52) cannot answer /health for the duration of that call even though
387
+ the process is fully alive and listening. Callers that have already
388
+ proven process-level ownership some other way (e.g. ``stop_daemon()`` via
389
+ ``_descriptor_process_is_alive``) should pass ``verify_health=False`` so a
390
+ busy-but-alive daemon does not get misreported as not running. Only
391
+ meaningful for the descriptor path — the legacy bridge has no capability
392
+ header and still needs health to identify its target.
393
+ """
354
394
  legacy = None
355
395
  if expected_legacy is not None:
356
396
  # Legacy daemons have no capability header. Bind the compatibility
@@ -377,11 +417,12 @@ def daemon_request(
377
417
  capability: str | None = None
378
418
  target_instance: str | None = None
379
419
  if descriptor is not None:
380
- health = _fetch_health(descriptor.port)
381
- if health is None or not descriptor_matches_health(descriptor, health):
382
- return None
383
- if method.upper() == "GET" and path == "/health":
384
- return health
420
+ if verify_health:
421
+ health = _fetch_health(descriptor.port)
422
+ if health is None or not descriptor_matches_health(descriptor, health):
423
+ return None
424
+ if method.upper() == "GET" and path == "/health":
425
+ return health
385
426
  port = descriptor.port
386
427
  capability = descriptor.capability
387
428
  target_instance = descriptor.instance_id
@@ -796,16 +837,38 @@ def stop_daemon() -> bool:
796
837
  HTTP capability; the daemon itself terminates its child process tree.
797
838
  Success means the owned process exited and released its listener, not just
798
839
  that the asynchronous stop request was accepted.
840
+
841
+ A busy daemon is not a dead daemon. ``daemon_request()`` normally
842
+ preflights every call with ``GET /health`` before sending it, but that
843
+ preflight needs the daemon's single-threaded event loop to be free to
844
+ answer HTTP. ``/maintenance/run`` and ``/consolidate/cognitive`` run
845
+ multi-second (sometimes multi-minute) synchronous work directly inline in
846
+ their handlers with no thread offload, which blocks *every* request on
847
+ that loop, health included, for as long as they run. Reproduced live: a
848
+ genuine ``/maintenance/run`` call held the loop long enough that 15/15
849
+ health polls during the window timed out at exactly the 2s cap while
850
+ ``ps``/``lsof`` proved the process never stopped listening — which is
851
+ exactly the "Daemon was not running" false report this fixes. Process
852
+ liveness (PID + clock-independent start token, proven below via
853
+ ``_descriptor_process_is_alive``) is the fact that actually matters for
854
+ "should I try to stop this," so it is checked directly and the mutating
855
+ ``/stop`` POST is sent with ``verify_health=False`` once that is proven —
856
+ the daemon still authenticates the request by its capability header on
857
+ arrival, so this loses no ownership guarantee, only the redundant,
858
+ stall-prone preflight round trip.
799
859
  """
800
860
  descriptor = read_descriptor()
801
861
  legacy = _verified_legacy_health() if descriptor is None else None
802
862
  if descriptor is None and legacy is None:
803
863
  return False
804
864
  if descriptor is not None:
865
+ if not _descriptor_process_is_alive(descriptor):
866
+ return False
805
867
  response = daemon_request(
806
868
  "POST",
807
869
  "/stop",
808
870
  expected_descriptor=descriptor,
871
+ verify_health=False,
809
872
  )
810
873
  else:
811
874
  if legacy is None: