superlocalmemory 4.1.3 → 4.1.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.
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +32 -0
- package/README.md +3 -3
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/CLAUDE.md +3 -3
- package/plugin/agents/slm-governance-advisor.md +1 -1
- package/plugin/agents/slm-loop-runner.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-governance/SKILL.md +1 -1
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-loop/SKILL.md +1 -1
- package/plugin/skills/slm-mesh/SKILL.md +1 -1
- package/plugin/skills/slm-profile/SKILL.md +1 -1
- package/plugin/skills/slm-recall/SKILL.md +1 -1
- package/plugin/skills/slm-remember/SKILL.md +1 -1
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/plugin-src/skills/slm-cache/SKILL.md +1 -1
- package/plugin-src/skills/slm-compress/SKILL.md +1 -1
- package/plugin-src/skills/slm-governance/SKILL.md +1 -1
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-loop/SKILL.md +1 -1
- package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
- package/plugin-src/skills/slm-profile/SKILL.md +1 -1
- package/plugin-src/skills/slm-recall/SKILL.md +1 -1
- package/plugin-src/skills/slm-remember/SKILL.md +1 -1
- package/plugin-src/skills/slm-scope/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/skills/slm-status/SKILL.md +1 -1
- package/pyproject.toml +1 -1
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/commands.py +8 -2
- package/src/superlocalmemory/cli/daemon.py +69 -6
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"license": "AGPL-3.0-or-later",
|
|
22
22
|
"name": "superlocalmemory",
|
|
23
23
|
"source": "./plugin",
|
|
24
|
-
"version": "4.1.
|
|
24
|
+
"version": "4.1.4"
|
|
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.
|
|
42
|
+
"version": "4.1.4"
|
|
43
43
|
}
|
|
44
44
|
]
|
|
45
45
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ 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.4] — A busy daemon is not a dead daemon
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **`slm serve stop` and `slm restart` could report a live daemon as "not
|
|
12
|
+
running."** `daemon_request()` preflights every call with `GET /health`,
|
|
13
|
+
which needs the daemon's single-threaded event loop to be free to answer.
|
|
14
|
+
`/maintenance/run` and `/consolidate/cognitive` run multi-second (sometimes
|
|
15
|
+
multi-minute) work directly inline in their handlers with no thread
|
|
16
|
+
offload — unlike `/recall`, fixed for exactly this in v3.4.52 — so every
|
|
17
|
+
request blocks for as long as they run, health included. Reproduced live: a
|
|
18
|
+
genuine `/maintenance/run` call held the loop long enough that 15 of 15
|
|
19
|
+
health polls in that window timed out at the 2s cap while the process never
|
|
20
|
+
stopped listening. `stop_daemon()` now checks process liveness directly
|
|
21
|
+
(PID plus a clock-independent start token, no HTTP) before deciding whether
|
|
22
|
+
a stop is owed, and sends the stop itself without the health preflight —
|
|
23
|
+
the daemon still authenticates the request by its capability header on
|
|
24
|
+
arrival, so no ownership guarantee is lost, only the stall-prone round
|
|
25
|
+
trip. `cmd_restart`'s Step 1 uses the same liveness check, so a busy-but-alive
|
|
26
|
+
daemon is no longer skipped as "already stopped," which previously cascaded
|
|
27
|
+
into Step 3 refusing to start a second daemon and failing the whole restart.
|
|
28
|
+
The underlying blocking handlers are unchanged by this fix — recall and
|
|
29
|
+
remember calls can still be delayed by a running maintenance pass, just not
|
|
30
|
+
misreported as a dead daemon.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- **A one-command Antigravity plugin install.** `agy`'s plugin installer only
|
|
34
|
+
accepts a local directory — there is no marketplace or remote-install
|
|
35
|
+
path — so Claude Code and Codex had a real one-liner and Antigravity did
|
|
36
|
+
not. `scripts/install-antigravity-plugin.sh` closes the gap: a shallow,
|
|
37
|
+
sparse checkout of `antigravity-plugin/` into a local cache, then
|
|
38
|
+
`agy plugin install` against it. Idempotent; safe to re-run to update.
|
|
39
|
+
|
|
8
40
|
## [4.1.3] — Your install, your tools, your memories
|
|
9
41
|
|
|
10
42
|
### Fixed
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
</picture>
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
|
-
<h1 align="center">SuperLocalMemory V4.1.
|
|
8
|
+
<h1 align="center">SuperLocalMemory V4.1.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.1.
|
|
30
|
+
<p align="center"><code>v4.1.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 & 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> · MCP: add <code>slm_compress</code> to your config · 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.
|
|
35
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/v4.1.4-Current_Release-2ea44f?style=for-the-badge&logo=checkmarx&logoColor=white" alt="v4.1.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>
|
package/package.json
CHANGED
package/plugin/CLAUDE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- BEGIN SuperLocalMemory v4.1.
|
|
1
|
+
<!-- BEGIN SuperLocalMemory v4.1.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.1.
|
|
42
|
+
<!-- END SuperLocalMemory v4.1.4 -->
|
|
43
43
|
|
|
44
|
-
SuperLocalMemory v4.1.
|
|
44
|
+
SuperLocalMemory v4.1.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.1.
|
|
80
|
+
SuperLocalMemory v4.1.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.1.
|
|
49
|
+
SuperLocalMemory v4.1.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.1.
|
|
44
|
+
SuperLocalMemory v4.1.4 · Qualixar · AGPL-3.0-or-later
|
package/plugin/requirements.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
superlocalmemory==4.1.
|
|
1
|
+
superlocalmemory==4.1.4
|
|
@@ -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.
|
|
140
|
+
SuperLocalMemory v4.1.4 · Qualixar · AGPL-3.0-or-later
|
package/pyproject.toml
CHANGED
|
@@ -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.
|
|
35
|
+
__version__ = "4.1.4"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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:
|