superlocalmemory 3.8.5 → 3.8.7

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 (82) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +3 -3
  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 -1
  32. package/src/superlocalmemory/__init__.py +1 -1
  33. package/src/superlocalmemory/cli/commands.py +139 -404
  34. package/src/superlocalmemory/core/backend_orchestrator.py +7 -1
  35. package/src/superlocalmemory/core/component_registry.py +4 -2
  36. package/src/superlocalmemory/core/embeddings.py +33 -6
  37. package/src/superlocalmemory/core/engine.py +94 -49
  38. package/src/superlocalmemory/core/engine_ingestion.py +150 -63
  39. package/src/superlocalmemory/core/ingestion_command.py +133 -21
  40. package/src/superlocalmemory/core/mutations.py +32 -10
  41. package/src/superlocalmemory/core/recall_pipeline.py +111 -77
  42. package/src/superlocalmemory/core/remember_admission.py +152 -0
  43. package/src/superlocalmemory/core/remember_runtime.py +712 -0
  44. package/src/superlocalmemory/graph/cozo_backend.py +5 -5
  45. package/src/superlocalmemory/learning/bandit.py +50 -1
  46. package/src/superlocalmemory/learning/source_quality.py +38 -35
  47. package/src/superlocalmemory/mcp/_daemon_proxy.py +38 -15
  48. package/src/superlocalmemory/mcp/tools_active.py +4 -41
  49. package/src/superlocalmemory/mcp/tools_core.py +26 -87
  50. package/src/superlocalmemory/mcp/tools_evolution.py +5 -10
  51. package/src/superlocalmemory/optimize/proxy/capture.py +196 -8
  52. package/src/superlocalmemory/retrieval/engine.py +8 -3
  53. package/src/superlocalmemory/retrieval/reranker.py +35 -10
  54. package/src/superlocalmemory/server/loopback.py +7 -13
  55. package/src/superlocalmemory/server/profile_runtime.py +14 -0
  56. package/src/superlocalmemory/server/routes/abstraction.py +2 -4
  57. package/src/superlocalmemory/server/routes/agents.py +3 -5
  58. package/src/superlocalmemory/server/routes/behavioral.py +5 -13
  59. package/src/superlocalmemory/server/routes/brain.py +6 -9
  60. package/src/superlocalmemory/server/routes/entity.py +3 -7
  61. package/src/superlocalmemory/server/routes/evolution.py +3 -5
  62. package/src/superlocalmemory/server/routes/helpers.py +44 -23
  63. package/src/superlocalmemory/server/routes/insights.py +2 -4
  64. package/src/superlocalmemory/server/routes/learning.py +2 -5
  65. package/src/superlocalmemory/server/routes/lifecycle.py +2 -4
  66. package/src/superlocalmemory/server/routes/memories.py +122 -100
  67. package/src/superlocalmemory/server/routes/tiers.py +3 -22
  68. package/src/superlocalmemory/server/routes/timeline.py +2 -4
  69. package/src/superlocalmemory/server/routes/v3_api.py +18 -16
  70. package/src/superlocalmemory/server/unified_daemon.py +200 -109
  71. package/src/superlocalmemory/storage/admission_codec.py +119 -0
  72. package/src/superlocalmemory/storage/admission_journal.py +728 -0
  73. package/src/superlocalmemory/storage/database.py +59 -0
  74. package/src/superlocalmemory/storage/deferred_writes.py +67 -11
  75. package/src/superlocalmemory/storage/memory_write.py +8 -12
  76. package/src/superlocalmemory/storage/migration_runner.py +37 -0
  77. package/src/superlocalmemory/storage/migrations/M032_write_coordinator_admission.py +188 -0
  78. package/src/superlocalmemory/storage/read_connection.py +115 -0
  79. package/src/superlocalmemory/storage/write_coordinator.py +756 -0
  80. package/src/superlocalmemory/ui/index.html +1 -1
  81. package/src/superlocalmemory/ui/js/auto-settings.js +14 -1
  82. package/src/superlocalmemory/ui/js/od-settings.js +9 -3
@@ -0,0 +1,119 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
+
4
+ """Production encryption codec for durable remember admission commands."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import os
9
+ import stat
10
+ import time
11
+ from pathlib import Path
12
+
13
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
14
+
15
+ from superlocalmemory.infra.data_root import state_path
16
+
17
+ _KEY_BYTES = 32
18
+ _NONCE_BYTES = 12
19
+ _FORMAT_VERSION = b"\x01"
20
+ _ASSOCIATED_DATA = b"superlocalmemory:remember-admission:v1"
21
+ _KEY_READ_ATTEMPTS = 100
22
+ _KEY_READ_RETRY_SECONDS = 0.005
23
+
24
+
25
+ class AdmissionKeyError(RuntimeError):
26
+ """The machine-local admission key cannot be created or loaded safely."""
27
+
28
+
29
+ class MachineKeyCommandCodec:
30
+ """AES-256-GCM codec backed by a machine-local, permission-bound key.
31
+
32
+ The key is intentionally separate from cache encryption keys. Exclusive
33
+ creation makes concurrent daemon startup converge on one key, while the
34
+ format byte and AEAD associated data prevent cross-protocol ciphertext use.
35
+ """
36
+
37
+ def __init__(self, key_path: str | Path | None = None) -> None:
38
+ self.key_path = Path(key_path) if key_path is not None else state_path(
39
+ "admission-key.bin"
40
+ )
41
+ self._key = _load_or_create_key(self.key_path)
42
+
43
+ def encrypt(self, plaintext: bytes) -> bytes:
44
+ if not isinstance(plaintext, bytes):
45
+ raise TypeError("plaintext must be bytes")
46
+ nonce = os.urandom(_NONCE_BYTES)
47
+ ciphertext = AESGCM(self._key).encrypt(nonce, plaintext, _ASSOCIATED_DATA)
48
+ return _FORMAT_VERSION + nonce + ciphertext
49
+
50
+ def decrypt(self, ciphertext: bytes) -> bytes:
51
+ if not isinstance(ciphertext, bytes):
52
+ raise TypeError("ciphertext must be bytes")
53
+ minimum = len(_FORMAT_VERSION) + _NONCE_BYTES + 16
54
+ if len(ciphertext) < minimum or ciphertext[:1] != _FORMAT_VERSION:
55
+ raise ValueError("unsupported admission ciphertext format")
56
+ nonce = ciphertext[1 : 1 + _NONCE_BYTES]
57
+ payload = ciphertext[1 + _NONCE_BYTES :]
58
+ return AESGCM(self._key).decrypt(nonce, payload, _ASSOCIATED_DATA)
59
+
60
+
61
+ def _load_or_create_key(path: Path) -> bytes:
62
+ path.parent.mkdir(parents=True, exist_ok=True)
63
+ if os.name != "nt":
64
+ os.chmod(path.parent, 0o700)
65
+
66
+ try:
67
+ fd = os.open(
68
+ path,
69
+ os.O_WRONLY
70
+ | os.O_CREAT
71
+ | os.O_EXCL
72
+ | getattr(os, "O_BINARY", 0),
73
+ 0o600,
74
+ )
75
+ except FileExistsError:
76
+ fd = -1
77
+ else:
78
+ try:
79
+ key = os.urandom(_KEY_BYTES)
80
+ view = memoryview(key)
81
+ written = 0
82
+ while written < _KEY_BYTES:
83
+ count = os.write(fd, view[written:])
84
+ if count <= 0:
85
+ raise AdmissionKeyError("admission key write was incomplete")
86
+ written += count
87
+ os.fsync(fd)
88
+ except BaseException:
89
+ try:
90
+ path.unlink()
91
+ except OSError:
92
+ pass
93
+ raise
94
+ finally:
95
+ os.close(fd)
96
+
97
+ for attempt in range(_KEY_READ_ATTEMPTS):
98
+ try:
99
+ info = path.lstat()
100
+ except OSError as exc:
101
+ raise AdmissionKeyError("admission key is unavailable") from exc
102
+ if stat.S_ISLNK(info.st_mode) or not stat.S_ISREG(info.st_mode):
103
+ raise AdmissionKeyError("admission key path must be a regular file")
104
+ if os.name != "nt":
105
+ if info.st_uid != os.getuid():
106
+ raise AdmissionKeyError("admission key is not owned by the current user")
107
+ os.chmod(path, 0o600)
108
+ try:
109
+ key = path.read_bytes()
110
+ except OSError as exc:
111
+ raise AdmissionKeyError("admission key cannot be read") from exc
112
+ if len(key) == _KEY_BYTES:
113
+ return key
114
+ # A process that lost O_EXCL may observe the winning creator between
115
+ # file creation and its final fsync/close. Wait only for that bounded
116
+ # window; a persistently truncated or oversized key still fails closed.
117
+ if attempt + 1 < _KEY_READ_ATTEMPTS:
118
+ time.sleep(_KEY_READ_RETRY_SECONDS)
119
+ raise AdmissionKeyError("admission key has an invalid length")