superlocalmemory 3.7.7 → 3.8.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/ATTRIBUTION.md +1 -3
- package/CHANGELOG.md +85 -0
- package/README.md +199 -29
- package/package.json +4 -2
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/CLAUDE.md +8 -8
- package/plugin/agents/slm-governance-advisor.md +80 -0
- package/plugin/agents/slm-loop-runner.md +71 -0
- package/plugin/agents/slm-memory-advisor.md +10 -5
- package/plugin/agents/slm-optimize-advisor.md +9 -3
- package/plugin/commands/slm-loop.md +31 -0
- package/plugin/hooks/hooks.json +79 -0
- package/plugin/requirements.txt +1 -1
- package/plugin/scripts/slm-launch +46 -7
- package/plugin/settings.json +9 -0
- package/plugin/skills/slm-cache/SKILL.md +9 -1
- package/plugin/skills/slm-compress/SKILL.md +8 -1
- package/plugin/skills/slm-governance/SKILL.md +248 -0
- package/plugin/skills/slm-graph/SKILL.md +17 -3
- package/plugin/skills/slm-loop/SKILL.md +99 -0
- package/plugin/skills/slm-mesh/SKILL.md +282 -0
- package/plugin/skills/slm-profile/SKILL.md +148 -0
- package/plugin/skills/slm-recall/SKILL.md +46 -10
- package/plugin/skills/slm-remember/SKILL.md +48 -1
- package/plugin/skills/slm-scope/SKILL.md +176 -0
- package/plugin/skills/slm-session/SKILL.md +24 -1
- package/plugin/skills/slm-status/SKILL.md +18 -1
- package/plugin-src/agents/slm-governance-advisor.md +80 -0
- package/plugin-src/agents/slm-loop-runner.md +71 -0
- package/plugin-src/agents/slm-memory-advisor.md +10 -5
- package/plugin-src/agents/slm-optimize-advisor.md +9 -3
- package/plugin-src/commands/slm-loop.md +31 -0
- package/plugin-src/hooks/hooks.json +79 -0
- package/plugin-src/manifest.json +7 -2
- package/plugin-src/requirements.txt +1 -1
- package/plugin-src/rules/AGENTS.md +57 -18
- package/plugin-src/rules/CLAUDE.md.fragment +8 -8
- package/plugin-src/scripts/slm-launch +46 -7
- package/plugin-src/settings.json +9 -0
- package/plugin-src/skills/slm-cache/SKILL.md +9 -1
- package/plugin-src/skills/slm-compress/SKILL.md +8 -1
- package/plugin-src/skills/slm-governance/SKILL.md +248 -0
- package/plugin-src/skills/slm-graph/SKILL.md +17 -3
- package/plugin-src/skills/slm-loop/SKILL.md +99 -0
- package/plugin-src/skills/slm-mesh/SKILL.md +282 -0
- package/plugin-src/skills/slm-profile/SKILL.md +148 -0
- package/plugin-src/skills/slm-recall/SKILL.md +46 -10
- package/plugin-src/skills/slm-remember/SKILL.md +48 -1
- package/plugin-src/skills/slm-scope/SKILL.md +176 -0
- package/plugin-src/skills/slm-session/SKILL.md +24 -1
- package/plugin-src/skills/slm-status/SKILL.md +18 -1
- package/pyproject.toml +1 -2
- package/scripts/postinstall/validation.js +2 -0
- package/scripts/postinstall-interactive.js +74 -2
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/access/__init__.py +3 -0
- package/src/superlocalmemory/access/rbac.py +477 -0
- package/src/superlocalmemory/cli/commands.py +96 -12
- package/src/superlocalmemory/cli/compress_cmd.py +17 -7
- package/src/superlocalmemory/cli/loop_cmd.py +192 -0
- package/src/superlocalmemory/cli/main.py +39 -4
- package/src/superlocalmemory/cli/mesh_cmd.py +38 -0
- package/src/superlocalmemory/cli/optimize_cmd.py +3 -0
- package/src/superlocalmemory/cli/pending_store.py +49 -13
- package/src/superlocalmemory/cli/proxy_cmd.py +4 -0
- package/src/superlocalmemory/cli/scale_engine_cmd.py +6 -0
- package/src/superlocalmemory/cli/setup_wizard.py +22 -13
- package/src/superlocalmemory/compliance/audit.py +6 -0
- package/src/superlocalmemory/compliance/gdpr.py +128 -138
- package/src/superlocalmemory/compliance/retention.py +176 -45
- package/src/superlocalmemory/core/backend_orchestrator.py +5 -43
- package/src/superlocalmemory/core/community_summary.py +267 -0
- package/src/superlocalmemory/core/config.py +216 -3
- package/src/superlocalmemory/core/consolidation_engine.py +95 -22
- package/src/superlocalmemory/core/context_cache.py +61 -18
- package/src/superlocalmemory/core/embedding_worker.py +17 -2
- package/src/superlocalmemory/core/embeddings.py +12 -1
- package/src/superlocalmemory/core/engine.py +17 -1
- package/src/superlocalmemory/core/engine_ingestion.py +29 -0
- package/src/superlocalmemory/core/engine_wiring.py +13 -0
- package/src/superlocalmemory/core/entity_community.py +178 -0
- package/src/superlocalmemory/core/graph_analyzer.py +39 -2
- package/src/superlocalmemory/core/graph_pruner.py +13 -8
- package/src/superlocalmemory/core/key_expander.py +138 -0
- package/src/superlocalmemory/core/maintenance.py +23 -0
- package/src/superlocalmemory/core/modes.py +1 -1
- package/src/superlocalmemory/core/mutations.py +2 -2
- package/src/superlocalmemory/core/pii.py +105 -0
- package/src/superlocalmemory/core/progressive_abstraction.py +208 -0
- package/src/superlocalmemory/core/recall_pipeline.py +2 -0
- package/src/superlocalmemory/core/recall_worker.py +20 -6
- package/src/superlocalmemory/core/scale_engine.py +60 -1
- package/src/superlocalmemory/core/security_primitives.py +40 -2
- package/src/superlocalmemory/core/store_pipeline.py +35 -11
- package/src/superlocalmemory/core/worker_pool.py +21 -6
- package/src/superlocalmemory/encoding/entity_reflexion.py +200 -0
- package/src/superlocalmemory/encoding/entity_resolver.py +34 -24
- package/src/superlocalmemory/encoding/fact_extractor.py +26 -1
- package/src/superlocalmemory/encoding/temporal_validator.py +64 -1
- package/src/superlocalmemory/evolution/evolution_store.py +122 -45
- package/src/superlocalmemory/evolution/llm_dispatch.py +12 -1
- package/src/superlocalmemory/evolution/model_selection.py +160 -0
- package/src/superlocalmemory/evolution/mutation_generator.py +16 -0
- package/src/superlocalmemory/evolution/skill_evolver.py +127 -42
- package/src/superlocalmemory/evolution/triggers.py +22 -13
- package/src/superlocalmemory/graph/cozo_backend.py +43 -20
- package/src/superlocalmemory/hooks/adapter_base.py +5 -1
- package/src/superlocalmemory/hooks/auto_recall.py +13 -1
- package/src/superlocalmemory/hooks/claude_code_hooks.py +11 -0
- package/src/superlocalmemory/hooks/codex_assets.py +64 -5
- package/src/superlocalmemory/hooks/hook_daemon.py +20 -3
- package/src/superlocalmemory/hooks/memory_protocol.py +54 -0
- package/src/superlocalmemory/hooks/portable_kit.py +114 -1
- package/src/superlocalmemory/infra/auth_middleware.py +28 -0
- package/src/superlocalmemory/infra/backup.py +12 -1
- package/src/superlocalmemory/infra/daemon_identity.py +40 -4
- package/src/superlocalmemory/infra/data_root.py +43 -4
- package/src/superlocalmemory/infra/event_bus.py +107 -24
- package/src/superlocalmemory/infra/rate_limiter.py +93 -0
- package/src/superlocalmemory/ingestion/adapter_manager.py +4 -1
- package/src/superlocalmemory/ingestion/credentials.py +1 -1
- package/src/superlocalmemory/learning/cross_project.py +28 -19
- package/src/superlocalmemory/learning/reward_proxy.py +42 -9
- package/src/superlocalmemory/loops/__init__.py +56 -0
- package/src/superlocalmemory/loops/budget.py +58 -0
- package/src/superlocalmemory/loops/engine.py +164 -0
- package/src/superlocalmemory/loops/ledger.py +243 -0
- package/src/superlocalmemory/loops/models.py +152 -0
- package/src/superlocalmemory/loops/rules.py +52 -0
- package/src/superlocalmemory/mcp/_daemon_proxy.py +3 -0
- package/src/superlocalmemory/mcp/_pool_adapter.py +2 -0
- package/src/superlocalmemory/mcp/profiles.py +103 -0
- package/src/superlocalmemory/mcp/server.py +21 -49
- package/src/superlocalmemory/mcp/tools_active.py +4 -7
- package/src/superlocalmemory/mcp/tools_code_graph.py +51 -5
- package/src/superlocalmemory/mcp/tools_core.py +50 -5
- package/src/superlocalmemory/mcp/tools_evolution.py +6 -3
- package/src/superlocalmemory/mcp/tools_loops.py +300 -0
- package/src/superlocalmemory/mcp/tools_mesh.py +140 -4
- package/src/superlocalmemory/mcp/tools_optimize.py +15 -8
- package/src/superlocalmemory/mesh/broker.py +237 -129
- package/src/superlocalmemory/mesh/remote_sync.py +50 -8
- package/src/superlocalmemory/optimize/NOTICE +1 -6
- package/src/superlocalmemory/optimize/adapters/anthropic_adapter.py +1 -4
- package/src/superlocalmemory/optimize/adapters/openai_adapter.py +1 -4
- package/src/superlocalmemory/optimize/cache/semantic.py +27 -19
- package/src/superlocalmemory/optimize/compress/align.py +32 -26
- package/src/superlocalmemory/optimize/compress/ccr.py +14 -71
- package/src/superlocalmemory/optimize/compress/router.py +105 -22
- package/src/superlocalmemory/optimize/config/defaults.py +1 -1
- package/src/superlocalmemory/optimize/config/schema.py +87 -4
- package/src/superlocalmemory/optimize/metrics/counters.py +13 -4
- package/src/superlocalmemory/optimize/metrics/estimator.py +0 -3
- package/src/superlocalmemory/optimize/proxy/_helpers.py +31 -4
- package/src/superlocalmemory/optimize/storage/db.py +38 -9
- package/src/superlocalmemory/optimize/storage/schema.py +10 -0
- package/src/superlocalmemory/parameterization/pattern_extractor.py +6 -3
- package/src/superlocalmemory/retrieval/agentic.py +1 -1
- package/src/superlocalmemory/retrieval/bm25_channel.py +68 -10
- package/src/superlocalmemory/retrieval/engine.py +168 -26
- package/src/superlocalmemory/retrieval/entity_channel.py +7 -5
- package/src/superlocalmemory/retrieval/hopfield_channel.py +9 -2
- package/src/superlocalmemory/retrieval/semantic_channel.py +114 -21
- package/src/superlocalmemory/retrieval/spreading_activation.py +11 -2
- package/src/superlocalmemory/retrieval/temporal_channel.py +48 -9
- package/src/superlocalmemory/retrieval/temporal_frame.py +102 -0
- package/src/superlocalmemory/retrieval/temporal_validity_filter.py +135 -0
- package/src/superlocalmemory/retrieval/time_window.py +181 -0
- package/src/superlocalmemory/server/api.py +21 -4
- package/src/superlocalmemory/server/profile_runtime.py +125 -8
- package/src/superlocalmemory/server/rbac_enforce.py +142 -0
- package/src/superlocalmemory/server/recall_health.py +24 -3
- package/src/superlocalmemory/server/recall_serializer.py +19 -1
- package/src/superlocalmemory/server/routes/abstraction.py +115 -0
- package/src/superlocalmemory/server/routes/agents.py +128 -38
- package/src/superlocalmemory/server/routes/backup.py +34 -10
- package/src/superlocalmemory/server/routes/behavioral.py +13 -12
- package/src/superlocalmemory/server/routes/brain.py +21 -5
- package/src/superlocalmemory/server/routes/chat.py +72 -16
- package/src/superlocalmemory/server/routes/compliance.py +171 -21
- package/src/superlocalmemory/server/routes/config_api.py +436 -0
- package/src/superlocalmemory/server/routes/data_io.py +30 -8
- package/src/superlocalmemory/server/routes/entity.py +9 -4
- package/src/superlocalmemory/server/routes/events.py +24 -8
- package/src/superlocalmemory/server/routes/evolution.py +135 -17
- package/src/superlocalmemory/server/routes/helpers.py +16 -1
- package/src/superlocalmemory/server/routes/ingest.py +7 -4
- package/src/superlocalmemory/server/routes/insights.py +3 -3
- package/src/superlocalmemory/server/routes/learning.py +14 -14
- package/src/superlocalmemory/server/routes/lifecycle.py +59 -8
- package/src/superlocalmemory/server/routes/memories.py +221 -49
- package/src/superlocalmemory/server/routes/mesh.py +95 -15
- package/src/superlocalmemory/server/routes/optimize.py +33 -1
- package/src/superlocalmemory/server/routes/prewarm.py +2 -0
- package/src/superlocalmemory/server/routes/profiles.py +63 -17
- package/src/superlocalmemory/server/routes/ratelimit.py +124 -0
- package/src/superlocalmemory/server/routes/rbac.py +367 -0
- package/src/superlocalmemory/server/routes/stats.py +13 -6
- package/src/superlocalmemory/server/routes/tiers.py +11 -9
- package/src/superlocalmemory/server/routes/v3_api.py +194 -81
- package/src/superlocalmemory/server/routes/ws.py +5 -2
- package/src/superlocalmemory/server/security_middleware.py +12 -5
- package/src/superlocalmemory/server/ui.py +30 -5
- package/src/superlocalmemory/server/unified_daemon.py +431 -75
- package/src/superlocalmemory/server/write_identity.py +38 -8
- package/src/superlocalmemory/storage/database.py +265 -53
- package/src/superlocalmemory/storage/migration_runner.py +53 -0
- package/src/superlocalmemory/storage/migrations/M021_ingestion_log_profile.py +108 -0
- package/src/superlocalmemory/storage/migrations/M022_entity_aliases_profile.py +86 -0
- package/src/superlocalmemory/storage/migrations/M023_mesh_profile_isolation.py +194 -0
- package/src/superlocalmemory/storage/migrations/M024_rbac_users_roles.py +87 -0
- package/src/superlocalmemory/storage/migrations/M025_perf_indexes.py +90 -0
- package/src/superlocalmemory/storage/migrations/M026_rbac_memberships_fk.py +136 -0
- package/src/superlocalmemory/storage/migrations/M027_transferable_patterns_profile.py +163 -0
- package/src/superlocalmemory/storage/models.py +4 -0
- package/src/superlocalmemory/storage/schema.py +87 -0
- package/src/superlocalmemory/storage/schema_v32.py +0 -9
- package/src/superlocalmemory/storage/schema_v343.py +24 -12
- package/src/superlocalmemory/trust/gate.py +49 -8
- package/src/superlocalmemory/ui/assets/slm-icon-white.svg +64 -0
- package/src/superlocalmemory/ui/assets/slm-icon.svg +36 -0
- package/src/superlocalmemory/ui/css/design-system.css +621 -0
- package/src/superlocalmemory/ui/css/neural-glass.css +6 -0
- package/src/superlocalmemory/ui/css/od-bridge.css +158 -0
- package/src/superlocalmemory/ui/favicon.svg +35 -4
- package/src/superlocalmemory/ui/index.html +306 -173
- package/src/superlocalmemory/ui/js/brain.js +5 -20
- package/src/superlocalmemory/ui/js/core.js +47 -31
- package/src/superlocalmemory/ui/js/dashboard.js +314 -63
- package/src/superlocalmemory/ui/js/event-delegation.js +102 -0
- package/src/superlocalmemory/ui/js/knowledge-graph.js +11 -11
- package/src/superlocalmemory/ui/js/math-health.js +1 -1
- package/src/superlocalmemory/ui/js/memories.js +15 -4
- package/src/superlocalmemory/ui/js/memory-chat.js +7 -7
- package/src/superlocalmemory/ui/js/ng-entities.js +6 -8
- package/src/superlocalmemory/ui/js/ng-ingestion.js +4 -4
- package/src/superlocalmemory/ui/js/ng-mesh.js +4 -9
- package/src/superlocalmemory/ui/js/ng-shell.js +8 -8
- package/src/superlocalmemory/ui/js/ng-skills.js +54 -2
- package/src/superlocalmemory/ui/js/od-agents.js +544 -0
- package/src/superlocalmemory/ui/js/od-auth-gate.js +257 -0
- package/src/superlocalmemory/ui/js/od-backup.js +780 -0
- package/src/superlocalmemory/ui/js/od-brain.js +779 -0
- package/src/superlocalmemory/ui/js/od-entities.js +579 -0
- package/src/superlocalmemory/ui/js/od-graph.js +593 -0
- package/src/superlocalmemory/ui/js/od-health.js +539 -0
- package/src/superlocalmemory/ui/js/od-mcp.js +508 -0
- package/src/superlocalmemory/ui/js/od-memories.js +887 -0
- package/src/superlocalmemory/ui/js/od-mesh.js +539 -0
- package/src/superlocalmemory/ui/js/od-operations.js +1250 -0
- package/src/superlocalmemory/ui/js/od-optimize.js +787 -0
- package/src/superlocalmemory/ui/js/od-settings.js +1053 -0
- package/src/superlocalmemory/ui/js/od-shell.js +593 -0
- package/src/superlocalmemory/ui/js/od-skills.js +573 -0
- package/src/superlocalmemory/ui/js/od-team.js +258 -0
- package/src/superlocalmemory/ui/js/profiles.js +159 -46
- package/src/superlocalmemory/ui/js/settings.js +2 -2
- package/src/superlocalmemory/ui/js/timeline.js +34 -5
- package/src/superlocalmemory/ui/js/trust-dashboard.js +2 -2
- package/src/superlocalmemory/vector/lancedb_backend.py +8 -6
- package/src/superlocalmemory/learning/behavioral_listener.py +0 -94
|
@@ -40,6 +40,14 @@ def _cmd_db_dispatch(args: Namespace) -> None:
|
|
|
40
40
|
sys.exit(2)
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
def _cmd_mesh_dispatch(args: Namespace) -> None:
|
|
44
|
+
"""Route ``slm mesh ...`` inspection subcommands (M-03)."""
|
|
45
|
+
from superlocalmemory.cli.mesh_cmd import cmd_mesh
|
|
46
|
+
rc = cmd_mesh(args)
|
|
47
|
+
if rc:
|
|
48
|
+
sys.exit(rc)
|
|
49
|
+
|
|
50
|
+
|
|
43
51
|
def _cmd_escape_disable(args: Namespace) -> None:
|
|
44
52
|
from superlocalmemory.cli.escape_hatch import cmd_disable
|
|
45
53
|
cmd_disable(args)
|
|
@@ -98,6 +106,11 @@ def _cmd_help_optimize(args: Namespace) -> None:
|
|
|
98
106
|
cmd_help_optimize(args)
|
|
99
107
|
|
|
100
108
|
|
|
109
|
+
def _cmd_loop(args: Namespace) -> None:
|
|
110
|
+
from superlocalmemory.cli.loop_cmd import cmd_loop
|
|
111
|
+
cmd_loop(args)
|
|
112
|
+
|
|
113
|
+
|
|
101
114
|
# ---- end SLM v3.6 Optimize dispatch functions ----
|
|
102
115
|
|
|
103
116
|
|
|
@@ -201,6 +214,8 @@ def dispatch(args: Namespace) -> None:
|
|
|
201
214
|
"context": _cmd_context_dispatch,
|
|
202
215
|
# V3.4.22 LLD-06 additive schema migrations
|
|
203
216
|
"db": _cmd_db_dispatch,
|
|
217
|
+
# V3.7.9 M-03 — terminal mesh inspection
|
|
218
|
+
"mesh": _cmd_mesh_dispatch,
|
|
204
219
|
# V3.4.22 Stage 8 SB-5 — MASTER-PLAN §8 escape hatches.
|
|
205
220
|
"disable": _cmd_escape_disable,
|
|
206
221
|
"enable": _cmd_escape_enable,
|
|
@@ -218,6 +233,8 @@ def dispatch(args: Namespace) -> None:
|
|
|
218
233
|
"compress": _cmd_compress,
|
|
219
234
|
"proxy": _cmd_proxy,
|
|
220
235
|
"help-optimize": _cmd_help_optimize,
|
|
236
|
+
# V3.8.0 bounded loops (gate-verified agent loops, SLM-backed ledger)
|
|
237
|
+
"loop": _cmd_loop,
|
|
221
238
|
}
|
|
222
239
|
handler = handlers.get(args.command)
|
|
223
240
|
if handler:
|
|
@@ -345,6 +362,11 @@ def cmd_serve(args: Namespace) -> None:
|
|
|
345
362
|
else:
|
|
346
363
|
from superlocalmemory.infra.data_root import state_path
|
|
347
364
|
print(f"Failed to start daemon. Check {state_path('logs', 'daemon.log')}")
|
|
365
|
+
# INT-H-02: exit non-zero so callers can detect the failure. The plugin
|
|
366
|
+
# launcher (plugin/scripts/slm-launch) guards on this exit code and
|
|
367
|
+
# refuses to open a direct MCP writer against a broken daemon; without
|
|
368
|
+
# the non-zero exit that guard was a dead no-op.
|
|
369
|
+
sys.exit(1)
|
|
348
370
|
|
|
349
371
|
|
|
350
372
|
# -- Ingestion Adapters (V3.4.3) ------------------------------------------
|
|
@@ -610,6 +632,8 @@ def cmd_config(args: Namespace) -> None:
|
|
|
610
632
|
elif action == "set":
|
|
611
633
|
_ALLOWED_CONFIG_KEYS = {
|
|
612
634
|
"evolution.enabled", "evolution.backend", "evolution.max_evolutions_per_cycle",
|
|
635
|
+
"evolution.mutation_model", "evolution.verify_model",
|
|
636
|
+
"evolution.confirm_model",
|
|
613
637
|
"mesh_enabled", "daemon_idle_timeout", "entity_compilation_enabled",
|
|
614
638
|
"graph_backend", "vector_backend", "scale_engine_state",
|
|
615
639
|
"scope.default_scope", "scope.recall_include_global",
|
|
@@ -677,6 +701,31 @@ def cmd_config(args: Namespace) -> None:
|
|
|
677
701
|
print(f"Error: {message}")
|
|
678
702
|
sys.exit(1)
|
|
679
703
|
|
|
704
|
+
_MODEL_KEYS = {
|
|
705
|
+
"evolution.mutation_model", "evolution.verify_model",
|
|
706
|
+
"evolution.confirm_model",
|
|
707
|
+
}
|
|
708
|
+
if key in _MODEL_KEYS:
|
|
709
|
+
from superlocalmemory.evolution.model_selection import _MODEL_ALIASES
|
|
710
|
+
|
|
711
|
+
accepted = set(_MODEL_ALIASES) | {"", "auto"}
|
|
712
|
+
sval = str(parsed_value)
|
|
713
|
+
if sval not in accepted:
|
|
714
|
+
allowed = ", ".join(["auto", *sorted(_MODEL_ALIASES)])
|
|
715
|
+
message = f"{key} must be one of: {allowed}"
|
|
716
|
+
if use_json:
|
|
717
|
+
from superlocalmemory.cli.json_output import json_print
|
|
718
|
+
json_print("config", error={
|
|
719
|
+
"code": "INVALID_VALUE", "message": message,
|
|
720
|
+
})
|
|
721
|
+
else:
|
|
722
|
+
print(f"Error: {message}")
|
|
723
|
+
sys.exit(1)
|
|
724
|
+
# Normalise the "auto" sentinel to the empty string the
|
|
725
|
+
# resolver treats as "pick the cheapest for the backend".
|
|
726
|
+
if sval == "auto":
|
|
727
|
+
parsed_value = ""
|
|
728
|
+
|
|
680
729
|
if key.startswith("scope."):
|
|
681
730
|
from superlocalmemory.cli.daemon import daemon_request, is_daemon_running
|
|
682
731
|
|
|
@@ -727,6 +776,18 @@ def cmd_config(args: Namespace) -> None:
|
|
|
727
776
|
})
|
|
728
777
|
else:
|
|
729
778
|
print(f"{key}: {old_value} -> {parsed_value}")
|
|
779
|
+
if key == "evolution.enabled" and parsed_value is True:
|
|
780
|
+
print(
|
|
781
|
+
"\n⚠ Skill evolution is now ON. It makes background "
|
|
782
|
+
"LLM calls during consolidation\n"
|
|
783
|
+
" (capped at 10 calls/cycle, 3 cycles/day). It defaults "
|
|
784
|
+
"to the lowest-cost model\n"
|
|
785
|
+
" for your backend (Claude → Haiku, Ollama → "
|
|
786
|
+
"local/free).\n"
|
|
787
|
+
" Pick models: slm config set evolution.mutation_model "
|
|
788
|
+
"<auto|haiku|sonnet|ollama>\n"
|
|
789
|
+
" Turn off: slm config set evolution.enabled false"
|
|
790
|
+
)
|
|
730
791
|
|
|
731
792
|
else:
|
|
732
793
|
if use_json:
|
|
@@ -1271,10 +1332,12 @@ def cmd_recall(args: Namespace) -> None:
|
|
|
1271
1332
|
scope_qs += f"&include_global={str(include_global).lower()}"
|
|
1272
1333
|
if include_shared is not None:
|
|
1273
1334
|
scope_qs += f"&include_shared={str(include_shared).lower()}"
|
|
1335
|
+
_window = getattr(args, "window", "") or ""
|
|
1336
|
+
window_qs = f"&window={quote(_window)}" if _window else ""
|
|
1274
1337
|
result = daemon_request(
|
|
1275
1338
|
"GET",
|
|
1276
1339
|
f"/recall?q={quote(args.query)}&limit={args.limit}"
|
|
1277
|
-
f"&session_id={quote(session_id)}{fast_qs}{scope_qs}",
|
|
1340
|
+
f"&session_id={quote(session_id)}{fast_qs}{scope_qs}{window_qs}",
|
|
1278
1341
|
)
|
|
1279
1342
|
if result and "results" in result:
|
|
1280
1343
|
# Format daemon response same as engine response
|
|
@@ -1294,8 +1357,10 @@ def cmd_recall(args: Namespace) -> None:
|
|
|
1294
1357
|
for i, r in enumerate(result["results"], 1):
|
|
1295
1358
|
print(f" {i}. [{r['score']:.2f}] {r['content']}")
|
|
1296
1359
|
return
|
|
1297
|
-
except Exception:
|
|
1298
|
-
|
|
1360
|
+
except Exception as _exc: # noqa: BLE001
|
|
1361
|
+
logger.warning(
|
|
1362
|
+
"Daemon recall failed, falling back to direct engine: %s", _exc
|
|
1363
|
+
)
|
|
1299
1364
|
|
|
1300
1365
|
from superlocalmemory.core.config import SLMConfig
|
|
1301
1366
|
from superlocalmemory.core.engine import MemoryEngine
|
|
@@ -1310,6 +1375,7 @@ def cmd_recall(args: Namespace) -> None:
|
|
|
1310
1375
|
fast=getattr(args, "fast", False),
|
|
1311
1376
|
include_global=include_global,
|
|
1312
1377
|
include_shared=include_shared,
|
|
1378
|
+
window=getattr(args, "window", "") or None,
|
|
1313
1379
|
)
|
|
1314
1380
|
except Exception as exc:
|
|
1315
1381
|
if use_json:
|
|
@@ -1394,12 +1460,32 @@ def cmd_forget(args: Namespace) -> None:
|
|
|
1394
1460
|
from superlocalmemory.core.config import SLMConfig
|
|
1395
1461
|
|
|
1396
1462
|
use_json = getattr(args, 'json', False)
|
|
1463
|
+
dry_run = getattr(args, 'dry_run', False)
|
|
1464
|
+
raw_query = getattr(args, 'query', None)
|
|
1465
|
+
|
|
1466
|
+
# F3: `query` is optional so `slm forget --dry-run` can preview every memory.
|
|
1467
|
+
# Deletion ALWAYS requires an explicit query — a bare `slm forget` must never
|
|
1468
|
+
# mass-delete. In dry-run mode a missing query means "match all" (preview).
|
|
1469
|
+
if raw_query is None and not dry_run:
|
|
1470
|
+
msg = (
|
|
1471
|
+
"A query is required to delete. Preview everything with "
|
|
1472
|
+
"'slm forget --dry-run', or delete matches with 'slm forget <query>'."
|
|
1473
|
+
)
|
|
1474
|
+
if use_json:
|
|
1475
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1476
|
+
json_print("forget", error={"code": "QUERY_REQUIRED", "message": msg})
|
|
1477
|
+
else:
|
|
1478
|
+
print(msg)
|
|
1479
|
+
sys.exit(2)
|
|
1480
|
+
|
|
1481
|
+
query_lower = "" if raw_query is None else raw_query.lower()
|
|
1482
|
+
query_label = raw_query if raw_query is not None else "*all*"
|
|
1483
|
+
|
|
1397
1484
|
try:
|
|
1398
1485
|
config = SLMConfig.load()
|
|
1399
1486
|
engine = MemoryEngine(config)
|
|
1400
1487
|
engine.initialize()
|
|
1401
1488
|
facts = engine._db.get_all_facts(engine.profile_id)
|
|
1402
|
-
query_lower = args.query.lower()
|
|
1403
1489
|
matches = [f for f in facts if query_lower in f.content.lower()]
|
|
1404
1490
|
except Exception as exc:
|
|
1405
1491
|
if use_json:
|
|
@@ -1408,8 +1494,6 @@ def cmd_forget(args: Namespace) -> None:
|
|
|
1408
1494
|
sys.exit(1)
|
|
1409
1495
|
raise
|
|
1410
1496
|
|
|
1411
|
-
dry_run = getattr(args, 'dry_run', False)
|
|
1412
|
-
|
|
1413
1497
|
def delete_fact_authorized_for_cli(fact_id: str) -> None:
|
|
1414
1498
|
from superlocalmemory.core.engine_ingestion import local_trusted_actor_id
|
|
1415
1499
|
from superlocalmemory.core.mutations import delete_fact_authorized
|
|
@@ -1450,12 +1534,12 @@ def cmd_forget(args: Namespace) -> None:
|
|
|
1450
1534
|
"matches": match_items,
|
|
1451
1535
|
"hint": "Add --yes to confirm deletion",
|
|
1452
1536
|
}, next_actions=[
|
|
1453
|
-
{"command": f"slm forget '{
|
|
1537
|
+
{"command": f"slm forget '{query_label}' --json --yes", "description": "Confirm deletion"},
|
|
1454
1538
|
])
|
|
1455
1539
|
return
|
|
1456
1540
|
|
|
1457
1541
|
if not matches:
|
|
1458
|
-
print(f"No memories matching '{
|
|
1542
|
+
print(f"No memories matching '{query_label}'")
|
|
1459
1543
|
return
|
|
1460
1544
|
print(f"Found {len(matches)} matching memories:")
|
|
1461
1545
|
for f in matches[:10]:
|
|
@@ -1835,8 +1919,8 @@ def cmd_health(args: Namespace) -> None:
|
|
|
1835
1919
|
|
|
1836
1920
|
print("Math Layer Health:")
|
|
1837
1921
|
print(f" Total facts: {len(facts)}")
|
|
1838
|
-
print(f"
|
|
1839
|
-
print(f"
|
|
1922
|
+
print(f" Math layer indexed: {fisher_count}/{len(facts)}")
|
|
1923
|
+
print(f" Lifecycle positioned: {langevin_count}/{len(facts)}")
|
|
1840
1924
|
print(f" Mode: {config.mode.value.upper()}")
|
|
1841
1925
|
|
|
1842
1926
|
|
|
@@ -1999,7 +2083,7 @@ def cmd_doctor(args: Namespace) -> None:
|
|
|
1999
2083
|
|
|
2000
2084
|
# 3. Search deps
|
|
2001
2085
|
search_mods = {"sentence_transformers": "sentence-transformers", "torch": "torch",
|
|
2002
|
-
"sklearn": "scikit-learn"
|
|
2086
|
+
"sklearn": "scikit-learn"}
|
|
2003
2087
|
search_ok = []
|
|
2004
2088
|
for mod, pkg in search_mods.items():
|
|
2005
2089
|
try:
|
|
@@ -2008,7 +2092,7 @@ def cmd_doctor(args: Namespace) -> None:
|
|
|
2008
2092
|
except Exception: # dependency import may fail after module discovery
|
|
2009
2093
|
pass
|
|
2010
2094
|
if len(search_ok) == len(search_mods):
|
|
2011
|
-
_check("Search deps", "PASS", "sentence-transformers, torch, sklearn
|
|
2095
|
+
_check("Search deps", "PASS", "sentence-transformers, torch, sklearn")
|
|
2012
2096
|
else:
|
|
2013
2097
|
missing = set(search_mods) - set(search_ok)
|
|
2014
2098
|
_check("Search deps", "WARN", f"Missing: {', '.join(missing)}",
|
|
@@ -118,8 +118,13 @@ def cmd_compress_prose(args: Namespace) -> None:
|
|
|
118
118
|
cfg = store.get()
|
|
119
119
|
|
|
120
120
|
fields: dict = {"compress_prose": (value == "on")}
|
|
121
|
-
if value == "on"
|
|
122
|
-
|
|
121
|
+
if value == "on":
|
|
122
|
+
# Prose (Layer 2) only fires in aggressive mode, so turning it on sets a
|
|
123
|
+
# COHERENT state — it can never be left enabled-but-inert in safe mode.
|
|
124
|
+
if not cfg.compress_enabled:
|
|
125
|
+
fields["compress_enabled"] = True
|
|
126
|
+
if cfg.compress_mode != "aggressive":
|
|
127
|
+
fields["compress_mode"] = "aggressive"
|
|
123
128
|
|
|
124
129
|
try:
|
|
125
130
|
cfg = dataclasses.replace(cfg, **fields)
|
|
@@ -129,13 +134,18 @@ def cmd_compress_prose(args: Namespace) -> None:
|
|
|
129
134
|
sys.exit(1)
|
|
130
135
|
|
|
131
136
|
if use_json:
|
|
132
|
-
print(json.dumps({
|
|
137
|
+
print(json.dumps({
|
|
138
|
+
"status": "ok",
|
|
139
|
+
"compress_prose": value == "on",
|
|
140
|
+
"compress_mode": cfg.compress_mode,
|
|
141
|
+
}))
|
|
133
142
|
return
|
|
134
143
|
|
|
135
144
|
print(f"Prose compression (LLMLingua-2): {'ENABLED' if value == 'on' else 'DISABLED'}.")
|
|
136
145
|
if value == "on":
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
146
|
+
if "compress_mode" in fields:
|
|
147
|
+
print(" Compression mode set to 'aggressive' (required for Layer 2).")
|
|
148
|
+
print(" Requires the llmlingua package for lossy prose compression.")
|
|
149
|
+
if "compress_enabled" in fields:
|
|
150
|
+
print(" (also enabled global compression)")
|
|
141
151
|
print("Daemon hot-reload: active within 2s.")
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""``slm loop`` — inspect and demonstrate bounded loops backed by SLM memory.
|
|
2
|
+
|
|
3
|
+
Subcommands:
|
|
4
|
+
|
|
5
|
+
* ``slm loop demo`` run a built-in, keyless convergence demo (stub
|
|
6
|
+
proposer + deterministic gate) and persist every
|
|
7
|
+
lap to SLM memory. Proves the engine + ledger
|
|
8
|
+
end to end without a credentialed agent.
|
|
9
|
+
* ``slm loop history [--name]`` list recorded loop runs from SLM memory.
|
|
10
|
+
* ``slm loop show <run_id>`` show every lap of one run.
|
|
11
|
+
|
|
12
|
+
The durable value here is that a loop's history lives in the same SLM data
|
|
13
|
+
root as everything else the agent remembers — queryable via ``slm recall`` and
|
|
14
|
+
visible in the dashboard. Reads and the demo run through an in-process engine
|
|
15
|
+
store rooted at the active data root (``SLM_DATA_DIR`` or
|
|
16
|
+
``~/.superlocalmemory``); point ``SLM_DATA_DIR`` elsewhere to sandbox a run.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
from argparse import Namespace
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
from superlocalmemory.loops import (
|
|
28
|
+
Bounds,
|
|
29
|
+
LapResult,
|
|
30
|
+
SLMMemoryLedger,
|
|
31
|
+
Verdict,
|
|
32
|
+
open_engine_store,
|
|
33
|
+
run_bounded_loop,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _data_root() -> Path:
|
|
38
|
+
value = (
|
|
39
|
+
os.environ.get("SLM_DATA_DIR")
|
|
40
|
+
or os.environ.get("SL_MEMORY_PATH")
|
|
41
|
+
or os.environ.get("SLM_HOME")
|
|
42
|
+
)
|
|
43
|
+
return Path(value).expanduser() if value else Path.home() / ".superlocalmemory"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _FailOpenLedger:
|
|
47
|
+
"""Wrap a ledger so a memory write hiccup never aborts a running loop.
|
|
48
|
+
|
|
49
|
+
A ledger is observability; the loop's correctness comes from the gate.
|
|
50
|
+
Write errors are counted and surfaced after the run rather than raised
|
|
51
|
+
mid-flight, so we never silently pretend they did not happen.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self, inner: Any) -> None:
|
|
55
|
+
self._inner = inner
|
|
56
|
+
self.write_errors: list[str] = []
|
|
57
|
+
|
|
58
|
+
def record(self, entry: Any) -> None:
|
|
59
|
+
try:
|
|
60
|
+
self._inner.record(entry)
|
|
61
|
+
except Exception as exc: # pragma: no cover - defensive path
|
|
62
|
+
self.write_errors.append(f"lap {getattr(entry, 'lap', '?')}: {exc}")
|
|
63
|
+
|
|
64
|
+
def laps(self, run_id: str) -> list:
|
|
65
|
+
return self._inner.laps(run_id)
|
|
66
|
+
|
|
67
|
+
def runs(self, name: str) -> list:
|
|
68
|
+
return self._inner.runs(name)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _open_ledger() -> tuple[SLMMemoryLedger, Any]:
|
|
72
|
+
store = open_engine_store(_data_root() / "memory.db")
|
|
73
|
+
return SLMMemoryLedger(store), store
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def cmd_loop(args: Namespace) -> None:
|
|
77
|
+
action = getattr(args, "loop_command", None)
|
|
78
|
+
if action == "demo":
|
|
79
|
+
_cmd_demo(args)
|
|
80
|
+
elif action == "history":
|
|
81
|
+
_cmd_history(args)
|
|
82
|
+
elif action == "show":
|
|
83
|
+
_cmd_show(args)
|
|
84
|
+
else:
|
|
85
|
+
print("Usage: slm loop {demo|history|show} [options]")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _cmd_demo(args: Namespace) -> None:
|
|
89
|
+
"""Run the convergence demo: the gate fails twice, then passes on lap 3."""
|
|
90
|
+
iterations = int(getattr(args, "iterations", 10) or 10)
|
|
91
|
+
as_json = bool(getattr(args, "json", False))
|
|
92
|
+
pass_on = 3
|
|
93
|
+
|
|
94
|
+
ledger, store = _open_ledger()
|
|
95
|
+
guarded = _FailOpenLedger(ledger)
|
|
96
|
+
try:
|
|
97
|
+
outcome = run_bounded_loop(
|
|
98
|
+
"convergence-demo",
|
|
99
|
+
bounds=Bounds(max_iterations=iterations),
|
|
100
|
+
runner=lambda lap: LapResult(changed=True, tokens=8),
|
|
101
|
+
gate=lambda lap: Verdict(lap >= pass_on, f"demo gate: lap {lap}"),
|
|
102
|
+
ledger=guarded,
|
|
103
|
+
)
|
|
104
|
+
laps = guarded.laps(outcome.run_id)
|
|
105
|
+
finally:
|
|
106
|
+
store.close()
|
|
107
|
+
|
|
108
|
+
if as_json:
|
|
109
|
+
print(json.dumps({
|
|
110
|
+
"status": outcome.status.value,
|
|
111
|
+
"reason": outcome.reason,
|
|
112
|
+
"laps": outcome.laps,
|
|
113
|
+
"run_id": outcome.run_id,
|
|
114
|
+
"ledger": [
|
|
115
|
+
{"lap": e.lap, "decision": e.decision, "passed": e.passed}
|
|
116
|
+
for e in laps
|
|
117
|
+
],
|
|
118
|
+
"write_errors": guarded.write_errors,
|
|
119
|
+
}, indent=2))
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
mark = "✓" if outcome.ok else "✗"
|
|
123
|
+
print(f"{mark} [{outcome.status.value}] {outcome.reason} (laps: {outcome.laps})")
|
|
124
|
+
for e in laps:
|
|
125
|
+
gate = "gate-pass" if e.passed else "gate-fail"
|
|
126
|
+
print(f" lap {e.lap}: {e.decision:<8} {gate} {e.detail}")
|
|
127
|
+
print(f"run_id: {outcome.run_id} (recall with tag loop:convergence-demo)")
|
|
128
|
+
if guarded.write_errors:
|
|
129
|
+
print(f"WARNING: {len(guarded.write_errors)} ledger write error(s): "
|
|
130
|
+
f"{guarded.write_errors[0]}")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _cmd_history(args: Namespace) -> None:
|
|
134
|
+
name = getattr(args, "name", None) or "convergence-demo"
|
|
135
|
+
as_json = bool(getattr(args, "json", False))
|
|
136
|
+
ledger, store = _open_ledger()
|
|
137
|
+
try:
|
|
138
|
+
run_ids = ledger.runs(name)
|
|
139
|
+
rows = []
|
|
140
|
+
for rid in run_ids:
|
|
141
|
+
laps = ledger.laps(rid)
|
|
142
|
+
last = laps[-1] if laps else None
|
|
143
|
+
rows.append({
|
|
144
|
+
"run_id": rid,
|
|
145
|
+
"laps": len(laps),
|
|
146
|
+
"final": last.decision if last else "unknown",
|
|
147
|
+
"ts": last.ts if last else "",
|
|
148
|
+
})
|
|
149
|
+
finally:
|
|
150
|
+
store.close()
|
|
151
|
+
|
|
152
|
+
if as_json:
|
|
153
|
+
print(json.dumps({"name": name, "runs": rows}, indent=2))
|
|
154
|
+
return
|
|
155
|
+
if not rows:
|
|
156
|
+
print(f"No recorded runs for loop '{name}'.")
|
|
157
|
+
return
|
|
158
|
+
print(f"Runs for loop '{name}':")
|
|
159
|
+
for r in rows:
|
|
160
|
+
print(f" {r['run_id']:<28} laps={r['laps']:<3} final={r['final']:<8} {r['ts']}")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _cmd_show(args: Namespace) -> None:
|
|
164
|
+
run_id = getattr(args, "run_id", None)
|
|
165
|
+
as_json = bool(getattr(args, "json", False))
|
|
166
|
+
if not run_id:
|
|
167
|
+
print("Usage: slm loop show <run_id>")
|
|
168
|
+
return
|
|
169
|
+
ledger, store = _open_ledger()
|
|
170
|
+
try:
|
|
171
|
+
laps = ledger.laps(run_id)
|
|
172
|
+
finally:
|
|
173
|
+
store.close()
|
|
174
|
+
|
|
175
|
+
if as_json:
|
|
176
|
+
print(json.dumps({
|
|
177
|
+
"run_id": run_id,
|
|
178
|
+
"laps": [
|
|
179
|
+
{"lap": e.lap, "ts": e.ts, "decision": e.decision,
|
|
180
|
+
"passed": e.passed, "detail": e.detail, "budget": e.budget}
|
|
181
|
+
for e in laps
|
|
182
|
+
],
|
|
183
|
+
}, indent=2))
|
|
184
|
+
return
|
|
185
|
+
if not laps:
|
|
186
|
+
print(f"No ledger entries for run '{run_id}'.")
|
|
187
|
+
return
|
|
188
|
+
print(f"Loop run {run_id} ({laps[0].name}):")
|
|
189
|
+
for e in laps:
|
|
190
|
+
gate = "gate-pass" if e.passed else "gate-fail"
|
|
191
|
+
print(f" lap {e.lap}: {e.decision:<8} {gate} {e.detail} "
|
|
192
|
+
f"[tokens={e.budget.get('tokens', 0)}]")
|
|
@@ -80,6 +80,8 @@ _NO_DAEMON_COMMANDS = {
|
|
|
80
80
|
"wrap",
|
|
81
81
|
# V3.6 Optimize commands that are config read/write only.
|
|
82
82
|
"optimize", "cache", "compress", "help-optimize",
|
|
83
|
+
# Bounded loops use an in-process engine store, not the daemon.
|
|
84
|
+
"loop",
|
|
83
85
|
# Lifecycle orchestration must run before any global auto-start hook.
|
|
84
86
|
"serve", "restart",
|
|
85
87
|
}
|
|
@@ -276,6 +278,14 @@ def main() -> None:
|
|
|
276
278
|
db_scale_p.add_argument("--stage-id", help="Stage identifier required by verify/promote")
|
|
277
279
|
db_scale_p.add_argument("--backup-id", help="Backup identifier required by rollback")
|
|
278
280
|
|
|
281
|
+
# -- Mesh inspection (v3.7.9, M-03) --------------------------------
|
|
282
|
+
mesh_p = sub.add_parser("mesh", help="Inspect the local agent mesh (status/peers)")
|
|
283
|
+
mesh_p.add_argument(
|
|
284
|
+
"mesh_action",
|
|
285
|
+
choices=("status", "peers"),
|
|
286
|
+
help="status: broker health + stats; peers: active peer sessions",
|
|
287
|
+
)
|
|
288
|
+
|
|
279
289
|
# -- Memory Operations ---------------------------------------------
|
|
280
290
|
remember_p = sub.add_parser("remember", help="Store a memory (extracts facts, builds graph)")
|
|
281
291
|
remember_p.add_argument("content", help="Content to remember")
|
|
@@ -304,9 +314,15 @@ def main() -> None:
|
|
|
304
314
|
help=f"Max results (default {CANONICAL_RECALL_LIMIT})",
|
|
305
315
|
)
|
|
306
316
|
recall_p.add_argument("--json", action="store_true", help="Output structured JSON (agent-native)")
|
|
317
|
+
recall_p.add_argument(
|
|
318
|
+
"--window", default="",
|
|
319
|
+
help="Restrict results to an event-time range: a relative span "
|
|
320
|
+
"(24h, 7d, 30d, 1y) or an explicit range (2026-07-01..2026-07-31). "
|
|
321
|
+
"Default: no time filter.",
|
|
322
|
+
)
|
|
307
323
|
recall_p.add_argument(
|
|
308
324
|
"--fast", action="store_true",
|
|
309
|
-
help="Skip
|
|
325
|
+
help="Skip graph-assisted candidate expansion and remote agentic verification for a "
|
|
310
326
|
"latency-bounded response. Other configured retrieval channels still run. "
|
|
311
327
|
"Use when you need recall before a tool call (e.g. before WebSearch).",
|
|
312
328
|
)
|
|
@@ -332,8 +348,8 @@ def main() -> None:
|
|
|
332
348
|
)
|
|
333
349
|
|
|
334
350
|
forget_p = sub.add_parser("forget", help="Delete memories matching a query (fuzzy)")
|
|
335
|
-
forget_p.add_argument("query", help="Query to match for deletion")
|
|
336
|
-
forget_p.add_argument("--dry-run", action="store_true", default=False, help="Preview matches without deleting")
|
|
351
|
+
forget_p.add_argument("query", nargs="?", default=None, help="Query to match for deletion. Optional with --dry-run (previews all memories).")
|
|
352
|
+
forget_p.add_argument("--dry-run", action="store_true", default=False, help="Preview matches without deleting. With no query, previews every memory.")
|
|
337
353
|
forget_p.add_argument("--yes", "-y", action="store_true", help="Skip confirmation prompt")
|
|
338
354
|
forget_p.add_argument("--json", action="store_true", help="Output structured JSON (agent-native)")
|
|
339
355
|
|
|
@@ -361,7 +377,7 @@ def main() -> None:
|
|
|
361
377
|
help="Show extended status: migration log, daemon port, disabled marker, last version",
|
|
362
378
|
)
|
|
363
379
|
|
|
364
|
-
health_p = sub.add_parser("health", help="Math layer health (
|
|
380
|
+
health_p = sub.add_parser("health", help="Math layer health (scoring, consistency, and lifecycle layers)")
|
|
365
381
|
health_p.add_argument("--json", action="store_true", help="Output structured JSON (agent-native)")
|
|
366
382
|
|
|
367
383
|
trace_p = sub.add_parser("trace", help="Recall with per-channel score breakdown")
|
|
@@ -774,6 +790,25 @@ def main() -> None:
|
|
|
774
790
|
|
|
775
791
|
# ---- end SLM v3.6 Optimize subcommands ----
|
|
776
792
|
|
|
793
|
+
# slm loop demo|history|show — bounded, gate-verified agent loops (v3.8.0)
|
|
794
|
+
loop_p = sub.add_parser(
|
|
795
|
+
"loop",
|
|
796
|
+
help="Bounded loops: gate-verified agent loops with an SLM-backed ledger",
|
|
797
|
+
)
|
|
798
|
+
loop_sub = loop_p.add_subparsers(dest="loop_command", title="loop subcommands")
|
|
799
|
+
loop_demo_p = loop_sub.add_parser(
|
|
800
|
+
"demo", help="Run the keyless convergence demo (proves engine + ledger)")
|
|
801
|
+
loop_demo_p.add_argument(
|
|
802
|
+
"--iterations", type=int, default=10, help="Max iterations (default: 10)")
|
|
803
|
+
loop_hist_p = loop_sub.add_parser("history", help="List recorded loop runs")
|
|
804
|
+
loop_hist_p.add_argument(
|
|
805
|
+
"--name", default=None, help="Loop name (default: convergence-demo)")
|
|
806
|
+
loop_show_p = loop_sub.add_parser("show", help="Show every lap of one run")
|
|
807
|
+
loop_show_p.add_argument("run_id", help="Run id (from history)")
|
|
808
|
+
for _sp in loop_sub.choices.values():
|
|
809
|
+
_sp.add_argument("--json", action="store_true",
|
|
810
|
+
help="Output structured JSON (agent-native)")
|
|
811
|
+
|
|
777
812
|
args = parser.parse_args()
|
|
778
813
|
|
|
779
814
|
if not args.command:
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under AGPL-3.0-or-later
|
|
3
|
+
"""`slm mesh` — inspect the local agent mesh from the terminal (M-03, v3.7.9).
|
|
4
|
+
|
|
5
|
+
Before 3.7.9 the mesh was reachable only via MCP tools, the dashboard, and
|
|
6
|
+
Claude Code skills — there was no terminal command to check broker health or
|
|
7
|
+
list peer sessions. This is a thin, read-only wrapper over the same
|
|
8
|
+
capability-authenticated `/mesh/*` daemon endpoints the MCP tools use.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from argparse import Namespace
|
|
14
|
+
|
|
15
|
+
_ACTION_ENDPOINTS = {
|
|
16
|
+
"status": "/mesh/status",
|
|
17
|
+
"peers": "/mesh/peers",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def cmd_mesh(args: Namespace) -> int:
|
|
22
|
+
action = getattr(args, "mesh_action", None)
|
|
23
|
+
endpoint = _ACTION_ENDPOINTS.get(action)
|
|
24
|
+
if endpoint is None:
|
|
25
|
+
print("Usage: slm mesh {status|peers}")
|
|
26
|
+
return 2
|
|
27
|
+
|
|
28
|
+
from superlocalmemory.cli.daemon import daemon_request
|
|
29
|
+
|
|
30
|
+
result = daemon_request("GET", endpoint)
|
|
31
|
+
if result is None:
|
|
32
|
+
print(
|
|
33
|
+
"Mesh: cannot reach the daemon broker. Is the daemon running? "
|
|
34
|
+
"(slm serve start)"
|
|
35
|
+
)
|
|
36
|
+
return 1
|
|
37
|
+
print(json.dumps(result, indent=2, sort_keys=True))
|
|
38
|
+
return 0
|
|
@@ -98,6 +98,9 @@ def cmd_optimize_status(args: Namespace) -> None:
|
|
|
98
98
|
print(f" Compress: {'enabled' if cfg.compress_enabled else 'disabled'}"
|
|
99
99
|
f" (mode: {cfg.compress_mode},"
|
|
100
100
|
f" prose/L2: {'ON' if cfg.compress_prose else 'OFF'})")
|
|
101
|
+
if cfg.compress_prose or cfg.compress_mode == "aggressive":
|
|
102
|
+
print(" note: the live proxy applies LOSSLESS compression only;"
|
|
103
|
+
" lossy Layer-2 (prose) runs via the slm_compress tool.")
|
|
101
104
|
proxy_status = f"running on :{OPTIMIZE_DEFAULT_PORT}" if proxy_running else "not running"
|
|
102
105
|
print(f" Proxy: {proxy_status}")
|
|
103
106
|
print(f" Config: ~/.superlocalmemory/optimize.json (version {cfg.config_version})")
|