superlocalmemory 3.8.0 → 3.8.1
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/CHANGELOG.md +60 -0
- package/README.md +32 -120
- package/package.json +9 -2
- package/plugin/.claude-plugin/plugin.json +1 -2
- 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-graph/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-session/SKILL.md +1 -1
- package/plugin-src/skills/slm-status/SKILL.md +1 -1
- package/pyproject.toml +2 -1
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/commands.py +134 -7
- package/src/superlocalmemory/cli/daemon.py +7 -0
- package/src/superlocalmemory/cli/loop_cmd.py +2 -7
- package/src/superlocalmemory/cli/main.py +10 -4
- package/src/superlocalmemory/cli/version_banner.py +17 -3
- package/src/superlocalmemory/core/backend_orchestrator.py +18 -16
- package/src/superlocalmemory/core/embedding_worker.py +4 -5
- package/src/superlocalmemory/core/embeddings.py +119 -45
- package/src/superlocalmemory/core/engine.py +24 -21
- package/src/superlocalmemory/core/engine_ingestion.py +332 -45
- package/src/superlocalmemory/core/ingestion_command.py +134 -25
- package/src/superlocalmemory/core/injection.py +12 -7
- package/src/superlocalmemory/core/maintenance_scheduler.py +17 -7
- package/src/superlocalmemory/core/recall_pipeline.py +5 -3
- package/src/superlocalmemory/core/store_pipeline.py +153 -20
- package/src/superlocalmemory/hooks/hook_handlers.py +6 -1
- package/src/superlocalmemory/hooks/portable_kit.py +34 -2
- package/src/superlocalmemory/learning/model_rollback.py +3 -0
- package/src/superlocalmemory/learning/ranker_retrain_online.py +2 -0
- package/src/superlocalmemory/learning/reward.py +50 -0
- package/src/superlocalmemory/learning/source_quality.py +523 -1
- package/src/superlocalmemory/loops/ledger.py +25 -5
- package/src/superlocalmemory/mcp/server.py +11 -30
- package/src/superlocalmemory/mcp/tools_core.py +4 -3
- package/src/superlocalmemory/mcp/tools_learning.py +2 -2
- package/src/superlocalmemory/retrieval/engine.py +53 -21
- package/src/superlocalmemory/retrieval/reranker.py +3 -4
- package/src/superlocalmemory/server/config_file.py +90 -0
- package/src/superlocalmemory/server/origin.py +50 -0
- package/src/superlocalmemory/server/routes/backup.py +293 -70
- package/src/superlocalmemory/server/routes/behavioral.py +336 -59
- package/src/superlocalmemory/server/routes/brain.py +57 -16
- package/src/superlocalmemory/server/routes/config_api.py +84 -82
- package/src/superlocalmemory/server/routes/entity.py +100 -23
- package/src/superlocalmemory/server/routes/evolution.py +103 -100
- package/src/superlocalmemory/server/routes/learning.py +276 -105
- package/src/superlocalmemory/server/routes/learning_telemetry.py +153 -0
- package/src/superlocalmemory/server/routes/mesh.py +121 -32
- package/src/superlocalmemory/server/routes/ratelimit.py +33 -25
- package/src/superlocalmemory/server/routes/stats.py +93 -155
- package/src/superlocalmemory/server/routes/token.py +3 -13
- package/src/superlocalmemory/server/routes/v3_api.py +64 -20
- package/src/superlocalmemory/server/unified_daemon.py +467 -40
- package/src/superlocalmemory/storage/migration_runner.py +79 -1
- package/src/superlocalmemory/storage/migrations/M010_evolution_config.py +5 -0
- package/src/superlocalmemory/storage/migrations/M028_fact_entity_associations.py +270 -0
- package/src/superlocalmemory/storage/migrations/M029_behavioral_history_indexes.py +137 -0
- package/src/superlocalmemory/storage/migrations/M030_entity_explorer_indexes.py +93 -0
- package/src/superlocalmemory/storage/migrations/__init__.py +4 -0
- package/src/superlocalmemory/storage/schema.py +49 -1
- package/src/superlocalmemory/storage/schema_v32.py +2 -0
- package/src/superlocalmemory/storage/schema_v347.py +4 -0
- package/src/superlocalmemory/ui/index.html +3 -6
- package/src/superlocalmemory/ui/js/core.js +52 -9
- package/src/superlocalmemory/ui/js/dashboard.js +169 -82
- package/src/superlocalmemory/ui/js/od-backup.js +156 -65
- package/src/superlocalmemory/ui/js/od-brain.js +88 -51
- package/src/superlocalmemory/ui/js/od-entities.js +22 -22
- package/src/superlocalmemory/ui/js/od-graph.js +11 -4
- package/src/superlocalmemory/ui/js/od-memories.js +47 -5
- package/src/superlocalmemory/ui/js/od-mesh.js +23 -9
- package/src/superlocalmemory/ui/js/od-settings.js +113 -59
- package/src/superlocalmemory/ui/js/od-shell.js +249 -33
- package/src/superlocalmemory/ui/js/od-skills.js +44 -17
- package/src/superlocalmemory/ui/js/settings.js +15 -1
- package/plugin-src/.mcp.json +0 -12
- package/plugin-src/agents/slm-governance-advisor.md +0 -80
- package/plugin-src/agents/slm-loop-runner.md +0 -71
- package/plugin-src/agents/slm-memory-advisor.md +0 -49
- package/plugin-src/agents/slm-optimize-advisor.md +0 -44
- package/plugin-src/commands/slm-loop.md +0 -31
- package/plugin-src/hooks/.gitkeep +0 -0
- package/plugin-src/hooks/hooks.json +0 -102
- package/plugin-src/manifest.json +0 -30
- package/plugin-src/requirements.txt +0 -1
- package/plugin-src/rules/CLAUDE.md.fragment +0 -44
- package/plugin-src/scripts/ensure-venv.bat +0 -122
- package/plugin-src/scripts/ensure-venv.sh +0 -105
- package/plugin-src/scripts/slm-launch +0 -62
- package/plugin-src/scripts/slm-launch.bat +0 -23
- package/plugin-src/settings.json +0 -25
- package/plugin-src/skills/slm-governance/SKILL.md +0 -248
- package/plugin-src/skills/slm-loop/SKILL.md +0 -99
- package/plugin-src/skills/slm-mesh/SKILL.md +0 -282
- package/plugin-src/skills/slm-profile/SKILL.md +0 -148
- package/plugin-src/skills/slm-scope/SKILL.md +0 -176
|
@@ -240,11 +240,12 @@ def connect_ide(
|
|
|
240
240
|
here: bool = False,
|
|
241
241
|
profile: str | None = None,
|
|
242
242
|
agents_md_source: Callable[[], str] | None = None,
|
|
243
|
+
dry_run: bool = False,
|
|
243
244
|
) -> dict[str, Any]:
|
|
244
245
|
"""Wire SLM into the target IDE config via merge-not-clobber.
|
|
245
246
|
|
|
246
247
|
Returns a result dict:
|
|
247
|
-
{ide, mcp_config: wrote|merged|unchanged|skipped|error,
|
|
248
|
+
{ide, mcp_config: wrote|merged|unchanged|would_write|skipped|error,
|
|
248
249
|
mcp_path, agents_md: wrote|skipped(...)|unchanged|error,
|
|
249
250
|
servers_preserved: int, error: str|None}
|
|
250
251
|
"""
|
|
@@ -265,6 +266,13 @@ def connect_ide(
|
|
|
265
266
|
)
|
|
266
267
|
return result
|
|
267
268
|
|
|
269
|
+
if ide_id == "vscode-copilot" and not here:
|
|
270
|
+
result["error"] = (
|
|
271
|
+
"VS Code / Copilot integration is project-scoped. "
|
|
272
|
+
"Run `slm connect vscode-copilot --here` from the project root."
|
|
273
|
+
)
|
|
274
|
+
return result
|
|
275
|
+
|
|
268
276
|
# Step 1a — claude-code short-circuit (AC6)
|
|
269
277
|
if desc.fmt == "":
|
|
270
278
|
print(CLAUDE_CODE_PLUGIN_POINTER)
|
|
@@ -325,7 +333,28 @@ def connect_ide(
|
|
|
325
333
|
result["servers_preserved"] = max(0, pre_count - (0 if pre_slm is None else 1))
|
|
326
334
|
result["mcp_config"] = mcp_status
|
|
327
335
|
|
|
328
|
-
# Step 6 — atomic write
|
|
336
|
+
# Step 6 — atomic write. A dry run deliberately exercises the merge and
|
|
337
|
+
# packaged-asset lookup path without touching a user-owned IDE config.
|
|
338
|
+
if dry_run:
|
|
339
|
+
result["mcp_config"] = "would_write"
|
|
340
|
+
if desc.agents_md_path is None:
|
|
341
|
+
result["agents_md"] = "skipped(unsupported)"
|
|
342
|
+
elif agents_md_source is None:
|
|
343
|
+
result["agents_md"] = "skipped(no-source)"
|
|
344
|
+
else:
|
|
345
|
+
try:
|
|
346
|
+
source_content = agents_md_source()
|
|
347
|
+
except Exception as exc:
|
|
348
|
+
result["agents_md"] = "error(source-unavailable)"
|
|
349
|
+
result["error"] = f"AGENTS.md asset lookup failed: {exc}"
|
|
350
|
+
return result
|
|
351
|
+
if not isinstance(source_content, str) or not source_content.strip():
|
|
352
|
+
result["agents_md"] = "error(empty-source)"
|
|
353
|
+
result["error"] = "AGENTS.md asset is empty"
|
|
354
|
+
return result
|
|
355
|
+
result["agents_md"] = "would_write"
|
|
356
|
+
return result
|
|
357
|
+
|
|
329
358
|
try:
|
|
330
359
|
_atomic_write(config_path, data, desc.fmt)
|
|
331
360
|
except Exception as exc:
|
|
@@ -528,6 +557,9 @@ def _handle_agents_md(
|
|
|
528
557
|
except Exception as exc:
|
|
529
558
|
logger.warning("agents_md_source() failed: %s — skipping AGENTS.md write", exc)
|
|
530
559
|
return "skipped(source-error)"
|
|
560
|
+
if not isinstance(source_content, str) or not source_content.strip():
|
|
561
|
+
logger.warning("agents_md_source() returned no content — skipping AGENTS.md write")
|
|
562
|
+
return "skipped(no-source-content)"
|
|
531
563
|
|
|
532
564
|
# Read existing content
|
|
533
565
|
existing = ""
|
|
@@ -34,6 +34,8 @@ import sqlite3
|
|
|
34
34
|
from datetime import datetime, timedelta, timezone
|
|
35
35
|
from typing import Final
|
|
36
36
|
|
|
37
|
+
from superlocalmemory.learning.model_cache import invalidate as invalidate_model_cache
|
|
38
|
+
|
|
37
39
|
logger = logging.getLogger(__name__)
|
|
38
40
|
|
|
39
41
|
|
|
@@ -235,6 +237,7 @@ class ModelRollback:
|
|
|
235
237
|
)
|
|
236
238
|
return False
|
|
237
239
|
|
|
240
|
+
invalidate_model_cache(self._profile_id)
|
|
238
241
|
logger.warning(
|
|
239
242
|
"AUTO-ROLLBACK profile=%s reason=%s observations=%d "
|
|
240
243
|
"baseline_ndcg=%.4f current_ndcg=%.4f",
|
|
@@ -33,6 +33,7 @@ from datetime import datetime, timezone
|
|
|
33
33
|
from pathlib import Path
|
|
34
34
|
from typing import Final
|
|
35
35
|
|
|
36
|
+
from superlocalmemory.learning.model_cache import invalidate as invalidate_model_cache
|
|
36
37
|
from superlocalmemory.learning.ranker_common import (
|
|
37
38
|
_build_training_matrix,
|
|
38
39
|
_compute_eval_metrics,
|
|
@@ -389,6 +390,7 @@ def _promote_candidate(
|
|
|
389
390
|
(json.dumps(meta), candidate_id),
|
|
390
391
|
)
|
|
391
392
|
conn.commit()
|
|
393
|
+
invalidate_model_cache(profile_id)
|
|
392
394
|
return True
|
|
393
395
|
except sqlite3.Error as exc:
|
|
394
396
|
conn.rollback()
|
|
@@ -73,6 +73,24 @@ _DWELL_MAX_MS: Final[int] = 3_600_000 # 1 h
|
|
|
73
73
|
_BUSY_TIMEOUT_MS: Final[int] = 50
|
|
74
74
|
|
|
75
75
|
|
|
76
|
+
def _feed_source_quality(
|
|
77
|
+
memory_db_path: Path,
|
|
78
|
+
rewards: list[tuple[str, str, list[str], float]],
|
|
79
|
+
) -> None:
|
|
80
|
+
"""Fail-soft provenance bridge kept outside the reward transaction."""
|
|
81
|
+
try:
|
|
82
|
+
from superlocalmemory.learning.source_quality import (
|
|
83
|
+
update_source_quality_for_reward_batch,
|
|
84
|
+
)
|
|
85
|
+
update_source_quality_for_reward_batch(
|
|
86
|
+
memory_db_path=memory_db_path,
|
|
87
|
+
learning_db_path=memory_db_path.parent / "learning.db",
|
|
88
|
+
rewards=rewards,
|
|
89
|
+
)
|
|
90
|
+
except Exception as exc: # noqa: BLE001 - reward finalization must survive
|
|
91
|
+
logger.debug("source-quality reward feed failed (non-fatal): %s", exc)
|
|
92
|
+
|
|
93
|
+
|
|
76
94
|
# ---------------------------------------------------------------------------
|
|
77
95
|
# Signal contract — names match the manifest A.1 label formula.
|
|
78
96
|
# ---------------------------------------------------------------------------
|
|
@@ -604,6 +622,7 @@ class EngagementRewardModel:
|
|
|
604
622
|
# lock-scope — SQLite rows are tied to the connection.
|
|
605
623
|
_pid = pending["profile_id"]
|
|
606
624
|
_qid = pending["recall_query_id"]
|
|
625
|
+
_facts_json = str(pending["fact_ids_json"] or "[]")
|
|
607
626
|
except sqlite3.Error as exc:
|
|
608
627
|
logger.debug("finalize_outcome SQLite error: %s", exc)
|
|
609
628
|
return _FALLBACK_REWARD
|
|
@@ -633,6 +652,21 @@ class EngagementRewardModel:
|
|
|
633
652
|
except Exception as exc: # noqa: BLE001 — defence in depth
|
|
634
653
|
logger.debug("feed_recall_settled failed (non-fatal): %s", exc)
|
|
635
654
|
|
|
655
|
+
try:
|
|
656
|
+
_fact_ids = json.loads(_facts_json)
|
|
657
|
+
if not isinstance(_fact_ids, list):
|
|
658
|
+
_fact_ids = []
|
|
659
|
+
except (TypeError, ValueError, json.JSONDecodeError):
|
|
660
|
+
_fact_ids = []
|
|
661
|
+
_feed_source_quality(
|
|
662
|
+
self._db,
|
|
663
|
+
[(
|
|
664
|
+
str(_pid),
|
|
665
|
+
str(outcome_id),
|
|
666
|
+
[str(fid) for fid in _fact_ids if fid],
|
|
667
|
+
float(reward),
|
|
668
|
+
)],
|
|
669
|
+
)
|
|
636
670
|
return reward
|
|
637
671
|
|
|
638
672
|
# ------------------------------------------------------------------
|
|
@@ -749,6 +783,22 @@ class EngagementRewardModel:
|
|
|
749
783
|
except sqlite3.Error:
|
|
750
784
|
conn.execute("ROLLBACK")
|
|
751
785
|
raise
|
|
786
|
+
reward_sources = []
|
|
787
|
+
for item in i_chunk:
|
|
788
|
+
try:
|
|
789
|
+
fact_ids = json.loads(str(item[2] or "[]"))
|
|
790
|
+
except (TypeError, ValueError, json.JSONDecodeError):
|
|
791
|
+
fact_ids = []
|
|
792
|
+
reward_sources.append((
|
|
793
|
+
str(item[1]),
|
|
794
|
+
str(item[0]),
|
|
795
|
+
(
|
|
796
|
+
[str(fid) for fid in fact_ids if fid]
|
|
797
|
+
if isinstance(fact_ids, list) else []
|
|
798
|
+
),
|
|
799
|
+
float(item[4]),
|
|
800
|
+
))
|
|
801
|
+
_feed_source_quality(self._db, reward_sources)
|
|
752
802
|
except sqlite3.Error as exc: # pragma: no cover — defensive
|
|
753
803
|
logger.debug("reap_stale SQLite error: %s", exc)
|
|
754
804
|
return written
|