superlocalmemory 3.8.3 → 3.8.6

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 (125) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +3 -2
  3. package/package.json +1 -1
  4. package/plugin/.claude-plugin/plugin.json +1 -1
  5. package/plugin/CLAUDE.md +3 -3
  6. package/plugin/agents/slm-governance-advisor.md +1 -1
  7. package/plugin/agents/slm-loop-runner.md +1 -1
  8. package/plugin/agents/slm-memory-advisor.md +1 -1
  9. package/plugin/agents/slm-optimize-advisor.md +1 -1
  10. package/plugin/requirements.txt +1 -1
  11. package/plugin/skills/slm-cache/SKILL.md +1 -1
  12. package/plugin/skills/slm-compress/SKILL.md +1 -1
  13. package/plugin/skills/slm-governance/SKILL.md +1 -1
  14. package/plugin/skills/slm-graph/SKILL.md +1 -1
  15. package/plugin/skills/slm-loop/SKILL.md +1 -1
  16. package/plugin/skills/slm-mesh/SKILL.md +1 -1
  17. package/plugin/skills/slm-profile/SKILL.md +1 -1
  18. package/plugin/skills/slm-recall/SKILL.md +1 -1
  19. package/plugin/skills/slm-remember/SKILL.md +1 -1
  20. package/plugin/skills/slm-scope/SKILL.md +1 -1
  21. package/plugin/skills/slm-session/SKILL.md +1 -1
  22. package/plugin/skills/slm-status/SKILL.md +1 -1
  23. package/plugin-src/rules/AGENTS.md +1 -1
  24. package/plugin-src/skills/slm-cache/SKILL.md +1 -1
  25. package/plugin-src/skills/slm-compress/SKILL.md +1 -1
  26. package/plugin-src/skills/slm-graph/SKILL.md +1 -1
  27. package/plugin-src/skills/slm-recall/SKILL.md +1 -1
  28. package/plugin-src/skills/slm-remember/SKILL.md +1 -1
  29. package/plugin-src/skills/slm-session/SKILL.md +1 -1
  30. package/plugin-src/skills/slm-status/SKILL.md +1 -1
  31. package/pyproject.toml +9 -4
  32. package/src/superlocalmemory/__init__.py +1 -1
  33. package/src/superlocalmemory/access/rbac.py +68 -76
  34. package/src/superlocalmemory/cli/commands.py +158 -404
  35. package/src/superlocalmemory/cli/ingest_cmd.py +11 -1
  36. package/src/superlocalmemory/cli/main.py +30 -0
  37. package/src/superlocalmemory/cli/pending_store.py +39 -14
  38. package/src/superlocalmemory/core/backend_orchestrator.py +93 -0
  39. package/src/superlocalmemory/core/component_registry.py +4 -2
  40. package/src/superlocalmemory/core/config.py +78 -0
  41. package/src/superlocalmemory/core/consolidation_engine.py +79 -73
  42. package/src/superlocalmemory/core/embeddings.py +33 -6
  43. package/src/superlocalmemory/core/engine.py +186 -60
  44. package/src/superlocalmemory/core/engine_ingestion.py +150 -63
  45. package/src/superlocalmemory/core/fact_consolidator.py +148 -30
  46. package/src/superlocalmemory/core/graph_pruner.py +436 -39
  47. package/src/superlocalmemory/core/ingestion_command.py +273 -32
  48. package/src/superlocalmemory/core/maintenance_scheduler.py +61 -1
  49. package/src/superlocalmemory/core/mutations.py +32 -10
  50. package/src/superlocalmemory/core/recall_pipeline.py +111 -74
  51. package/src/superlocalmemory/core/registry.py +5 -1
  52. package/src/superlocalmemory/core/remember_admission.py +152 -0
  53. package/src/superlocalmemory/core/remember_runtime.py +712 -0
  54. package/src/superlocalmemory/core/remote_mode.py +3 -1
  55. package/src/superlocalmemory/core/scale_engine.py +41 -18
  56. package/src/superlocalmemory/core/store_pipeline.py +18 -4
  57. package/src/superlocalmemory/encoding/entity_resolver.py +18 -11
  58. package/src/superlocalmemory/graph/cozo_backend.py +5 -5
  59. package/src/superlocalmemory/hooks/_outcome_common.py +9 -2
  60. package/src/superlocalmemory/hooks/adapter_base.py +58 -44
  61. package/src/superlocalmemory/hooks/ide_connector.py +26 -8
  62. package/src/superlocalmemory/hooks/portable_kit.py +105 -9
  63. package/src/superlocalmemory/hooks/prewarm_auth.py +21 -2
  64. package/src/superlocalmemory/infra/auth_middleware.py +3 -1
  65. package/src/superlocalmemory/infra/cloud_backup.py +26 -27
  66. package/src/superlocalmemory/infra/event_bus.py +250 -88
  67. package/src/superlocalmemory/learning/bandit.py +50 -1
  68. package/src/superlocalmemory/learning/consolidation_cycle.py +33 -16
  69. package/src/superlocalmemory/learning/entity_compiler.py +148 -132
  70. package/src/superlocalmemory/learning/memory_merge.py +97 -82
  71. package/src/superlocalmemory/learning/reward_archive.py +98 -90
  72. package/src/superlocalmemory/learning/reward_boost.py +40 -30
  73. package/src/superlocalmemory/learning/source_quality.py +38 -35
  74. package/src/superlocalmemory/mcp/_daemon_proxy.py +38 -15
  75. package/src/superlocalmemory/mcp/http_transport.py +335 -3
  76. package/src/superlocalmemory/mcp/tools_active.py +4 -41
  77. package/src/superlocalmemory/mcp/tools_core.py +26 -87
  78. package/src/superlocalmemory/mcp/tools_evolution.py +5 -10
  79. package/src/superlocalmemory/optimize/proxy/capture.py +196 -8
  80. package/src/superlocalmemory/retrieval/engine.py +15 -4
  81. package/src/superlocalmemory/retrieval/entity_channel.py +25 -1
  82. package/src/superlocalmemory/retrieval/reranker.py +130 -22
  83. package/src/superlocalmemory/retrieval/spreading_activation.py +20 -12
  84. package/src/superlocalmemory/retrieval/vector_store.py +84 -69
  85. package/src/superlocalmemory/server/loopback.py +85 -0
  86. package/src/superlocalmemory/server/origin.py +9 -4
  87. package/src/superlocalmemory/server/profile_runtime.py +14 -0
  88. package/src/superlocalmemory/server/routes/abstraction.py +2 -4
  89. package/src/superlocalmemory/server/routes/agents.py +3 -5
  90. package/src/superlocalmemory/server/routes/backup.py +6 -2
  91. package/src/superlocalmemory/server/routes/behavioral.py +11 -25
  92. package/src/superlocalmemory/server/routes/brain.py +6 -9
  93. package/src/superlocalmemory/server/routes/compliance.py +20 -23
  94. package/src/superlocalmemory/server/routes/config_api.py +83 -0
  95. package/src/superlocalmemory/server/routes/entity.py +3 -7
  96. package/src/superlocalmemory/server/routes/evolution.py +3 -5
  97. package/src/superlocalmemory/server/routes/helpers.py +57 -25
  98. package/src/superlocalmemory/server/routes/insights.py +2 -4
  99. package/src/superlocalmemory/server/routes/learning.py +2 -5
  100. package/src/superlocalmemory/server/routes/lifecycle.py +2 -4
  101. package/src/superlocalmemory/server/routes/memories.py +119 -98
  102. package/src/superlocalmemory/server/routes/mesh.py +7 -2
  103. package/src/superlocalmemory/server/routes/profiles.py +20 -21
  104. package/src/superlocalmemory/server/routes/rbac.py +0 -1
  105. package/src/superlocalmemory/server/routes/tiers.py +28 -35
  106. package/src/superlocalmemory/server/routes/timeline.py +2 -4
  107. package/src/superlocalmemory/server/routes/v3_api.py +85 -93
  108. package/src/superlocalmemory/server/unified_daemon.py +400 -140
  109. package/src/superlocalmemory/server/write_identity.py +22 -4
  110. package/src/superlocalmemory/storage/admission_codec.py +119 -0
  111. package/src/superlocalmemory/storage/admission_journal.py +728 -0
  112. package/src/superlocalmemory/storage/database.py +168 -19
  113. package/src/superlocalmemory/storage/deferred_writes.py +209 -0
  114. package/src/superlocalmemory/storage/embedding_migrator.py +19 -0
  115. package/src/superlocalmemory/storage/memory_write.py +115 -0
  116. package/src/superlocalmemory/storage/migration_runner.py +44 -0
  117. package/src/superlocalmemory/storage/migrations/M028_fact_entity_associations.py +113 -78
  118. package/src/superlocalmemory/storage/migrations/M031_dead_letter_operations.py +80 -0
  119. package/src/superlocalmemory/storage/migrations/M032_write_coordinator_admission.py +188 -0
  120. package/src/superlocalmemory/storage/read_connection.py +115 -0
  121. package/src/superlocalmemory/storage/write_coordinator.py +756 -0
  122. package/src/superlocalmemory/storage/write_lock.py +88 -0
  123. package/src/superlocalmemory/ui/index.html +1 -1
  124. package/src/superlocalmemory/ui/js/auto-settings.js +14 -1
  125. package/src/superlocalmemory/ui/js/od-settings.js +9 -3
@@ -0,0 +1,88 @@
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
4
+
5
+ """Process-level per-path write-lock registry for memory.db.
6
+
7
+ ALL code that writes to a given SQLite file MUST acquire the lock returned
8
+ by ``get_write_lock(db_path)`` BEFORE opening a sqlite3 connection for
9
+ writing. This serialises writes at the Python layer so that only ONE
10
+ sqlite3 connection holds the SQLite WAL write lock at any instant, which
11
+ eliminates all SQLITE_BUSY retries within the process.
12
+
13
+ Invariant
14
+ ---------
15
+ Every memory.db write path in the process acquires the same RLock before
16
+ touching sqlite3. Because SQLite WAL mode allows one writer at a time, a
17
+ Python-level RLock gives us the same serialisation guarantee while
18
+ completely avoiding the SQLITE_BUSY / busy_timeout retry loop.
19
+
20
+ Lock ordering rule (must never be violated)
21
+ -------------------------------------------
22
+ get_write_lock(memory.db) ← OUTERMOST
23
+ ↳ VectorStore._lock (inner — VectorStore in-memory state)
24
+ ↳ sqlite3 write transaction (BEGIN … COMMIT)
25
+
26
+ Re-entrancy
27
+ -----------
28
+ ``threading.RLock`` is used so that the same thread can re-acquire the
29
+ lock without deadlocking. This is required by the self-heal backfill
30
+ pattern::
31
+
32
+ with db._lock: # db._lock IS get_write_lock(memory.db)
33
+ vs.upsert(...) # also calls get_write_lock → re-entrant OK
34
+
35
+ Usage
36
+ -----
37
+ from superlocalmemory.storage.write_lock import get_write_lock
38
+
39
+ with get_write_lock(db_path):
40
+ conn = sqlite3.connect(str(db_path))
41
+ conn.execute("INSERT INTO ...")
42
+ conn.commit()
43
+ conn.close()
44
+ """
45
+ from __future__ import annotations
46
+
47
+ import threading
48
+ from pathlib import Path
49
+
50
+ # Registry: resolved absolute path string → RLock.
51
+ # Protected by its own Lock so the registry itself is thread-safe.
52
+ _registry: dict[str, threading.RLock] = {}
53
+ _registry_lock = threading.Lock()
54
+
55
+
56
+ def get_write_lock(db_path: str | Path) -> threading.RLock:
57
+ """Return the process-level write-serialisation RLock for *db_path*.
58
+
59
+ Resolves the path to its canonical absolute form before hashing so
60
+ that relative paths, ``.`` components, and symlinks all map to the
61
+ same lock as their resolved target. Creates a new ``RLock`` on first
62
+ call for each unique resolved path. Thread-safe.
63
+
64
+ Parameters
65
+ ----------
66
+ db_path:
67
+ Path to the SQLite database file. The file need not exist yet.
68
+
69
+ Returns
70
+ -------
71
+ threading.RLock
72
+ The shared write-serialisation lock for *db_path*. All writers
73
+ of this file in the current process share the same object.
74
+ """
75
+ p = Path(db_path)
76
+ try:
77
+ key = str(p.resolve())
78
+ except OSError:
79
+ # File doesn't exist yet; use absolute path as best-effort key.
80
+ key = str(p.absolute())
81
+
82
+ with _registry_lock:
83
+ if key not in _registry:
84
+ _registry[key] = threading.RLock()
85
+ return _registry[key]
86
+
87
+
88
+ __all__ = ["get_write_lock"]
@@ -1578,7 +1578,7 @@
1578
1578
  <script src="static/js/od-skills.js?v=379"></script>
1579
1579
  <script src="static/js/od-mesh.js?v=379"></script>
1580
1580
  <script src="static/js/od-optimize.js?v=379"></script>
1581
- <script src="static/js/od-settings.js?v=382"></script>
1581
+ <script src="static/js/od-settings.js?v=386"></script>
1582
1582
  <script src="static/js/od-backup.js?v=379"></script>
1583
1583
  <!-- MCP & Integrations pane (v3.8.0): shows exposed MCP tool profile + counts -->
1584
1584
  <script src="static/js/od-mcp.js?v=380"></script>
@@ -463,7 +463,10 @@ async function saveAllSettings() {
463
463
  async function loadEmbeddingSettings() {
464
464
  try {
465
465
  var resp = await fetch('/api/v3/embedding/config');
466
- if (!resp.ok) return;
466
+ if (!resp.ok) {
467
+ showEmbeddingConfigUnavailable();
468
+ return;
469
+ }
467
470
  var data = await resp.json();
468
471
 
469
472
  var provEl = document.getElementById('settings-emb-provider');
@@ -495,9 +498,19 @@ async function loadEmbeddingSettings() {
495
498
  }
496
499
  } catch (e) {
497
500
  console.log('Load embedding settings error:', e);
501
+ showEmbeddingConfigUnavailable();
498
502
  }
499
503
  }
500
504
 
505
+ function showEmbeddingConfigUnavailable() {
506
+ var info = document.getElementById('settings-emb-info');
507
+ if (!info) return;
508
+ // Do not alter any form values here: rendering fallback values would make
509
+ // a transient daemon/config failure look like a valid Mode A setup.
510
+ info.textContent = 'Embedding configuration unavailable. Check daemon health and retry.';
511
+ info.className = 'text-warning small';
512
+ }
513
+
501
514
  function updateEmbeddingUI() {
502
515
  var provider = document.getElementById('settings-emb-provider')?.value || 'default';
503
516
  var isCustom = provider === 'openai';
@@ -368,9 +368,12 @@
368
368
  ]);
369
369
  }
370
370
  function loadEmb() {
371
- fetch('/api/v3/embedding/config').then(function (r) { return r.ok ? r.json() : null; })
371
+ fetch('/api/v3/embedding/config').then(function (r) {
372
+ if (!r.ok) throw new Error('embedding config unavailable');
373
+ return r.json();
374
+ })
372
375
  .then(function (d) {
373
- if (!d) return;
376
+ if (!d) throw new Error('embedding config unavailable');
374
377
  var p = q('emb-prov');
375
378
  if (p) { p.value = d.is_openai_compatible ? 'openai' : 'default'; p.dispatchEvent(new Event('change')); }
376
379
  var m = q('emb-model'); if (m) m.value = d.model_name || '';
@@ -378,7 +381,10 @@
378
381
  var ep = q('emb-endpoint'); if (ep) ep.value = d.api_endpoint || '';
379
382
  var inf = q('emb-info');
380
383
  if (inf) inf.textContent = 'Current: ' + (d.model_name || 'nomic-embed-text') + ' (' + (d.dimension || 768) + 'd)';
381
- }).catch(function () {});
384
+ }).catch(function () {
385
+ var inf = q('emb-info');
386
+ if (inf) inf.textContent = 'Embedding configuration unavailable. Check daemon health and retry.';
387
+ });
382
388
  }
383
389
  function saveEmb() {
384
390
  setSt('emb', 'Saving…', null);