superlocalmemory 3.4.10 → 3.4.11
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/README.md +17 -11
- package/docs/skill-evolution.md +77 -10
- package/ide/hooks/tool-event-hook.sh +4 -4
- package/package.json +1 -1
- package/pyproject.toml +3 -2
- package/src/superlocalmemory/cli/commands.py +170 -0
- package/src/superlocalmemory/cli/main.py +21 -0
- package/src/superlocalmemory/cli/setup_wizard.py +54 -11
- package/src/superlocalmemory/core/config.py +35 -0
- package/src/superlocalmemory/core/consolidation_engine.py +128 -0
- package/src/superlocalmemory/core/embedding_worker.py +1 -1
- package/src/superlocalmemory/core/engine.py +12 -0
- package/src/superlocalmemory/core/fact_consolidator.py +425 -0
- package/src/superlocalmemory/core/graph_pruner.py +290 -0
- package/src/superlocalmemory/core/maintenance_scheduler.py +20 -0
- package/src/superlocalmemory/core/recall_pipeline.py +9 -0
- package/src/superlocalmemory/core/tier_manager.py +325 -0
- package/src/superlocalmemory/encoding/entity_resolver.py +6 -5
- package/src/superlocalmemory/evolution/__init__.py +29 -0
- package/src/superlocalmemory/evolution/blind_verifier.py +115 -0
- package/src/superlocalmemory/evolution/evolution_store.py +302 -0
- package/src/superlocalmemory/evolution/mutation_generator.py +181 -0
- package/src/superlocalmemory/evolution/skill_evolver.py +555 -0
- package/src/superlocalmemory/evolution/triggers.py +367 -0
- package/src/superlocalmemory/evolution/types.py +92 -0
- package/src/superlocalmemory/hooks/hook_handlers.py +13 -0
- package/src/superlocalmemory/learning/skill_performance_miner.py +44 -11
- package/src/superlocalmemory/mcp/server.py +4 -0
- package/src/superlocalmemory/mcp/tools_evolution.py +338 -0
- package/src/superlocalmemory/retrieval/engine.py +64 -4
- package/src/superlocalmemory/retrieval/forgetting_filter.py +22 -7
- package/src/superlocalmemory/retrieval/strategy.py +2 -2
- package/src/superlocalmemory/server/routes/behavioral.py +19 -15
- package/src/superlocalmemory/server/routes/evolution.py +213 -0
- package/src/superlocalmemory/server/routes/tiers.py +195 -0
- package/src/superlocalmemory/server/unified_daemon.py +36 -5
- package/src/superlocalmemory/storage/schema_v3411.py +149 -0
- package/src/superlocalmemory/ui/index.html +5 -2
- package/src/superlocalmemory/ui/js/lifecycle.js +83 -0
- package/src/superlocalmemory/ui/js/ng-skills.js +394 -10
- package/src/superlocalmemory.egg-info/PKG-INFO +609 -0
- package/src/superlocalmemory.egg-info/SOURCES.txt +335 -0
- package/src/superlocalmemory.egg-info/dependency_links.txt +1 -0
- package/src/superlocalmemory.egg-info/entry_points.txt +2 -0
- package/src/superlocalmemory.egg-info/requires.txt +55 -0
- package/src/superlocalmemory.egg-info/top_level.txt +1 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<h1 align="center">SuperLocalMemory V3.4</h1>
|
|
6
6
|
<p align="center"><strong>Every other AI forgets. Yours won't.</strong><br/><em>Infinite memory for Claude Code, Cursor, Windsurf & 17+ AI tools.</em></p>
|
|
7
|
-
<p align="center"><code>v3.4.
|
|
7
|
+
<p align="center"><code>v3.4.11</code> — Install once. Every session remembers the last. Automatically.</p>
|
|
8
8
|
<p align="center"><strong>Backed by 3 peer-reviewed research papers</strong> · <a href="https://arxiv.org/abs/2603.02240">arXiv:2603.02240</a> · <a href="https://arxiv.org/abs/2603.14588">arXiv:2603.14588</a> · <a href="https://arxiv.org/abs/2604.04514">arXiv:2604.04514</a></p>
|
|
9
9
|
|
|
10
10
|
<p align="center">
|
|
@@ -378,14 +378,20 @@ Auto-capture hooks: `slm hooks install` + `slm observe` + `slm session-context`.
|
|
|
378
378
|
- Auto-learned soft prompts injected into agent context
|
|
379
379
|
- Behavioral pattern detection and outcome tracking
|
|
380
380
|
|
|
381
|
-
### Skill Evolution
|
|
382
|
-
- **Per-skill performance tracking** —
|
|
383
|
-
- **
|
|
384
|
-
- **
|
|
385
|
-
- **
|
|
386
|
-
- **
|
|
387
|
-
- **
|
|
388
|
-
- **
|
|
381
|
+
### Skill Evolution
|
|
382
|
+
- **Per-skill performance tracking** — tracks which skills succeed and fail across sessions (zero-LLM, always on)
|
|
383
|
+
- **Evolution engine** — 3-trigger system with blind verification. Off by default — enable via `slm config set evolution.enabled true`
|
|
384
|
+
- **MCP tools** — `evolve_skill`, `skill_health`, `skill_lineage` for programmatic access
|
|
385
|
+
- **Lineage DAG** — visual evolution history in the dashboard
|
|
386
|
+
- **CLI config** — `slm config get/set` for all evolution settings
|
|
387
|
+
- **Post-session triggers** — automatic analysis on session end via Stop hook
|
|
388
|
+
- **[ECC](https://github.com/affaan-m/everything-claude-code) integration** — optional enhanced observations via `slm ingest --source ecc`
|
|
389
|
+
|
|
390
|
+
### Tiered Storage & Scaling
|
|
391
|
+
- **4-tier lifecycle** — active, warm, cold, archived with automatic promotion/demotion
|
|
392
|
+
- **Deep recall** — archived facts searchable at reduced weight
|
|
393
|
+
- **Graph pruning** — automatic cleanup of orphan edges, self-loops, duplicates
|
|
394
|
+
- **Fact consolidation** — clusters related facts into consolidated summaries
|
|
389
395
|
|
|
390
396
|
### Trust & Security
|
|
391
397
|
- Bayesian Beta-distribution trust scoring (per-agent, per-fact)
|
|
@@ -396,9 +402,9 @@ Auto-capture hooks: `slm hooks install` + `slm observe` + `slm session-context`.
|
|
|
396
402
|
### Infrastructure
|
|
397
403
|
- 23-tab web dashboard with real-time visualization
|
|
398
404
|
- 17+ IDE integrations (Claude, Cursor, Windsurf, VS Code, JetBrains, Zed, etc.)
|
|
399
|
-
-
|
|
405
|
+
- 38 MCP tools + 7 MCP resources
|
|
400
406
|
- Profile isolation (independent memory spaces)
|
|
401
|
-
-
|
|
407
|
+
- 2,900+ tests, AGPL v3, cross-platform (Mac/Linux/Windows)
|
|
402
408
|
- CPU-only — no GPU required
|
|
403
409
|
- Automatic orphaned process cleanup
|
|
404
410
|
|
package/docs/skill-evolution.md
CHANGED
|
@@ -70,8 +70,10 @@ The dedicated **Skill Evolution** tab in the SLM dashboard shows:
|
|
|
70
70
|
|
|
71
71
|
- **Overview cards** — Total skill events, unique skills, performance assertions, skill correlations
|
|
72
72
|
- **Skill performance cards** — Per-skill effective score, invocation count, confidence level
|
|
73
|
+
- **Evolution Engine status** — Backend detection, enable/disable toggle, run button
|
|
74
|
+
- **Skill Lineage DAG** — Visual graph of evolved skill versions (parent → child relationships)
|
|
75
|
+
- **Lineage table** — Clickable rows showing evolution type, status, verification result
|
|
73
76
|
- **Skill correlations** — Which skills work well together
|
|
74
|
-
- **Compatibility notice** — Current IDE support status
|
|
75
77
|
|
|
76
78
|
Access: Open `http://localhost:8765` and navigate to the Skill Evolution tab in the sidebar.
|
|
77
79
|
|
|
@@ -132,23 +134,69 @@ This reads ECC's observation files from `~/.claude/homunculus/projects/*/observa
|
|
|
132
134
|
|
|
133
135
|
## Configuration
|
|
134
136
|
|
|
135
|
-
###
|
|
137
|
+
### Skill Tracking (C1 — always on)
|
|
136
138
|
|
|
137
|
-
Skill tracking is enabled by default when the SLM hook is registered.
|
|
139
|
+
Skill performance tracking is enabled by default when the SLM hook is registered. Zero-LLM, zero-cost. Runs as Step 10 in the consolidation pipeline.
|
|
138
140
|
|
|
139
141
|
```bash
|
|
140
142
|
slm status # Shows hook registration status
|
|
143
|
+
slm consolidate --cognitive # Trigger manual consolidation
|
|
141
144
|
```
|
|
142
145
|
|
|
143
|
-
###
|
|
146
|
+
### Skill Evolution (C2 — off by default)
|
|
144
147
|
|
|
145
|
-
Skill
|
|
148
|
+
The Skill Evolution Engine uses LLM calls to generate improved skill versions. **It is OFF by default** — end users must opt in.
|
|
149
|
+
|
|
150
|
+
**Why off by default:** Evolution makes LLM calls (confirmation gate + mutation + blind verification). Even with budget caps, users should consciously enable this and configure their LLM backend.
|
|
151
|
+
|
|
152
|
+
#### Enable via CLI
|
|
146
153
|
|
|
147
154
|
```bash
|
|
148
|
-
slm
|
|
155
|
+
slm config set evolution.enabled true
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### Enable via Interactive Installer
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
slm setup # Interactive wizard includes evolution opt-in
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### Enable via Dashboard
|
|
165
|
+
|
|
166
|
+
Navigate to Settings → Skill Evolution → Enable.
|
|
167
|
+
|
|
168
|
+
### LLM Backend — Auto-Detect
|
|
169
|
+
|
|
170
|
+
Evolution uses a single auto-detect chain. No manual configuration needed for most users:
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
Priority 1: `claude` CLI available → spawn `claude --model haiku` (FREE, best quality)
|
|
174
|
+
Priority 2: Ollama running → use Ollama (FREE, local)
|
|
175
|
+
Priority 3: API key set → use Anthropic/OpenAI API (paid)
|
|
176
|
+
Priority 4: Nothing available → dashboard-only (show candidates, manual evolution)
|
|
149
177
|
```
|
|
150
178
|
|
|
151
|
-
|
|
179
|
+
This means:
|
|
180
|
+
- **Claude Code users:** Evolution works for free — uses your existing Claude subscription
|
|
181
|
+
- **Other IDE users with Ollama:** Evolution works for free — uses local Ollama
|
|
182
|
+
- **Advanced users:** Can point at Anthropic/OpenAI API if preferred
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Override auto-detect (optional — most users never need this)
|
|
186
|
+
slm config set evolution.backend claude
|
|
187
|
+
slm config set evolution.backend ollama
|
|
188
|
+
slm config set evolution.backend anthropic
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Full Evolution Config Reference
|
|
192
|
+
|
|
193
|
+
| Key | Default | Description |
|
|
194
|
+
|-----|---------|-------------|
|
|
195
|
+
| `evolution.enabled` | `false` | Master switch — off by default, opt-in |
|
|
196
|
+
| `evolution.backend` | `auto` | LLM backend: `auto`, `claude`, `ollama`, `anthropic`, `openai` |
|
|
197
|
+
| `evolution.max_evolutions_per_cycle` | `3` | Budget cap per consolidation cycle |
|
|
198
|
+
|
|
199
|
+
### Tracking Thresholds (C1)
|
|
152
200
|
|
|
153
201
|
| Parameter | Default | Description |
|
|
154
202
|
|-----------|---------|-------------|
|
|
@@ -172,11 +220,30 @@ SLM's skill evolution system draws from:
|
|
|
172
220
|
|
|
173
221
|
---
|
|
174
222
|
|
|
223
|
+
## MCP Tools
|
|
224
|
+
|
|
225
|
+
Three MCP tools are available for programmatic access:
|
|
226
|
+
|
|
227
|
+
| Tool | Description |
|
|
228
|
+
|------|-------------|
|
|
229
|
+
| `evolve_skill` | Manually trigger evolution for a specific skill |
|
|
230
|
+
| `skill_health` | Get health metrics (invocations, error rate, status) for skills |
|
|
231
|
+
| `skill_lineage` | Get evolution lineage tree for a skill |
|
|
232
|
+
|
|
233
|
+
These tools are registered automatically and available in all supported IDEs.
|
|
234
|
+
|
|
235
|
+
## CLI Commands
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
slm config get evolution.enabled # Check if evolution is enabled
|
|
239
|
+
slm config set evolution.enabled true # Enable evolution
|
|
240
|
+
slm config set evolution.backend auto # Set LLM backend
|
|
241
|
+
```
|
|
242
|
+
|
|
175
243
|
## What's Next
|
|
176
244
|
|
|
177
|
-
- **
|
|
178
|
-
- **
|
|
179
|
-
- **IDE Adapters** — Cursor, Windsurf, VS Code Copilot, JetBrains support.
|
|
245
|
+
- **IDE Adapters** — Cursor, Windsurf, VS Code Copilot, JetBrains support for skill tracking.
|
|
246
|
+
- **Skill lineage visualization improvements** — Richer DAG with performance history overlay.
|
|
180
247
|
|
|
181
248
|
---
|
|
182
249
|
|
|
@@ -76,14 +76,14 @@ import json, sys, os, re
|
|
|
76
76
|
|
|
77
77
|
# Secret scrubbing pattern (adopted from ECC observe.sh)
|
|
78
78
|
_SECRET_RE = re.compile(
|
|
79
|
-
r"(?i)(api[_-]?key|token|secret|password|authorization|credentials?|auth)"
|
|
79
|
+
r"(?i)(api[_-]?key|[a-z_]*_key|token|secret|password|pass\b|authorization|credentials?|auth)"
|
|
80
80
|
r"""([\"'"'"'"'"'"'\s:=]+)"""
|
|
81
81
|
r"([A-Za-z]+\s+)?"
|
|
82
82
|
r"([A-Za-z0-9_\-/.+=]{8,})"
|
|
83
83
|
)
|
|
84
84
|
|
|
85
85
|
def scrub(val, max_len=500):
|
|
86
|
-
"""
|
|
86
|
+
"""Scrub secrets first, then truncate."""
|
|
87
87
|
if val is None:
|
|
88
88
|
return ""
|
|
89
89
|
if isinstance(val, dict):
|
|
@@ -92,10 +92,10 @@ def scrub(val, max_len=500):
|
|
|
92
92
|
s = json.dumps(val, default=str)
|
|
93
93
|
else:
|
|
94
94
|
s = str(val)
|
|
95
|
-
s =
|
|
96
|
-
return _SECRET_RE.sub(
|
|
95
|
+
s = _SECRET_RE.sub(
|
|
97
96
|
lambda m: m.group(1) + m.group(2) + (m.group(3) or "") + "[REDACTED]", s
|
|
98
97
|
)
|
|
98
|
+
return s[:max_len]
|
|
99
99
|
|
|
100
100
|
try:
|
|
101
101
|
data = json.load(sys.stdin)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superlocalmemory",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.11",
|
|
4
4
|
"description": "Information-geometric agent memory with mathematical guarantees. 4-channel retrieval, Fisher-Rao similarity, zero-LLM mode, EU AI Act compliant. Works with Claude, Cursor, Windsurf, and 17+ AI tools.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-memory",
|
package/pyproject.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "superlocalmemory"
|
|
3
|
-
version = "3.4.
|
|
3
|
+
version = "3.4.11"
|
|
4
4
|
description = "Information-geometric agent memory with mathematical guarantees"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {text = "AGPL-3.0-or-later"}
|
|
@@ -112,8 +112,9 @@ superlocalmemory = ["ui/**/*"]
|
|
|
112
112
|
[tool.pytest.ini_options]
|
|
113
113
|
testpaths = ["tests"]
|
|
114
114
|
pythonpath = ["src"]
|
|
115
|
+
addopts = "-m 'not slow and not ollama'"
|
|
115
116
|
markers = [
|
|
116
|
-
"slow: marks tests as slow (
|
|
117
|
+
"slow: marks tests as slow — real engine/model loading (run with: pytest -m slow)",
|
|
117
118
|
"ollama: marks tests that require a running Ollama instance",
|
|
118
119
|
]
|
|
119
120
|
filterwarnings = [
|
|
@@ -64,6 +64,9 @@ def dispatch(args: Namespace) -> None:
|
|
|
64
64
|
"adapters": cmd_adapters,
|
|
65
65
|
# V3.4.8 external observation ingestion
|
|
66
66
|
"ingest": cmd_ingest,
|
|
67
|
+
# V3.4.11 skill evolution
|
|
68
|
+
"config": cmd_config,
|
|
69
|
+
"evolve": cmd_evolve,
|
|
67
70
|
}
|
|
68
71
|
handler = handlers.get(args.command)
|
|
69
72
|
if handler:
|
|
@@ -341,6 +344,173 @@ def cmd_ingest(args: Namespace) -> None:
|
|
|
341
344
|
_ingest(args)
|
|
342
345
|
|
|
343
346
|
|
|
347
|
+
# -- Config & Evolution (V3.4.11) -----------------------------------------------
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def cmd_config(args: Namespace) -> None:
|
|
351
|
+
"""Get or set config values (dot-notation).
|
|
352
|
+
|
|
353
|
+
Usage:
|
|
354
|
+
slm config set evolution.enabled true
|
|
355
|
+
slm config set evolution.backend auto
|
|
356
|
+
slm config get evolution.enabled
|
|
357
|
+
slm config get evolution.backend
|
|
358
|
+
"""
|
|
359
|
+
import json
|
|
360
|
+
from pathlib import Path
|
|
361
|
+
|
|
362
|
+
use_json = getattr(args, "json", False)
|
|
363
|
+
action = getattr(args, "action", "get")
|
|
364
|
+
key = getattr(args, "key", "")
|
|
365
|
+
value = getattr(args, "value", None)
|
|
366
|
+
|
|
367
|
+
config_path = Path.home() / ".superlocalmemory" / "config.json"
|
|
368
|
+
|
|
369
|
+
# Read existing config
|
|
370
|
+
cfg: dict = {}
|
|
371
|
+
if config_path.exists():
|
|
372
|
+
try:
|
|
373
|
+
cfg = json.loads(config_path.read_text())
|
|
374
|
+
except (json.JSONDecodeError, OSError):
|
|
375
|
+
pass
|
|
376
|
+
|
|
377
|
+
if action == "get":
|
|
378
|
+
# Parse dot-notation key (e.g. "evolution.enabled")
|
|
379
|
+
parts = key.split(".") if key else []
|
|
380
|
+
node = cfg
|
|
381
|
+
for part in parts:
|
|
382
|
+
if isinstance(node, dict) and part in node:
|
|
383
|
+
node = node[part]
|
|
384
|
+
else:
|
|
385
|
+
node = None
|
|
386
|
+
break
|
|
387
|
+
|
|
388
|
+
if use_json:
|
|
389
|
+
from superlocalmemory.cli.json_output import json_print
|
|
390
|
+
json_print("config", data={"key": key, "value": node})
|
|
391
|
+
else:
|
|
392
|
+
if node is None:
|
|
393
|
+
print(f"{key}: (not set)")
|
|
394
|
+
else:
|
|
395
|
+
print(f"{key}: {node}")
|
|
396
|
+
|
|
397
|
+
elif action == "set":
|
|
398
|
+
_ALLOWED_CONFIG_KEYS = {
|
|
399
|
+
"evolution.enabled", "evolution.backend", "evolution.max_evolutions_per_cycle",
|
|
400
|
+
"mesh_enabled", "daemon_idle_timeout", "entity_compilation_enabled",
|
|
401
|
+
}
|
|
402
|
+
if key not in _ALLOWED_CONFIG_KEYS:
|
|
403
|
+
if use_json:
|
|
404
|
+
from superlocalmemory.cli.json_output import json_print
|
|
405
|
+
json_print("config", error={
|
|
406
|
+
"code": "DISALLOWED_KEY",
|
|
407
|
+
"message": f"'{key}' is not a configurable key. Allowed: {', '.join(sorted(_ALLOWED_CONFIG_KEYS))}",
|
|
408
|
+
})
|
|
409
|
+
else:
|
|
410
|
+
print(f"Error: '{key}' is not a configurable key. Allowed: {', '.join(sorted(_ALLOWED_CONFIG_KEYS))}")
|
|
411
|
+
sys.exit(1)
|
|
412
|
+
|
|
413
|
+
if not key or value is None:
|
|
414
|
+
if use_json:
|
|
415
|
+
from superlocalmemory.cli.json_output import json_print
|
|
416
|
+
json_print("config", error={
|
|
417
|
+
"code": "INVALID_INPUT",
|
|
418
|
+
"message": "Usage: slm config set <key> <value>",
|
|
419
|
+
})
|
|
420
|
+
else:
|
|
421
|
+
print("Usage: slm config set <key> <value>")
|
|
422
|
+
sys.exit(1)
|
|
423
|
+
|
|
424
|
+
# Parse value: booleans, numbers, strings
|
|
425
|
+
parsed_value: object
|
|
426
|
+
if value.lower() in ("true", "yes", "on"):
|
|
427
|
+
parsed_value = True
|
|
428
|
+
elif value.lower() in ("false", "no", "off"):
|
|
429
|
+
parsed_value = False
|
|
430
|
+
else:
|
|
431
|
+
try:
|
|
432
|
+
parsed_value = int(value)
|
|
433
|
+
except ValueError:
|
|
434
|
+
try:
|
|
435
|
+
parsed_value = float(value)
|
|
436
|
+
except ValueError:
|
|
437
|
+
parsed_value = value
|
|
438
|
+
|
|
439
|
+
# Set via dot-notation (e.g. "evolution.enabled" -> cfg["evolution"]["enabled"])
|
|
440
|
+
parts = key.split(".")
|
|
441
|
+
node = cfg
|
|
442
|
+
for part in parts[:-1]:
|
|
443
|
+
if part not in node or not isinstance(node.get(part), dict):
|
|
444
|
+
node[part] = {}
|
|
445
|
+
node = node[part]
|
|
446
|
+
old_value = node.get(parts[-1])
|
|
447
|
+
node[parts[-1]] = parsed_value
|
|
448
|
+
|
|
449
|
+
# Write back
|
|
450
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
451
|
+
config_path.write_text(json.dumps(cfg, indent=2) + "\n")
|
|
452
|
+
|
|
453
|
+
if use_json:
|
|
454
|
+
from superlocalmemory.cli.json_output import json_print
|
|
455
|
+
json_print("config", data={
|
|
456
|
+
"key": key, "old_value": old_value, "new_value": parsed_value,
|
|
457
|
+
})
|
|
458
|
+
else:
|
|
459
|
+
print(f"{key}: {old_value} -> {parsed_value}")
|
|
460
|
+
|
|
461
|
+
else:
|
|
462
|
+
if use_json:
|
|
463
|
+
from superlocalmemory.cli.json_output import json_print
|
|
464
|
+
json_print("config", error={
|
|
465
|
+
"code": "UNKNOWN_ACTION",
|
|
466
|
+
"message": f"Unknown action: {action}. Use 'get' or 'set'.",
|
|
467
|
+
})
|
|
468
|
+
else:
|
|
469
|
+
print(f"Unknown config action: {action}. Use 'get' or 'set'.")
|
|
470
|
+
sys.exit(1)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def cmd_evolve(args: Namespace) -> None:
|
|
474
|
+
"""Run skill evolution for a session (called from Stop hook).
|
|
475
|
+
|
|
476
|
+
Reads config.json to check if evolution is enabled.
|
|
477
|
+
If enabled, imports SkillEvolver and runs run_post_session().
|
|
478
|
+
If disabled, exits silently (zero output for fire-and-forget).
|
|
479
|
+
"""
|
|
480
|
+
import json
|
|
481
|
+
from pathlib import Path
|
|
482
|
+
|
|
483
|
+
session_id = getattr(args, "session", "") or ""
|
|
484
|
+
profile = getattr(args, "profile", "default") or "default"
|
|
485
|
+
|
|
486
|
+
if not session_id:
|
|
487
|
+
return # Silent exit — nothing to do without a session
|
|
488
|
+
|
|
489
|
+
# Check if evolution is enabled via config.json
|
|
490
|
+
config_path = Path.home() / ".superlocalmemory" / "config.json"
|
|
491
|
+
try:
|
|
492
|
+
cfg = json.loads(config_path.read_text()) if config_path.exists() else {}
|
|
493
|
+
except (json.JSONDecodeError, OSError):
|
|
494
|
+
return # Config unreadable — silent exit
|
|
495
|
+
|
|
496
|
+
evolution_cfg = cfg.get("evolution", {})
|
|
497
|
+
if not evolution_cfg.get("enabled", False):
|
|
498
|
+
return # Disabled — silent exit
|
|
499
|
+
|
|
500
|
+
# Heavy imports only if enabled (this runs as a Popen child)
|
|
501
|
+
try:
|
|
502
|
+
from superlocalmemory.evolution.skill_evolver import SkillEvolver
|
|
503
|
+
|
|
504
|
+
db_path = Path.home() / ".superlocalmemory" / "memory.db"
|
|
505
|
+
if not db_path.exists():
|
|
506
|
+
return
|
|
507
|
+
|
|
508
|
+
evolver = SkillEvolver(db_path)
|
|
509
|
+
evolver.run_post_session(session_id, profile)
|
|
510
|
+
except Exception:
|
|
511
|
+
pass # Best-effort — don't crash the Stop hook
|
|
512
|
+
|
|
513
|
+
|
|
344
514
|
def cmd_adapters(args: Namespace) -> None:
|
|
345
515
|
"""Manage ingestion adapters (Gmail, Calendar, Transcript).
|
|
346
516
|
|
|
@@ -313,6 +313,26 @@ def main() -> None:
|
|
|
313
313
|
)
|
|
314
314
|
ingest_p.add_argument("--json", action="store_true", help="Output structured JSON")
|
|
315
315
|
|
|
316
|
+
# V3.4.11: Config get/set (dot-notation)
|
|
317
|
+
config_p = sub.add_parser(
|
|
318
|
+
"config",
|
|
319
|
+
help="Get or set config values (e.g. slm config set evolution.enabled true)",
|
|
320
|
+
)
|
|
321
|
+
config_p.add_argument(
|
|
322
|
+
"action", choices=["get", "set"], help="Action: get or set",
|
|
323
|
+
)
|
|
324
|
+
config_p.add_argument("key", help="Config key in dot notation (e.g. evolution.enabled)")
|
|
325
|
+
config_p.add_argument("value", nargs="?", default=None, help="Value to set (for 'set' action)")
|
|
326
|
+
config_p.add_argument("--json", action="store_true", help="Output structured JSON")
|
|
327
|
+
|
|
328
|
+
# V3.4.11: Skill evolution (called from Stop hook, fire-and-forget)
|
|
329
|
+
evolve_p = sub.add_parser(
|
|
330
|
+
"evolve",
|
|
331
|
+
help="Run post-session skill evolution (internal, called by Stop hook)",
|
|
332
|
+
)
|
|
333
|
+
evolve_p.add_argument("--session", default="", help="Session ID to process")
|
|
334
|
+
evolve_p.add_argument("--profile", default="default", help="Profile ID")
|
|
335
|
+
|
|
316
336
|
args = parser.parse_args()
|
|
317
337
|
|
|
318
338
|
if not args.command:
|
|
@@ -328,6 +348,7 @@ def main() -> None:
|
|
|
328
348
|
# Cross-platform: macOS + Windows + Linux.
|
|
329
349
|
_NO_DAEMON_COMMANDS = {
|
|
330
350
|
"setup", "mode", "provider", "connect", "migrate", "mcp", "warmup",
|
|
351
|
+
"config", "evolve",
|
|
331
352
|
}
|
|
332
353
|
if args.command not in _NO_DAEMON_COMMANDS:
|
|
333
354
|
try:
|
|
@@ -265,7 +265,7 @@ def run_wizard(auto: bool = False) -> None:
|
|
|
265
265
|
print()
|
|
266
266
|
|
|
267
267
|
# -- Step 1: System check --
|
|
268
|
-
print("─── Step 1/
|
|
268
|
+
print("─── Step 1/10: System Check ───")
|
|
269
269
|
print()
|
|
270
270
|
py_ver = platform.python_version()
|
|
271
271
|
py_ok = sys.version_info >= (3, 11)
|
|
@@ -293,7 +293,7 @@ def run_wizard(auto: bool = False) -> None:
|
|
|
293
293
|
|
|
294
294
|
# -- Step 2: Mode selection --
|
|
295
295
|
print()
|
|
296
|
-
print("─── Step 2/
|
|
296
|
+
print("─── Step 2/10: Choose Operating Mode ───")
|
|
297
297
|
print()
|
|
298
298
|
print(" [A] Local Guardian (recommended)")
|
|
299
299
|
print(" Zero cloud. Zero LLM. Full privacy.")
|
|
@@ -342,7 +342,7 @@ def run_wizard(auto: bool = False) -> None:
|
|
|
342
342
|
|
|
343
343
|
# -- Step 3: Code Knowledge Graph --
|
|
344
344
|
print()
|
|
345
|
-
print("─── Step 3/
|
|
345
|
+
print("─── Step 3/10: Code Knowledge Graph ───")
|
|
346
346
|
print()
|
|
347
347
|
print(" CodeGraph builds a structural map of your codebase using Tree-sitter.")
|
|
348
348
|
print(" It gives your AI assistant blast-radius analysis, call graphs,")
|
|
@@ -375,7 +375,7 @@ def run_wizard(auto: bool = False) -> None:
|
|
|
375
375
|
|
|
376
376
|
# -- Step 4: Download models --
|
|
377
377
|
print()
|
|
378
|
-
print("─── Step 4/
|
|
378
|
+
print("─── Step 4/10: Download Embedding Model ───")
|
|
379
379
|
|
|
380
380
|
if not st_ok:
|
|
381
381
|
print(" ⚠ Skipped (sentence-transformers not installed)")
|
|
@@ -386,7 +386,7 @@ def run_wizard(auto: bool = False) -> None:
|
|
|
386
386
|
print(" ⚠ Model will download on first use (may take a few minutes)")
|
|
387
387
|
|
|
388
388
|
print()
|
|
389
|
-
print("─── Step 4b/
|
|
389
|
+
print("─── Step 4b/10: Download Reranker Model ───")
|
|
390
390
|
|
|
391
391
|
if not st_ok:
|
|
392
392
|
print(" ⚠ Skipped (sentence-transformers not installed)")
|
|
@@ -395,7 +395,7 @@ def run_wizard(auto: bool = False) -> None:
|
|
|
395
395
|
|
|
396
396
|
# -- Step 5: Daemon Configuration (v3.4.3) --
|
|
397
397
|
print()
|
|
398
|
-
print("─── Step 5/
|
|
398
|
+
print("─── Step 5/10: Daemon Configuration ───")
|
|
399
399
|
print()
|
|
400
400
|
print(" The SLM daemon runs in the background for instant memory access.")
|
|
401
401
|
print()
|
|
@@ -425,7 +425,7 @@ def run_wizard(auto: bool = False) -> None:
|
|
|
425
425
|
|
|
426
426
|
# -- Step 6: Mesh Communication (v3.4.3) --
|
|
427
427
|
print()
|
|
428
|
-
print("─── Step 6/
|
|
428
|
+
print("─── Step 6/10: Mesh Communication ───")
|
|
429
429
|
print()
|
|
430
430
|
print(" SLM Mesh enables agent-to-agent P2P communication.")
|
|
431
431
|
print(" Multiple AI sessions can share knowledge in real-time.")
|
|
@@ -446,7 +446,7 @@ def run_wizard(auto: bool = False) -> None:
|
|
|
446
446
|
|
|
447
447
|
# -- Step 7: Ingestion Adapters (v3.4.3) --
|
|
448
448
|
print()
|
|
449
|
-
print("─── Step 7/
|
|
449
|
+
print("─── Step 7/10: Ingestion Adapters ───")
|
|
450
450
|
print()
|
|
451
451
|
print(" These let SLM learn from your email, calendar, and meetings.")
|
|
452
452
|
print(" All adapters are OFF by default. You can enable them later.")
|
|
@@ -486,7 +486,7 @@ def run_wizard(auto: bool = False) -> None:
|
|
|
486
486
|
|
|
487
487
|
# -- Step 8: Entity Compilation (v3.4.3) --
|
|
488
488
|
print()
|
|
489
|
-
print("─── Step 8/
|
|
489
|
+
print("─── Step 8/10: Entity Compilation ───")
|
|
490
490
|
print()
|
|
491
491
|
print(" Entity compilation builds knowledge summaries per person,")
|
|
492
492
|
print(" project, and concept. Runs automatically during consolidation.")
|
|
@@ -505,9 +505,50 @@ def run_wizard(auto: bool = False) -> None:
|
|
|
505
505
|
config.save()
|
|
506
506
|
print(f"\n ✓ Entity compilation {'enabled' if config.entity_compilation_enabled else 'disabled'}")
|
|
507
507
|
|
|
508
|
-
# -- Step 9:
|
|
508
|
+
# -- Step 9: Skill Evolution (v3.4.11) --
|
|
509
509
|
print()
|
|
510
|
-
print("─── Step 9/
|
|
510
|
+
print("─── Step 9/10: Skill Evolution ───")
|
|
511
|
+
print()
|
|
512
|
+
print(" SLM can automatically evolve skills that underperform.")
|
|
513
|
+
print(" It detects degradation, generates improvements, and verifies them blindly.")
|
|
514
|
+
print(" Requires an LLM backend (Claude CLI, Ollama, or API key).")
|
|
515
|
+
print()
|
|
516
|
+
print(" [Y] Enable Skill Evolution")
|
|
517
|
+
print(" [N] Disable (can enable later: slm config set evolution.enabled true)")
|
|
518
|
+
print()
|
|
519
|
+
|
|
520
|
+
if interactive:
|
|
521
|
+
evo_choice = _prompt(" Enable Skill Evolution? [Y/n] (default: Y): ", "y").lower()
|
|
522
|
+
else:
|
|
523
|
+
evo_choice = "y"
|
|
524
|
+
print(" Auto-enabling Skill Evolution (non-interactive)")
|
|
525
|
+
|
|
526
|
+
evolution_enabled = evo_choice in ("", "y", "yes")
|
|
527
|
+
|
|
528
|
+
# Write evolution config to config.json directly
|
|
529
|
+
# (SLMConfig.save() doesn't serialize evolution)
|
|
530
|
+
_SLM_HOME.mkdir(parents=True, exist_ok=True)
|
|
531
|
+
evo_config_path = _SLM_HOME / "config.json"
|
|
532
|
+
evo_cfg: dict = {}
|
|
533
|
+
if evo_config_path.exists():
|
|
534
|
+
try:
|
|
535
|
+
evo_cfg = json.loads(evo_config_path.read_text())
|
|
536
|
+
except (json.JSONDecodeError, OSError):
|
|
537
|
+
pass
|
|
538
|
+
evo_cfg["evolution"] = {
|
|
539
|
+
"enabled": evolution_enabled,
|
|
540
|
+
"backend": "auto",
|
|
541
|
+
}
|
|
542
|
+
evo_config_path.write_text(json.dumps(evo_cfg, indent=2) + "\n")
|
|
543
|
+
|
|
544
|
+
if evolution_enabled:
|
|
545
|
+
print(f"\n ✓ Skill Evolution enabled (backend: auto-detect)")
|
|
546
|
+
else:
|
|
547
|
+
print(f"\n ✓ Skill Evolution disabled")
|
|
548
|
+
|
|
549
|
+
# -- Step 10: Verification --
|
|
550
|
+
print()
|
|
551
|
+
print("─── Step 10/10: Verification ───")
|
|
511
552
|
|
|
512
553
|
if st_ok:
|
|
513
554
|
verified = _verify_installation()
|
|
@@ -537,6 +578,8 @@ def run_wizard(auto: bool = False) -> None:
|
|
|
537
578
|
print(", Entity Compilation", end="")
|
|
538
579
|
if code_graph_enabled:
|
|
539
580
|
print(", CodeGraph", end="")
|
|
581
|
+
if evolution_enabled:
|
|
582
|
+
print(", Skill Evolution", end="")
|
|
540
583
|
print()
|
|
541
584
|
if enabled_adapters:
|
|
542
585
|
print(f" Adapters: {', '.join(enabled_adapters)}")
|
|
@@ -490,6 +490,25 @@ class TemporalValidatorConfig:
|
|
|
490
490
|
include_expired_in_history: bool = True
|
|
491
491
|
|
|
492
492
|
|
|
493
|
+
@dataclass(frozen=True)
|
|
494
|
+
class EvolutionConfig:
|
|
495
|
+
"""Configuration for Skill Evolution Engine (v3.4.10).
|
|
496
|
+
|
|
497
|
+
OFF by default — opt in via `slm setup` (interactive) or
|
|
498
|
+
`slm config set evolution.enabled true` (CLI).
|
|
499
|
+
|
|
500
|
+
Backend auto-detection priority:
|
|
501
|
+
1. `claude` CLI available → spawn `claude --model haiku` (ECC pattern, free)
|
|
502
|
+
2. Ollama running → use Ollama (free, local)
|
|
503
|
+
3. API key set → use Anthropic/OpenAI API (paid)
|
|
504
|
+
4. Nothing → dashboard-only (show candidates, manual evolution)
|
|
505
|
+
"""
|
|
506
|
+
|
|
507
|
+
enabled: bool = False # OFF by default, opt-in
|
|
508
|
+
backend: str = "auto" # auto, claude, ollama, anthropic, openai
|
|
509
|
+
max_evolutions_per_cycle: int = 3 # Budget cap per consolidation
|
|
510
|
+
|
|
511
|
+
|
|
493
512
|
@dataclass(frozen=True)
|
|
494
513
|
class AutoInvokeConfig:
|
|
495
514
|
"""Configuration for the Auto-Invoke Engine (Phase 2).
|
|
@@ -580,6 +599,7 @@ class SLMConfig:
|
|
|
580
599
|
parameterization: ParameterizationConfig = field(
|
|
581
600
|
default_factory=ParameterizationConfig,
|
|
582
601
|
)
|
|
602
|
+
evolution: EvolutionConfig = field(default_factory=EvolutionConfig)
|
|
583
603
|
|
|
584
604
|
# v3.4.3: Daemon configuration
|
|
585
605
|
daemon_idle_timeout: int = 0 # 0 = 24/7 (no auto-kill). >0 = seconds before auto-kill.
|
|
@@ -657,6 +677,14 @@ class SLMConfig:
|
|
|
657
677
|
)
|
|
658
678
|
config.mesh_enabled = data.get("mesh_enabled", True)
|
|
659
679
|
|
|
680
|
+
# V3.4.10: Evolution config
|
|
681
|
+
evo = data.get("evolution", {})
|
|
682
|
+
if evo:
|
|
683
|
+
config.evolution = EvolutionConfig(**{
|
|
684
|
+
k: v for k, v in evo.items()
|
|
685
|
+
if k in EvolutionConfig.__dataclass_fields__
|
|
686
|
+
})
|
|
687
|
+
|
|
660
688
|
return config
|
|
661
689
|
|
|
662
690
|
def save(self, config_path: Path | None = None) -> None:
|
|
@@ -696,6 +724,13 @@ class SLMConfig:
|
|
|
696
724
|
},
|
|
697
725
|
}
|
|
698
726
|
|
|
727
|
+
# V3.4.11: Persist evolution config (C-CONFIGSAVE fix)
|
|
728
|
+
data["evolution"] = {
|
|
729
|
+
"enabled": self.evolution.enabled,
|
|
730
|
+
"backend": self.evolution.backend,
|
|
731
|
+
"max_evolutions_per_cycle": self.evolution.max_evolutions_per_cycle,
|
|
732
|
+
}
|
|
733
|
+
|
|
699
734
|
# Preserve existing V3.3 config sections that aren't in for_mode()
|
|
700
735
|
for key in ("forgetting", "quantization", "sagq", "embedding_signature", "auto_invoke"):
|
|
701
736
|
if key in existing:
|