superlocalmemory 3.8.13 → 4.0.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.
Files changed (212) hide show
  1. package/ATTRIBUTION.md +4 -4
  2. package/CHANGELOG.md +113 -121
  3. package/README.md +65 -63
  4. package/docs/pi-dev-integration.md +1 -1
  5. package/package.json +6 -1
  6. package/plugin/.claude-plugin/plugin.json +1 -1
  7. package/plugin/CLAUDE.md +3 -3
  8. package/plugin/agents/slm-governance-advisor.md +1 -1
  9. package/plugin/agents/slm-loop-runner.md +1 -1
  10. package/plugin/agents/slm-memory-advisor.md +1 -1
  11. package/plugin/agents/slm-optimize-advisor.md +1 -1
  12. package/plugin/requirements.txt +1 -1
  13. package/plugin/skills/slm-cache/SKILL.md +1 -1
  14. package/plugin/skills/slm-compress/SKILL.md +1 -1
  15. package/plugin/skills/slm-governance/SKILL.md +1 -1
  16. package/plugin/skills/slm-graph/SKILL.md +1 -1
  17. package/plugin/skills/slm-loop/SKILL.md +1 -1
  18. package/plugin/skills/slm-mesh/SKILL.md +1 -1
  19. package/plugin/skills/slm-profile/SKILL.md +1 -1
  20. package/plugin/skills/slm-recall/SKILL.md +1 -1
  21. package/plugin/skills/slm-remember/SKILL.md +1 -1
  22. package/plugin/skills/slm-scope/SKILL.md +1 -1
  23. package/plugin/skills/slm-session/SKILL.md +1 -1
  24. package/plugin/skills/slm-status/SKILL.md +1 -1
  25. package/plugin-src/rules/AGENTS.md +1 -1
  26. package/plugin-src/skills/slm-cache/SKILL.md +1 -1
  27. package/plugin-src/skills/slm-compress/SKILL.md +1 -1
  28. package/plugin-src/skills/slm-governance/SKILL.md +248 -0
  29. package/plugin-src/skills/slm-graph/SKILL.md +1 -1
  30. package/plugin-src/skills/slm-loop/SKILL.md +99 -0
  31. package/plugin-src/skills/slm-mesh/SKILL.md +282 -0
  32. package/plugin-src/skills/slm-profile/SKILL.md +148 -0
  33. package/plugin-src/skills/slm-recall/SKILL.md +1 -1
  34. package/plugin-src/skills/slm-remember/SKILL.md +1 -1
  35. package/plugin-src/skills/slm-scope/SKILL.md +176 -0
  36. package/plugin-src/skills/slm-session/SKILL.md +1 -1
  37. package/plugin-src/skills/slm-status/SKILL.md +1 -1
  38. package/pyproject.toml +11 -4
  39. package/src/superlocalmemory/__init__.py +1 -1
  40. package/src/superlocalmemory/cli/commands.py +125 -11
  41. package/src/superlocalmemory/cli/daemon.py +5 -1
  42. package/src/superlocalmemory/cli/main.py +35 -2
  43. package/src/superlocalmemory/cli/ops_cmd.py +281 -0
  44. package/src/superlocalmemory/cli/setup_wizard.py +1 -1
  45. package/src/superlocalmemory/compliance/audit.py +65 -0
  46. package/src/superlocalmemory/compliance/eu_ai_act.py +27 -57
  47. package/src/superlocalmemory/compliance/gdpr.py +416 -20
  48. package/src/superlocalmemory/compliance/retention.py +74 -22
  49. package/src/superlocalmemory/compliance/scheduler.py +78 -9
  50. package/src/superlocalmemory/core/actor_context.py +166 -0
  51. package/src/superlocalmemory/core/admission.py +549 -0
  52. package/src/superlocalmemory/core/backend_orchestrator.py +23 -10
  53. package/src/superlocalmemory/core/config.py +202 -24
  54. package/src/superlocalmemory/core/consolidation_engine.py +13 -13
  55. package/src/superlocalmemory/core/context_cache.py +28 -0
  56. package/src/superlocalmemory/core/embeddings.py +64 -2
  57. package/src/superlocalmemory/core/engine.py +7 -2
  58. package/src/superlocalmemory/core/engine_ingestion.py +65 -3
  59. package/src/superlocalmemory/core/engine_wiring.py +36 -9
  60. package/src/superlocalmemory/core/ingest_policy.py +38 -0
  61. package/src/superlocalmemory/core/maintenance.py +255 -0
  62. package/src/superlocalmemory/core/modes.py +40 -13
  63. package/src/superlocalmemory/core/mutations.py +437 -44
  64. package/src/superlocalmemory/core/operation_policy.py +92 -0
  65. package/src/superlocalmemory/core/operation_policy_registry.py +542 -0
  66. package/src/superlocalmemory/core/operation_request.py +127 -0
  67. package/src/superlocalmemory/core/ops_remediation.py +542 -0
  68. package/src/superlocalmemory/core/recall_pipeline.py +7 -0
  69. package/src/superlocalmemory/core/remember_runtime.py +202 -4
  70. package/src/superlocalmemory/core/remote_mode.py +20 -5
  71. package/src/superlocalmemory/core/store_pipeline.py +150 -0
  72. package/src/superlocalmemory/core/topic_signature.py +19 -4
  73. package/src/superlocalmemory/core/transactions/__init__.py +78 -0
  74. package/src/superlocalmemory/core/transactions/concrete_owners.py +597 -0
  75. package/src/superlocalmemory/core/transactions/erasure.py +825 -0
  76. package/src/superlocalmemory/core/transactions/manifest.py +255 -0
  77. package/src/superlocalmemory/core/transactions/manifest_key.py +155 -0
  78. package/src/superlocalmemory/core/transactions/obligations.py +272 -0
  79. package/src/superlocalmemory/core/transactions/owners.py +114 -0
  80. package/src/superlocalmemory/core/transactions/reconciler.py +285 -0
  81. package/src/superlocalmemory/core/transactions/service.py +330 -0
  82. package/src/superlocalmemory/core/worker_pool.py +33 -5
  83. package/src/superlocalmemory/encoding/cognitive_consolidator.py +70 -28
  84. package/src/superlocalmemory/encoding/emotional.py +75 -14
  85. package/src/superlocalmemory/encoding/scene_builder.py +115 -13
  86. package/src/superlocalmemory/encoding/temporal_parser.py +4 -0
  87. package/src/superlocalmemory/evolution/blind_verifier.py +11 -4
  88. package/src/superlocalmemory/evolution/evolution_store.py +244 -4
  89. package/src/superlocalmemory/evolution/llm_dispatch.py +40 -0
  90. package/src/superlocalmemory/evolution/model_selection.py +18 -3
  91. package/src/superlocalmemory/evolution/mutation_generator.py +3 -0
  92. package/src/superlocalmemory/evolution/skill_activator.py +270 -0
  93. package/src/superlocalmemory/evolution/skill_evolver.py +281 -59
  94. package/src/superlocalmemory/evolution/types.py +30 -8
  95. package/src/superlocalmemory/graph/cozo_backend.py +17 -9
  96. package/src/superlocalmemory/hooks/auto_invoker.py +2 -1
  97. package/src/superlocalmemory/hooks/auto_recall.py +64 -30
  98. package/src/superlocalmemory/hooks/codex_assets.py +14 -1
  99. package/src/superlocalmemory/infra/backup.py +434 -7
  100. package/src/superlocalmemory/infra/process_reaper.py +18 -0
  101. package/src/superlocalmemory/infra/self_heal.py +401 -0
  102. package/src/superlocalmemory/learning/feedback.py +52 -9
  103. package/src/superlocalmemory/loops/engine.py +10 -0
  104. package/src/superlocalmemory/mcp/_daemon_proxy.py +3 -0
  105. package/src/superlocalmemory/mcp/http_transport.py +30 -331
  106. package/src/superlocalmemory/mcp/profiles.py +5 -0
  107. package/src/superlocalmemory/mcp/resources.py +8 -0
  108. package/src/superlocalmemory/mcp/server.py +51 -4
  109. package/src/superlocalmemory/mcp/shared.py +19 -0
  110. package/src/superlocalmemory/mcp/tools_active.py +25 -4
  111. package/src/superlocalmemory/mcp/tools_code_graph.py +26 -18
  112. package/src/superlocalmemory/mcp/tools_context.py +50 -8
  113. package/src/superlocalmemory/mcp/tools_core.py +69 -21
  114. package/src/superlocalmemory/mcp/tools_evolution.py +9 -2
  115. package/src/superlocalmemory/mcp/tools_learning.py +21 -10
  116. package/src/superlocalmemory/mcp/tools_loops.py +29 -18
  117. package/src/superlocalmemory/mcp/tools_mesh.py +8 -0
  118. package/src/superlocalmemory/mcp/tools_ops.py +115 -0
  119. package/src/superlocalmemory/mcp/tools_optimize.py +4 -0
  120. package/src/superlocalmemory/mcp/tools_v28.py +10 -3
  121. package/src/superlocalmemory/mcp/tools_v3.py +34 -14
  122. package/src/superlocalmemory/mcp/tools_v33.py +18 -33
  123. package/src/superlocalmemory/mesh/broker.py +124 -46
  124. package/src/superlocalmemory/mesh/broker_security.py +470 -0
  125. package/src/superlocalmemory/mesh/discovery.py +365 -0
  126. package/src/superlocalmemory/mesh/lock_protocol.py +313 -0
  127. package/src/superlocalmemory/mesh/node_identity.py +97 -0
  128. package/src/superlocalmemory/mesh/outbox_remote.py +429 -0
  129. package/src/superlocalmemory/mesh/remote_sync.py +511 -28
  130. package/src/superlocalmemory/mesh/state_sync.py +286 -0
  131. package/src/superlocalmemory/optimize/config/store.py +45 -0
  132. package/src/superlocalmemory/parameterization/cross_project.py +12 -0
  133. package/src/superlocalmemory/parameterization/prompt_injector.py +13 -11
  134. package/src/superlocalmemory/parameterization/prompt_lifecycle.py +8 -2
  135. package/src/superlocalmemory/parameterization/workflow_miner.py +17 -0
  136. package/src/superlocalmemory/retrieval/ann_index.py +5 -0
  137. package/src/superlocalmemory/retrieval/bm25_channel.py +49 -2
  138. package/src/superlocalmemory/retrieval/engine.py +19 -4
  139. package/src/superlocalmemory/retrieval/fusion.py +4 -1
  140. package/src/superlocalmemory/retrieval/hopfield_channel.py +9 -3
  141. package/src/superlocalmemory/retrieval/remote_reranker.py +47 -22
  142. package/src/superlocalmemory/retrieval/reranker.py +32 -1
  143. package/src/superlocalmemory/retrieval/temporal_channel.py +16 -3
  144. package/src/superlocalmemory/retrieval/temporal_utils.py +107 -0
  145. package/src/superlocalmemory/retrieval/temporal_validity_filter.py +155 -42
  146. package/src/superlocalmemory/retrieval/vector_store.py +214 -8
  147. package/src/superlocalmemory/server/api.py +5 -5
  148. package/src/superlocalmemory/server/egress_policy.py +258 -0
  149. package/src/superlocalmemory/server/rbac_enforce.py +32 -0
  150. package/src/superlocalmemory/server/route_mutations.py +20 -0
  151. package/src/superlocalmemory/server/routes/compliance.py +153 -7
  152. package/src/superlocalmemory/server/routes/data_io.py +43 -2
  153. package/src/superlocalmemory/server/routes/events.py +15 -0
  154. package/src/superlocalmemory/server/routes/memories.py +56 -3
  155. package/src/superlocalmemory/server/routes/mesh.py +82 -1
  156. package/src/superlocalmemory/server/routes/mesh_lock.py +54 -0
  157. package/src/superlocalmemory/server/routes/mesh_state.py +63 -0
  158. package/src/superlocalmemory/server/routes/v3_api.py +50 -27
  159. package/src/superlocalmemory/server/routes/ws.py +86 -0
  160. package/src/superlocalmemory/server/ui.py +6 -6
  161. package/src/superlocalmemory/server/unified_daemon.py +942 -119
  162. package/src/superlocalmemory/storage/_migration_internals.py +568 -0
  163. package/src/superlocalmemory/storage/_schema_version.py +110 -0
  164. package/src/superlocalmemory/storage/database.py +329 -24
  165. package/src/superlocalmemory/storage/embedding_migrator.py +246 -51
  166. package/src/superlocalmemory/storage/erasure_fence.py +45 -0
  167. package/src/superlocalmemory/storage/generation_fence.py +63 -0
  168. package/src/superlocalmemory/storage/migration_runner.py +140 -417
  169. package/src/superlocalmemory/storage/migrations/M009_model_lineage.py +40 -0
  170. package/src/superlocalmemory/storage/migrations/M033_projection_transactions.py +148 -0
  171. package/src/superlocalmemory/storage/migrations/M034_obligation_integrity.py +58 -0
  172. package/src/superlocalmemory/storage/migrations/M035_erasure_receipts.py +113 -0
  173. package/src/superlocalmemory/storage/migrations/M036_vector_row_map.py +107 -0
  174. package/src/superlocalmemory/storage/migrations/M037_manifest_hmac_version.py +162 -0
  175. package/src/superlocalmemory/storage/migrations/{M033_learning_feedback_channel.py → M038_learning_feedback_channel.py} +3 -3
  176. package/src/superlocalmemory/storage/migrations/M039_scene_fact_members.py +137 -0
  177. package/src/superlocalmemory/storage/migrations/__init__.py +4 -2
  178. package/src/superlocalmemory/storage/schema.py +67 -0
  179. package/src/superlocalmemory/storage/write_coordinator.py +125 -0
  180. package/src/superlocalmemory/trust/scorer.py +28 -4
  181. package/src/superlocalmemory/ui/index.html +14 -3
  182. package/src/superlocalmemory/ui/js/auto-settings.js +12 -1
  183. package/src/superlocalmemory/ui/js/brain.js +6 -4
  184. package/src/superlocalmemory/ui/js/compliance.js +66 -12
  185. package/src/superlocalmemory/ui/js/dashboard.js +13 -3
  186. package/src/superlocalmemory/ui/js/feedback.js +8 -2
  187. package/src/superlocalmemory/ui/js/lifecycle.js +7 -1
  188. package/src/superlocalmemory/ui/js/modal.js +272 -5
  189. package/src/superlocalmemory/ui/js/od-backup.js +9 -2
  190. package/src/superlocalmemory/ui/js/od-compliance-ext.js +301 -0
  191. package/src/superlocalmemory/ui/js/od-operations.js +154 -23
  192. package/src/superlocalmemory/ui/js/od-ops-health.js +417 -0
  193. package/src/superlocalmemory/ui/js/od-optimize.js +35 -21
  194. package/src/superlocalmemory/ui/js/od-team.js +9 -2
  195. package/src/superlocalmemory/ui/js/optimize.js +13 -16
  196. package/src/superlocalmemory/ui/js/profiles.js +7 -3
  197. package/src/superlocalmemory/ui/js/settings.js +7 -1
  198. package/src/superlocalmemory/vector/lancedb_backend.py +19 -9
  199. package/src/superlocalmemory/attribution/mathematical_dna.py +0 -235
  200. package/src/superlocalmemory/cli/post_install.py +0 -114
  201. package/src/superlocalmemory/core/clock_monitor.py +0 -45
  202. package/src/superlocalmemory/core/db_pool.py +0 -80
  203. package/src/superlocalmemory/core/error_catalog.py +0 -113
  204. package/src/superlocalmemory/core/loop_watchdog.py +0 -56
  205. package/src/superlocalmemory/core/priority_queue.py +0 -61
  206. package/src/superlocalmemory/core/pruning_engine.py +0 -216
  207. package/src/superlocalmemory/core/queue_dispatcher.py +0 -73
  208. package/src/superlocalmemory/core/slmignore.py +0 -125
  209. package/src/superlocalmemory/infra/heartbeat_monitor.py +0 -140
  210. package/src/superlocalmemory/infra/webhook_dispatcher.py +0 -247
  211. package/src/superlocalmemory/learning/quantization_scheduler.py +0 -320
  212. package/src/superlocalmemory/storage/access_control.py +0 -182
@@ -1,235 +0,0 @@
1
- # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under AGPL-3.0-or-later - see LICENSE file
3
- # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
-
5
- """Self-enforcing inter-layer parameter constraints.
6
-
7
- The mathematical layers have parameters that are coupled through
8
- a constraint derived from a private seed. If anyone modifies the
9
- parameters without knowing the constraint, performance degrades.
10
-
11
- Constraint: alpha * kappa = C * beta
12
- Where:
13
- alpha = metric scaling parameter
14
- kappa = curvature parameter
15
- beta = inverse temperature
16
- C = derived from private HMAC key
17
-
18
- If the constraint is violated:
19
- - Similarity calibration drifts
20
- - Hierarchical distance fidelity drops
21
- - Energy landscape flattens
22
- - Overall retrieval quality drops 15-20%
23
-
24
- Part of Qualixar | Author: Varun Pratap Bhardwaj
25
- License: AGPL-3.0-or-later
26
- """
27
-
28
- from __future__ import annotations
29
-
30
- import hashlib
31
- import hmac
32
- import os
33
-
34
- import numpy as np
35
-
36
- # Hopfield inverse temperature (legacy constant, kept for DNA compatibility)
37
- HOPFIELD_INVERSE_TEMP = 4.0
38
-
39
- # Default seed — production should use env var SLM_DNA_SEED
40
- _DNA_SEED = "qualixar-slm-alpha-dna-v1"
41
-
42
-
43
- class MathematicalDNA:
44
- """Self-enforcing parameter constraint system.
45
-
46
- Derives a coupling constant from a private seed and provides
47
- utilities to:
48
- - Get a parameter set satisfying the constraint.
49
- - Verify whether arbitrary parameters satisfy it.
50
- - Compute an integrity score (1.0 = perfect).
51
- - Embed / detect computation fingerprints at the precision level.
52
-
53
- Args:
54
- seed: Override the DNA seed. If ``None``, falls back to
55
- ``SLM_DNA_SEED`` env var, then the built-in default.
56
- """
57
-
58
- def __init__(self, seed: str | None = None) -> None:
59
- self._seed = seed or os.environ.get("SLM_DNA_SEED", _DNA_SEED)
60
- self._constraint_C = self._derive_constraint()
61
-
62
- # ── Constraint derivation ─────────────────────────────────────
63
-
64
- def _derive_constraint(self) -> float:
65
- """Derive the inter-layer coupling constant from seed.
66
-
67
- Uses HMAC-SHA256 to produce a deterministic float in [0.5, 2.0].
68
-
69
- Returns:
70
- Coupling constant C.
71
- """
72
- h = hmac.new(
73
- self._seed.encode("utf-8"),
74
- b"layer-coupling",
75
- hashlib.sha256,
76
- )
77
- hash_int = int(h.hexdigest()[:8], 16)
78
- return 0.5 + (hash_int / 0xFFFFFFFF) * 1.5
79
-
80
- # ── Parameter generation ──────────────────────────────────────
81
-
82
- def get_coupled_parameters(self) -> dict[str, float | bool]:
83
- """Get a parameter set satisfying the constraint.
84
-
85
- Returns:
86
- Dict with ``fisher_alpha``, ``poincare_kappa``,
87
- ``hopfield_beta``, ``constraint_C``, and
88
- ``constraint_satisfied`` (always ``True``).
89
- """
90
- beta = HOPFIELD_INVERSE_TEMP
91
- target_product = self._constraint_C * beta
92
-
93
- kappa = abs(self._constraint_C)
94
- alpha = target_product / kappa if kappa > 1e-10 else 1.0
95
-
96
- return {
97
- "fisher_alpha": float(alpha),
98
- "poincare_kappa": float(kappa),
99
- "hopfield_beta": float(beta),
100
- "constraint_C": float(self._constraint_C),
101
- "constraint_satisfied": True,
102
- }
103
-
104
- # ── Constraint verification ───────────────────────────────────
105
-
106
- def verify_constraint(
107
- self,
108
- alpha: float,
109
- kappa: float,
110
- beta: float,
111
- tolerance: float = 0.01,
112
- ) -> bool:
113
- """Check if parameters satisfy alpha * kappa ≈ C * beta.
114
-
115
- Args:
116
- alpha: Metric scaling parameter.
117
- kappa: Curvature parameter.
118
- beta: Inverse temperature.
119
- tolerance: Relative tolerance for the check.
120
-
121
- Returns:
122
- ``True`` if the constraint is satisfied.
123
- """
124
- lhs = alpha * kappa
125
- rhs = self._constraint_C * beta
126
- denom = max(abs(rhs), 1e-10)
127
- return abs(lhs - rhs) / denom < tolerance
128
-
129
- def compute_integrity_score(
130
- self,
131
- alpha: float,
132
- kappa: float,
133
- beta: float,
134
- ) -> float:
135
- """Compute integrity score in [0, 1]. 1.0 = perfect.
136
-
137
- The score multiplies retrieval quality — violation degrades
138
- performance through a sigmoid penalty.
139
-
140
- Args:
141
- alpha: Metric scaling parameter.
142
- kappa: Curvature parameter.
143
- beta: Inverse temperature.
144
-
145
- Returns:
146
- Integrity score in [0.0, 1.0].
147
- """
148
- lhs = alpha * kappa
149
- rhs = self._constraint_C * beta
150
- denom = max(abs(rhs), 1e-10)
151
- deviation = abs(lhs - rhs) / denom
152
- # Sigmoid degradation: small deviations tolerated
153
- return float(1.0 / (1.0 + 10.0 * deviation ** 2))
154
-
155
- # ── Fingerprinting ────────────────────────────────────────────
156
-
157
- def embed_fingerprint(self, value: float, memory_id: int) -> float:
158
- """Embed a computation fingerprint at the precision level.
159
-
160
- Modifies the 12th decimal place to encode a signature.
161
- The fingerprint is below the noise floor and does not affect
162
- retrieval quality but can be forensically detected.
163
-
164
- Args:
165
- value: Original floating point value.
166
- memory_id: Unique memory identifier for per-memory signing.
167
-
168
- Returns:
169
- Value with embedded fingerprint.
170
- """
171
- sig = hmac.new(
172
- self._seed.encode("utf-8"),
173
- f"fingerprint:{memory_id}".encode("utf-8"),
174
- hashlib.sha256,
175
- ).hexdigest()
176
- # 8-digit fingerprint from hash, mapped to [0, 1)
177
- fp = int(sig[:8], 16) / 0xFFFFFFFF
178
- scale = 1e-10
179
- return value + fp * scale
180
-
181
- def detect_fingerprint(
182
- self,
183
- value: float,
184
- memory_id: int,
185
- original: float,
186
- ) -> bool:
187
- """Check if a value carries our fingerprint.
188
-
189
- Args:
190
- value: The potentially fingerprinted value.
191
- memory_id: Memory ID used during embedding.
192
- original: The original pre-fingerprint value.
193
-
194
- Returns:
195
- ``True`` if the fingerprint is detected.
196
- """
197
- expected = self.embed_fingerprint(original, memory_id)
198
- return abs(value - expected) < 1e-14
199
-
200
- def generate_dna_hash(self, memory_id: int) -> str:
201
- """Generate a unique DNA hash for a memory.
202
-
203
- This hash can be stored alongside the memory for provenance.
204
-
205
- Args:
206
- memory_id: Memory identifier.
207
-
208
- Returns:
209
- Hex digest string (64 chars).
210
- """
211
- return hmac.new(
212
- self._seed.encode("utf-8"),
213
- f"dna:{memory_id}".encode("utf-8"),
214
- hashlib.sha256,
215
- ).hexdigest()
216
-
217
- def verify_dna_hash(self, memory_id: int, dna_hash: str) -> bool:
218
- """Verify that a DNA hash matches the expected value.
219
-
220
- Args:
221
- memory_id: Memory identifier.
222
- dna_hash: Hash to verify.
223
-
224
- Returns:
225
- ``True`` if the hash is valid.
226
- """
227
- expected = self.generate_dna_hash(memory_id)
228
- return hmac.compare_digest(expected, dna_hash)
229
-
230
- # ── Properties ────────────────────────────────────────────────
231
-
232
- @property
233
- def constraint_C(self) -> float:
234
- """The derived coupling constant."""
235
- return self._constraint_C
@@ -1,114 +0,0 @@
1
- # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under AGPL-3.0-or-later - see LICENSE file
3
- # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
-
5
- """Post-install script for npm package.
6
-
7
- Runs after `npm install -g superlocalmemory`. Detects V2 installations,
8
- prompts for migration, and runs the setup wizard for new users.
9
- """
10
-
11
- from __future__ import annotations
12
-
13
- import sys
14
- from pathlib import Path
15
-
16
-
17
- def run_post_install():
18
- """Main post-install entry point."""
19
- print()
20
- print("SuperLocalMemory V3")
21
- print("=" * 30)
22
- print()
23
-
24
- # Upgrade banner — fires when a prior-version user runs
25
- # ``npm install -g superlocalmemory@latest``. Silent on fresh
26
- # installs (setup wizard handles welcome).
27
- try:
28
- from superlocalmemory import __version__ as _slm_ver
29
- from superlocalmemory.cli.version_banner import (
30
- check_and_emit_upgrade_banner,
31
- )
32
- if check_and_emit_upgrade_banner(_slm_ver):
33
- # Upgrade detected — the banner already covered it;
34
- # skip the V2 path + fresh-install copy and return.
35
- return
36
- except Exception:
37
- pass
38
-
39
- # Step 1: Check for V2 installation
40
- from superlocalmemory.storage.v2_migrator import V2Migrator
41
-
42
- migrator = V2Migrator()
43
-
44
- if migrator.detect_v2() and not migrator.is_already_migrated():
45
- _handle_v2_upgrade(migrator)
46
- else:
47
- _handle_fresh_install()
48
-
49
-
50
- def _handle_v2_upgrade(migrator):
51
- """Handle upgrade from V2."""
52
- stats = migrator.get_v2_stats()
53
-
54
- print("Existing V2 installation detected!")
55
- print(f" Database: {stats.get('db_path', '~/.superlocalmemory/memory.db')}")
56
- print(f" Memories: {stats.get('memory_count', 'unknown')}")
57
- print(f" Profiles: {stats.get('profile_count', 1)}")
58
- print()
59
- print("V3 requires a one-time migration to upgrade your database.")
60
- print("Your data will be preserved. A backup is created automatically.")
61
- print("You can rollback anytime within 30 days.")
62
- print()
63
-
64
- choice = input("Run migration now? [Y/n]: ").strip().lower()
65
-
66
- if choice in ("", "y", "yes"):
67
- print()
68
- print("Migrating...")
69
- result = migrator.migrate()
70
-
71
- if result.get("success"):
72
- print()
73
- for step in result.get("steps", []):
74
- print(f" [ok] {step}")
75
- print()
76
- print("Migration complete!")
77
- print(f" V3 database: {result.get('v3_db', '')}")
78
- print(f" Backup: {result.get('backup_db', '')}")
79
- print()
80
- # Run setup wizard after migration
81
- _run_setup()
82
- else:
83
- print(f"Migration failed: {result.get('error', 'unknown')}")
84
- print("Your V2 data is untouched. Run `slm migrate` to try again.")
85
- sys.exit(1)
86
- else:
87
- print()
88
- print("Migration skipped. Run `slm migrate` when ready.")
89
- print("Note: V3 features are unavailable until you migrate.")
90
-
91
-
92
- def _handle_fresh_install():
93
- """Handle fresh install (no V2 detected)."""
94
- from superlocalmemory.storage.v2_migrator import V2Migrator
95
-
96
- migrator = V2Migrator()
97
-
98
- if migrator.is_already_migrated():
99
- print("V3 already configured. Run `slm setup` to reconfigure.")
100
- return
101
-
102
- print("Welcome! Let's set up SuperLocalMemory V3.")
103
- _run_setup()
104
-
105
-
106
- def _run_setup():
107
- """Run the interactive setup wizard."""
108
- from superlocalmemory.cli.setup_wizard import run_wizard
109
-
110
- run_wizard()
111
-
112
-
113
- if __name__ == "__main__":
114
- run_post_install()
@@ -1,45 +0,0 @@
1
- # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under AGPL-3.0-or-later - see LICENSE file
3
- # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
-
5
- """Wall-clock jump detector.
6
-
7
- Part of Qualixar | Author: Varun Pratap Bhardwaj
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- from typing import Literal, Optional
13
-
14
- Event = Literal["forward", "backward"]
15
-
16
-
17
- class ClockJumpDetector:
18
- """Detects NTP-style jumps by comparing wall-clock and monotonic deltas."""
19
-
20
- def __init__(self, threshold_s: float = 1.0) -> None:
21
- self._threshold = threshold_s
22
- self._last_wall: Optional[float] = None
23
- self._last_mono: Optional[float] = None
24
- self.last_drift_s: float = 0.0
25
- self.last_event: Optional[Event] = None
26
-
27
- def tick(self, wall: float, monotonic: float) -> Optional[Event]:
28
- if self._last_wall is None or self._last_mono is None:
29
- self._last_wall = wall
30
- self._last_mono = monotonic
31
- return None
32
- dw = wall - self._last_wall
33
- dm = monotonic - self._last_mono
34
- drift = dw - dm
35
- self._last_wall = wall
36
- self._last_mono = monotonic
37
- self.last_drift_s = drift
38
- if abs(drift) < self._threshold:
39
- self.last_event = None
40
- return None
41
- self.last_event = "forward" if drift > 0 else "backward"
42
- return self.last_event
43
-
44
- def drift_magnitude_s(self) -> float:
45
- return abs(self.last_drift_s)
@@ -1,80 +0,0 @@
1
- # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under AGPL-3.0-or-later - see LICENSE file
3
- # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
-
5
- """Simple bounded SQLite connection pool.
6
-
7
- Part of Qualixar | Author: Varun Pratap Bhardwaj
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- import sqlite3
13
- import threading
14
- from contextlib import contextmanager
15
- from queue import Empty, Queue
16
- from typing import Callable, Iterator
17
-
18
-
19
- class ConnectionPool:
20
- """Bounded pool of SQLite connections created on-demand."""
21
-
22
- def __init__(
23
- self,
24
- opener: Callable[[], sqlite3.Connection],
25
- size: int = 8,
26
- ) -> None:
27
- if size < 1:
28
- raise ValueError("size must be >= 1")
29
- self._opener = opener
30
- self._size = size
31
- self._available: Queue[sqlite3.Connection] = Queue(maxsize=size)
32
- self._created = 0
33
- self._lock = threading.Lock()
34
- self._closed = False
35
- self._all: list[sqlite3.Connection] = []
36
-
37
- def _get_or_create(self, timeout: float | None) -> sqlite3.Connection:
38
- try:
39
- return self._available.get_nowait()
40
- except Empty:
41
- pass
42
- with self._lock:
43
- if self._closed:
44
- raise RuntimeError("pool is closed")
45
- if self._created < self._size:
46
- conn = self._opener()
47
- self._all.append(conn)
48
- self._created += 1
49
- return conn
50
- # Pool saturated — block for a returned connection
51
- try:
52
- return self._available.get(timeout=timeout)
53
- except Empty as exc:
54
- raise TimeoutError("timed out acquiring DB connection") from exc
55
-
56
- @contextmanager
57
- def acquire(self, timeout: float | None = 30.0) -> Iterator[sqlite3.Connection]:
58
- if self._closed:
59
- raise RuntimeError("pool is closed")
60
- conn = self._get_or_create(timeout)
61
- try:
62
- yield conn
63
- finally:
64
- if not self._closed:
65
- self._available.put(conn)
66
-
67
- def close(self) -> None:
68
- with self._lock:
69
- if self._closed:
70
- return
71
- self._closed = True
72
- for conn in self._all:
73
- try:
74
- conn.close()
75
- except Exception:
76
- pass
77
- self._all.clear()
78
-
79
- def size(self) -> int:
80
- return self._size
@@ -1,113 +0,0 @@
1
- # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under AGPL-3.0-or-later - see LICENSE file
3
- # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
-
5
- """User-facing strings for queue error codes."""
6
-
7
- from __future__ import annotations
8
-
9
- from typing import TypedDict
10
-
11
- from superlocalmemory.core.error_envelope import ErrorCode
12
-
13
-
14
- class ErrorEntry(TypedDict):
15
- code: str
16
- title: str
17
- cli_message: str
18
- recovery: list[str]
19
- exit_code: int
20
-
21
-
22
- CATALOG: dict[str, ErrorEntry] = {
23
- ErrorCode.RATE_LIMITED.value: {
24
- "code": "RATE_LIMITED",
25
- "title": "Rate limited",
26
- "cli_message": "Too many recalls in the last second.",
27
- "recovery": [
28
- "Wait a moment and try again.",
29
- "For batch work, export SLM_RATE_LIMIT_PER_AGENT=50 and restart the daemon.",
30
- ],
31
- "exit_code": 3,
32
- },
33
- ErrorCode.QUEUE_FULL.value: {
34
- "code": "QUEUE_FULL",
35
- "title": "Queue is full",
36
- "cli_message": "The request queue cannot accept more work right now.",
37
- "recovery": [
38
- "Retry with backoff.",
39
- "Run: slm queue status",
40
- ],
41
- "exit_code": 5,
42
- },
43
- ErrorCode.TIMEOUT.value: {
44
- "code": "TIMEOUT",
45
- "title": "Timed out",
46
- "cli_message": "The recall did not finish in the allotted time.",
47
- "recovery": [
48
- "Retry.",
49
- "Run: slm doctor",
50
- ],
51
- "exit_code": 2,
52
- },
53
- ErrorCode.CANCELLED.value: {
54
- "code": "CANCELLED",
55
- "title": "Cancelled",
56
- "cli_message": "The request was cancelled before it completed.",
57
- "recovery": ["Re-issue the request if needed."],
58
- "exit_code": 6,
59
- },
60
- ErrorCode.DEAD_LETTER.value: {
61
- "code": "DEAD_LETTER",
62
- "title": "Request failed after retries",
63
- "cli_message": (
64
- "The request could not complete after the maximum number of "
65
- "attempts. Your query is preserved in the dead-letter queue."
66
- ),
67
- "recovery": [
68
- "Inspect: slm queue dlq",
69
- "Run: slm doctor",
70
- ],
71
- "exit_code": 4,
72
- },
73
- ErrorCode.DAEMON_DOWN.value: {
74
- "code": "DAEMON_DOWN",
75
- "title": "Daemon unreachable",
76
- "cli_message": "Cannot reach the SLM daemon.",
77
- "recovery": [
78
- "Start the daemon: slm daemon start",
79
- "Check status: slm daemon status",
80
- ],
81
- "exit_code": 7,
82
- },
83
- ErrorCode.INTERNAL.value: {
84
- "code": "INTERNAL",
85
- "title": "Internal error",
86
- "cli_message": "An unexpected internal error occurred.",
87
- "recovery": [
88
- "Run: slm doctor",
89
- "If the issue persists, file an issue with the log excerpt.",
90
- ],
91
- "exit_code": 8,
92
- },
93
- }
94
-
95
-
96
- def lookup(code: str | ErrorCode) -> ErrorEntry:
97
- key = code.value if isinstance(code, ErrorCode) else code
98
- if key not in CATALOG:
99
- return CATALOG[ErrorCode.INTERNAL.value]
100
- return CATALOG[key]
101
-
102
-
103
- def format_cli(code: str | ErrorCode, detail: str | None = None) -> str:
104
- entry = lookup(code)
105
- lines = [f"\u2717 {entry['title']}"]
106
- lines.append(f" {entry['cli_message']}")
107
- if detail:
108
- lines.append(f" Detail: {detail}")
109
- if entry["recovery"]:
110
- lines.append(" Try:")
111
- for step in entry["recovery"]:
112
- lines.append(f" - {step}")
113
- return "\n".join(lines)
@@ -1,56 +0,0 @@
1
- # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under AGPL-3.0-or-later - see LICENSE file
3
- # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
-
5
- """Tick-based watchdog that fires once when a cooperative loop goes silent.
6
-
7
- Part of Qualixar | Author: Varun Pratap Bhardwaj
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- import threading
13
- import time
14
- from typing import Callable, Optional
15
-
16
-
17
- class LoopWatchdog:
18
- def __init__(
19
- self,
20
- stale_threshold_s: float,
21
- on_stale: Optional[Callable[[float], None]] = None,
22
- ) -> None:
23
- self._threshold = stale_threshold_s
24
- self._on_stale = on_stale
25
- self._last_tick = time.monotonic()
26
- self._fired = False
27
- self._lock = threading.Lock()
28
-
29
- def tick(self) -> None:
30
- with self._lock:
31
- self._last_tick = time.monotonic()
32
- self._fired = False
33
-
34
- def age_s(self) -> float:
35
- with self._lock:
36
- return time.monotonic() - self._last_tick
37
-
38
- def is_stale(self) -> bool:
39
- return self.age_s() >= self._threshold
40
-
41
- def check(self) -> bool:
42
- """Fire callback once if stale; return True if just fired."""
43
- with self._lock:
44
- age = time.monotonic() - self._last_tick
45
- if age < self._threshold or self._fired:
46
- return False
47
- self._fired = True
48
- cb = self._on_stale
49
- if cb is not None:
50
- cb(age)
51
- return True
52
-
53
- def run_forever(self, stop: threading.Event, interval_s: float = 1.0) -> None:
54
- while not stop.is_set():
55
- self.check()
56
- stop.wait(timeout=interval_s)