superlocalmemory 4.0.9 → 4.1.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/CHANGELOG.md +245 -0
- package/README.md +7 -7
- package/package.json +4 -2
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/CLAUDE.md +3 -3
- package/plugin/agents/slm-governance-advisor.md +1 -1
- package/plugin/agents/slm-loop-runner.md +4 -4
- 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 +2 -2
- package/plugin/skills/slm-mesh/SKILL.md +1 -1
- package/plugin/skills/slm-profile/SKILL.md +5 -5
- package/plugin/skills/slm-recall/SKILL.md +102 -15
- package/plugin/skills/slm-remember/SKILL.md +35 -3
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +29 -3
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/rules/AGENTS.md +16 -8
- 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 +2 -2
- package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
- package/plugin-src/skills/slm-profile/SKILL.md +5 -5
- package/plugin-src/skills/slm-recall/SKILL.md +102 -15
- package/plugin-src/skills/slm-remember/SKILL.md +35 -3
- package/plugin-src/skills/slm-scope/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +29 -3
- 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 +308 -20
- package/src/superlocalmemory/cli/daemon.py +30 -0
- package/src/superlocalmemory/cli/db_migrate.py +71 -1
- package/src/superlocalmemory/cli/gdpr_cmd.py +15 -2
- package/src/superlocalmemory/cli/main.py +26 -4
- package/src/superlocalmemory/code_graph/bridge/maintenance.py +8 -0
- package/src/superlocalmemory/code_graph/database.py +44 -0
- package/src/superlocalmemory/compliance/gdpr.py +449 -39
- package/src/superlocalmemory/core/admission.py +231 -11
- package/src/superlocalmemory/core/backend_orchestrator.py +190 -84
- package/src/superlocalmemory/core/config.py +90 -11
- package/src/superlocalmemory/core/consolidation_engine.py +34 -0
- package/src/superlocalmemory/core/engine.py +140 -11
- package/src/superlocalmemory/core/fact_consolidator.py +316 -125
- package/src/superlocalmemory/core/graph_analyzer.py +76 -112
- package/src/superlocalmemory/core/graph_metrics.py +597 -0
- package/src/superlocalmemory/core/graph_pruner.py +121 -0
- package/src/superlocalmemory/core/maintenance.py +44 -6
- package/src/superlocalmemory/core/maintenance_scheduler.py +205 -0
- package/src/superlocalmemory/core/memory_health.py +266 -0
- package/src/superlocalmemory/core/mode_capability.py +111 -0
- package/src/superlocalmemory/core/ollama_validator.py +315 -0
- package/src/superlocalmemory/core/operation_policy_registry.py +1 -1
- package/src/superlocalmemory/core/operation_request.py +1 -1
- package/src/superlocalmemory/core/ops_remediation.py +2 -2
- package/src/superlocalmemory/core/projection_drain.py +380 -0
- package/src/superlocalmemory/core/recall_pipeline.py +390 -3
- package/src/superlocalmemory/core/recall_worker.py +6 -3
- package/src/superlocalmemory/core/scale_autopromote.py +196 -0
- package/src/superlocalmemory/core/scale_engine.py +16 -2
- package/src/superlocalmemory/core/score_contract.py +21 -1
- package/src/superlocalmemory/core/session_identity.py +85 -0
- package/src/superlocalmemory/core/status_contract.py +108 -0
- package/src/superlocalmemory/core/store_pipeline.py +78 -3
- package/src/superlocalmemory/core/worker_pool.py +4 -4
- package/src/superlocalmemory/core/working_memory.py +288 -0
- package/src/superlocalmemory/encoding/cognitive_consolidator.py +51 -7
- package/src/superlocalmemory/encoding/context_generator.py +1 -1
- package/src/superlocalmemory/encoding/entity_resolver.py +38 -0
- package/src/superlocalmemory/encoding/fact_extractor.py +18 -14
- package/src/superlocalmemory/encoding/prospective_markers.py +262 -0
- package/src/superlocalmemory/encoding/type_router.py +12 -12
- package/src/superlocalmemory/evolution/mutation_generator.py +30 -4
- package/src/superlocalmemory/graph/cozo_adjacency.py +122 -0
- package/src/superlocalmemory/graph/cozo_backend.py +103 -138
- package/src/superlocalmemory/hooks/portable_kit.py +10 -2
- package/src/superlocalmemory/learning/bandit.py +43 -0
- package/src/superlocalmemory/learning/consolidation_worker.py +54 -0
- package/src/superlocalmemory/learning/database.py +60 -3
- package/src/superlocalmemory/learning/entity_compiler.py +21 -58
- package/src/superlocalmemory/learning/feedback.py +3 -1
- package/src/superlocalmemory/learning/outcomes.py +47 -16
- package/src/superlocalmemory/learning/pattern_miner.py +28 -3
- package/src/superlocalmemory/learning/pattern_miner_constants.py +43 -0
- package/src/superlocalmemory/learning/pcos.py +291 -0
- package/src/superlocalmemory/learning/reward_from_outcomes.py +365 -0
- package/src/superlocalmemory/learning/reward_proxy.py +100 -10
- package/src/superlocalmemory/learning/signal_kinds.py +79 -0
- package/src/superlocalmemory/mcp/profiles.py +14 -2
- package/src/superlocalmemory/mcp/server.py +1 -1
- package/src/superlocalmemory/mcp/session_binding.py +92 -0
- package/src/superlocalmemory/mcp/tools_active.py +2 -1
- package/src/superlocalmemory/mcp/tools_core.py +71 -42
- package/src/superlocalmemory/mcp/tools_ops.py +2 -2
- package/src/superlocalmemory/mcp/tools_v28.py +20 -1
- package/src/superlocalmemory/parameterization/pattern_extractor.py +14 -1
- package/src/superlocalmemory/parameterization/soft_prompt_generator.py +98 -0
- package/src/superlocalmemory/retrieval/bm25_channel.py +68 -11
- package/src/superlocalmemory/retrieval/channel_status.py +117 -0
- package/src/superlocalmemory/retrieval/engine.py +106 -11
- package/src/superlocalmemory/retrieval/entity_channel.py +217 -257
- package/src/superlocalmemory/retrieval/graph_adjacency.py +219 -0
- package/src/superlocalmemory/retrieval/scope_policy.py +42 -1
- package/src/superlocalmemory/retrieval/semantic_channel.py +47 -5
- package/src/superlocalmemory/retrieval/spreading.py +288 -0
- package/src/superlocalmemory/retrieval/temporal_channel.py +13 -1
- package/src/superlocalmemory/retrieval/vector_store.py +63 -0
- package/src/superlocalmemory/server/api.py +26 -2
- package/src/superlocalmemory/server/asset_versions.py +171 -0
- package/src/superlocalmemory/server/bandit_loops.py +17 -1
- package/src/superlocalmemory/server/rbac_enforce.py +26 -6
- package/src/superlocalmemory/server/recall_serializer.py +9 -0
- package/src/superlocalmemory/server/routes/abstraction.py +201 -0
- package/src/superlocalmemory/server/routes/behavioral.py +75 -10
- package/src/superlocalmemory/server/routes/compliance.py +98 -18
- package/src/superlocalmemory/server/routes/config_api.py +186 -4
- package/src/superlocalmemory/server/routes/data_io.py +29 -1
- package/src/superlocalmemory/server/routes/entity.py +13 -1
- package/src/superlocalmemory/server/routes/evolution.py +178 -0
- package/src/superlocalmemory/server/routes/ingest.py +8 -0
- package/src/superlocalmemory/server/routes/learning_telemetry.py +2 -1
- package/src/superlocalmemory/server/routes/memories.py +49 -7
- package/src/superlocalmemory/server/routes/mesh.py +1 -1
- package/src/superlocalmemory/server/routes/timeline.py +4 -0
- package/src/superlocalmemory/server/routes/v3_api.py +193 -17
- package/src/superlocalmemory/server/ui.py +24 -1
- package/src/superlocalmemory/server/unified_daemon.py +292 -9
- package/src/superlocalmemory/storage/_migration_internals.py +35 -0
- package/src/superlocalmemory/storage/_schema_version.py +24 -3
- package/src/superlocalmemory/storage/database.py +598 -82
- package/src/superlocalmemory/storage/embedding_codec.py +71 -0
- package/src/superlocalmemory/storage/lineage_retention.py +236 -0
- package/src/superlocalmemory/storage/logical_edges.py +43 -2
- package/src/superlocalmemory/storage/migration_runner.py +130 -0
- package/src/superlocalmemory/storage/migrations/M043_quarantine_display_summaries.py +488 -0
- package/src/superlocalmemory/storage/migrations/M044_play_carries_its_own_evidence.py +127 -0
- package/src/superlocalmemory/storage/migrations/M045_fact_outcome_score.py +158 -0
- package/src/superlocalmemory/storage/migrations/M046_prospective_memory_has_its_own_name.py +620 -0
- package/src/superlocalmemory/storage/migrations/M047_fisher_vectors_are_stored_like_every_other_vector.py +306 -0
- package/src/superlocalmemory/storage/migrations/M048_upcoming_holds_only_what_is_upcoming.py +207 -0
- package/src/superlocalmemory/storage/migrations/M049_a_schema_version_marker_is_one_row.py +201 -0
- package/src/superlocalmemory/storage/migrations.py +18 -2
- package/src/superlocalmemory/storage/models.py +40 -1
- package/src/superlocalmemory/storage/projection_outbox.py +346 -0
- package/src/superlocalmemory/storage/retention_policy.py +860 -0
- package/src/superlocalmemory/storage/schema.py +110 -1
- package/src/superlocalmemory/storage/write_coordinator.py +19 -2
- package/src/superlocalmemory/summaries/base.py +1 -1
- package/src/superlocalmemory/summaries/non_answer.py +223 -0
- package/src/superlocalmemory/trust/scorer.py +43 -1
- package/src/superlocalmemory/ui/index.html +10 -19
- package/src/superlocalmemory/ui/js/event-delegation.js +12 -1
- package/src/superlocalmemory/ui/js/od-health.js +28 -6
- package/src/superlocalmemory/ui/js/od-memories.js +209 -1
- package/src/superlocalmemory/ui/js/od-ops-health.js +1 -1
- package/src/superlocalmemory/ui/js/od-settings.js +87 -1
- package/src/superlocalmemory/ui/js/recall-lab.js +78 -3
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under AGPL-3.0-or-later - see LICENSE file
|
|
3
|
+
|
|
4
|
+
"""M045 — per-fact outcome score (PCOS), so ranking can use what worked.
|
|
5
|
+
|
|
6
|
+
WHY
|
|
7
|
+
---
|
|
8
|
+
Ranking scores a memory by how much it *looks like* the query. Nothing in the
|
|
9
|
+
pipeline knows whether a memory has ever actually helped. This table holds one
|
|
10
|
+
number per (fact, profile): an exponentially-weighted average of the rewards of
|
|
11
|
+
the settlements it took part in.
|
|
12
|
+
|
|
13
|
+
WHY IT LIVES IN memory.db
|
|
14
|
+
-------------------------
|
|
15
|
+
Beside ``atomic_facts`` and ``action_outcomes``, the backfill source, so the
|
|
16
|
+
whole thing stays in one file. SQLite cannot join across databases without
|
|
17
|
+
ATTACH, and taking an ATTACH on the recall path to reach a single REAL would
|
|
18
|
+
cost more than the number is worth.
|
|
19
|
+
|
|
20
|
+
The ranker reads it with one batched keyed lookup rather than a LEFT JOIN into
|
|
21
|
+
fact hydration: ``_row_to_fact`` builds ``AtomicFact`` from named columns and
|
|
22
|
+
silently drops any it does not declare, so a JOIN would mean adding fields to a
|
|
23
|
+
model used across the whole codebase to carry a value only the ranker reads.
|
|
24
|
+
|
|
25
|
+
profile_id IS PART OF THE PRIMARY KEY, AND THAT IS NOT COSMETIC
|
|
26
|
+
---------------------------------------------------------------
|
|
27
|
+
A learned per-fact score is derived personal data. If it had no ``profile_id``,
|
|
28
|
+
``forget_profile()`` would run, report success, and leave the erased user's
|
|
29
|
+
learned outcome history behind — which is the same defect already
|
|
30
|
+
known in ``fact_expansion_fts``. Creating a second table with the same defect
|
|
31
|
+
in the same release would be indefensible. ``compliance/gdpr.py`` deletes from
|
|
32
|
+
this table; a test asserts one profile's erasure leaves another's rows intact.
|
|
33
|
+
|
|
34
|
+
THE BACKFILL, AND A CORRECTION TO THE PLAN
|
|
35
|
+
------------------------------------------
|
|
36
|
+
An earlier design's backfill read ``SELECT fact_id, profile_id, AVG(reward) FROM
|
|
37
|
+
action_outcomes GROUP BY fact_id, profile_id``. **There is no ``fact_id`` column
|
|
38
|
+
on that table** — it stores ``fact_ids_json``, a JSON array, because one outcome
|
|
39
|
+
covers the set of memories an answer used. So the backfill expands the array with
|
|
40
|
+
``json_each``, guarded by ``json_valid`` (an unguarded ``json_each`` raises on the
|
|
41
|
+
first malformed row and takes the whole migration with it).
|
|
42
|
+
|
|
43
|
+
On a live store this initialises every touched fact at 0.5 from 162 rows
|
|
44
|
+
that are all 0.5 — which is the neutral prior, so the backfill adds no bias. Its
|
|
45
|
+
only job is to avoid a cold start looking like a penalty.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
|
|
50
|
+
import sqlite3
|
|
51
|
+
|
|
52
|
+
NAME = "M045_fact_outcome_score"
|
|
53
|
+
DB_TARGET = "memory"
|
|
54
|
+
|
|
55
|
+
_TABLE = "fact_outcome_score"
|
|
56
|
+
|
|
57
|
+
DDL = """
|
|
58
|
+
BEGIN IMMEDIATE;
|
|
59
|
+
CREATE TABLE IF NOT EXISTS fact_outcome_score (
|
|
60
|
+
fact_id TEXT NOT NULL,
|
|
61
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
62
|
+
score REAL NOT NULL DEFAULT 0.5,
|
|
63
|
+
play_count INTEGER NOT NULL DEFAULT 0,
|
|
64
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
65
|
+
PRIMARY KEY (fact_id, profile_id)
|
|
66
|
+
);
|
|
67
|
+
CREATE INDEX IF NOT EXISTS idx_fos_profile
|
|
68
|
+
ON fact_outcome_score (profile_id, fact_id);
|
|
69
|
+
COMMIT;
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
#: Expand ``fact_ids_json`` and average the reward per (fact, profile).
|
|
73
|
+
#:
|
|
74
|
+
#: ``INSERT OR IGNORE`` names no conflict target on purpose: the table has one
|
|
75
|
+
#: unique constraint today, and a targeted ``ON CONFLICT`` silently stops
|
|
76
|
+
#: covering a row the moment a second constraint is added.
|
|
77
|
+
_BACKFILL = """
|
|
78
|
+
INSERT OR IGNORE INTO fact_outcome_score
|
|
79
|
+
(fact_id, profile_id, score, play_count, updated_at)
|
|
80
|
+
SELECT
|
|
81
|
+
j.value AS fact_id,
|
|
82
|
+
o.profile_id,
|
|
83
|
+
AVG(o.reward),
|
|
84
|
+
COUNT(*),
|
|
85
|
+
datetime('now')
|
|
86
|
+
FROM action_outcomes AS o, json_each(o.fact_ids_json) AS j
|
|
87
|
+
WHERE o.reward IS NOT NULL
|
|
88
|
+
AND json_valid(o.fact_ids_json)
|
|
89
|
+
AND o.fact_ids_json IS NOT NULL
|
|
90
|
+
AND j.value IS NOT NULL AND j.value != ''
|
|
91
|
+
GROUP BY j.value, o.profile_id
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _table_exists(conn: sqlite3.Connection, name: str) -> bool:
|
|
96
|
+
try:
|
|
97
|
+
return conn.execute(
|
|
98
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
|
|
99
|
+
(name,),
|
|
100
|
+
).fetchone() is not None
|
|
101
|
+
except sqlite3.Error:
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _columns(conn: sqlite3.Connection, table: str) -> set[str]:
|
|
106
|
+
try:
|
|
107
|
+
return {row[1] for row in conn.execute(f"PRAGMA table_info({table})")}
|
|
108
|
+
except sqlite3.Error:
|
|
109
|
+
return set()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def verify(conn: sqlite3.Connection) -> bool:
|
|
113
|
+
"""End state: the table exists with profile_id in it.
|
|
114
|
+
|
|
115
|
+
Deliberately does NOT assert anything about backfilled rows. The backfill
|
|
116
|
+
depends on ``action_outcomes``, which is bootstrapped at engine init and may
|
|
117
|
+
hold nothing at all — so asserting rows would make ``verify`` fail forever
|
|
118
|
+
on a fresh install. Everything asserted here is produced by every path
|
|
119
|
+
through ``apply``; that is the M043 lesson, learned the hard way.
|
|
120
|
+
"""
|
|
121
|
+
if not _table_exists(conn, _TABLE):
|
|
122
|
+
return False
|
|
123
|
+
cols = _columns(conn, _TABLE)
|
|
124
|
+
return {"fact_id", "profile_id", "score", "play_count"} <= cols
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def apply(conn: sqlite3.Connection) -> None:
|
|
128
|
+
"""Create the table, then backfill from reported outcomes if any exist."""
|
|
129
|
+
conn.executescript(DDL)
|
|
130
|
+
if not verify(conn): # pragma: no cover — defensive
|
|
131
|
+
raise sqlite3.OperationalError(
|
|
132
|
+
"M045 fact_outcome_score did not reach its end-state"
|
|
133
|
+
)
|
|
134
|
+
_backfill(conn)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _backfill(conn: sqlite3.Connection) -> int:
|
|
138
|
+
"""Seed from ``action_outcomes``. Never fatal — the table is what matters.
|
|
139
|
+
|
|
140
|
+
A failure here means facts start at the cold-start default of 0.5, which is
|
|
141
|
+
the same value the backfill would have written on this store anyway. Losing
|
|
142
|
+
the migration over that would be the wrong trade.
|
|
143
|
+
"""
|
|
144
|
+
if not _table_exists(conn, "action_outcomes"):
|
|
145
|
+
return 0
|
|
146
|
+
if "reward" not in _columns(conn, "action_outcomes"):
|
|
147
|
+
# reward arrives with M006, which is deferred. Nothing to average yet.
|
|
148
|
+
return 0
|
|
149
|
+
try:
|
|
150
|
+
cur = conn.execute(_BACKFILL)
|
|
151
|
+
return int(cur.rowcount or 0)
|
|
152
|
+
except sqlite3.Error:
|
|
153
|
+
return 0
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def repair(conn: sqlite3.Connection) -> None:
|
|
157
|
+
"""Re-run apply. Safe: the DDL is IF NOT EXISTS and the backfill IGNOREs."""
|
|
158
|
+
apply(conn)
|