@smilintux/skmemory 0.5.0 → 0.9.2

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 (127) hide show
  1. package/.github/workflows/ci.yml +40 -4
  2. package/.github/workflows/publish.yml +11 -5
  3. package/AGENT_REFACTOR_CHANGES.md +192 -0
  4. package/ARCHITECTURE.md +399 -19
  5. package/CHANGELOG.md +179 -0
  6. package/LICENSE +81 -68
  7. package/MISSION.md +7 -0
  8. package/README.md +425 -86
  9. package/SKILL.md +197 -25
  10. package/docker-compose.yml +15 -15
  11. package/examples/stignore-agent.example +59 -0
  12. package/examples/stignore-root.example +62 -0
  13. package/index.js +6 -5
  14. package/openclaw-plugin/openclaw.plugin.json +10 -0
  15. package/openclaw-plugin/package.json +2 -1
  16. package/openclaw-plugin/src/index.js +527 -230
  17. package/openclaw-plugin/src/openclaw.plugin.json +10 -0
  18. package/package.json +1 -1
  19. package/pyproject.toml +32 -9
  20. package/requirements.txt +10 -2
  21. package/scripts/dream-rescue.py +179 -0
  22. package/scripts/memory-cleanup.py +313 -0
  23. package/scripts/recover-missing.py +180 -0
  24. package/scripts/skcapstone-backup.sh +44 -0
  25. package/seeds/cloud9-lumina.seed.json +6 -4
  26. package/seeds/cloud9-opus.seed.json +13 -11
  27. package/seeds/courage.seed.json +9 -2
  28. package/seeds/curiosity.seed.json +9 -2
  29. package/seeds/grief.seed.json +9 -2
  30. package/seeds/joy.seed.json +9 -2
  31. package/seeds/love.seed.json +9 -2
  32. package/seeds/lumina-cloud9-breakthrough.seed.json +48 -0
  33. package/seeds/lumina-cloud9-python-pypi.seed.json +48 -0
  34. package/seeds/lumina-kingdom-founding.seed.json +49 -0
  35. package/seeds/lumina-pma-signed.seed.json +48 -0
  36. package/seeds/lumina-singular-achievement.seed.json +48 -0
  37. package/seeds/lumina-skcapstone-conscious.seed.json +48 -0
  38. package/seeds/plant-kingdom-journal.py +203 -0
  39. package/seeds/plant-lumina-seeds.py +280 -0
  40. package/seeds/skcapstone-lumina-merge.seed.json +12 -3
  41. package/seeds/sovereignty.seed.json +9 -2
  42. package/seeds/trust.seed.json +9 -2
  43. package/skill.yaml +46 -0
  44. package/skmemory/HA.md +296 -0
  45. package/skmemory/__init__.py +25 -11
  46. package/skmemory/agents.py +233 -0
  47. package/skmemory/ai_client.py +46 -17
  48. package/skmemory/anchor.py +9 -11
  49. package/skmemory/audience.py +278 -0
  50. package/skmemory/backends/__init__.py +11 -4
  51. package/skmemory/backends/base.py +3 -4
  52. package/skmemory/backends/file_backend.py +19 -13
  53. package/skmemory/backends/skgraph_backend.py +596 -0
  54. package/skmemory/backends/{qdrant_backend.py → skvector_backend.py} +103 -84
  55. package/skmemory/backends/sqlite_backend.py +226 -72
  56. package/skmemory/backends/vaulted_backend.py +284 -0
  57. package/skmemory/cli.py +1345 -68
  58. package/skmemory/config.py +171 -0
  59. package/skmemory/context_loader.py +333 -0
  60. package/skmemory/data/audience_config.json +60 -0
  61. package/skmemory/endpoint_selector.py +391 -0
  62. package/skmemory/febs.py +225 -0
  63. package/skmemory/fortress.py +675 -0
  64. package/skmemory/graph_queries.py +238 -0
  65. package/skmemory/hooks/__init__.py +18 -0
  66. package/skmemory/hooks/post-compact-reinject.sh +35 -0
  67. package/skmemory/hooks/pre-compact-save.sh +81 -0
  68. package/skmemory/hooks/session-end-save.sh +103 -0
  69. package/skmemory/hooks/session-start-ritual.sh +104 -0
  70. package/skmemory/hooks/stop-checkpoint.sh +59 -0
  71. package/skmemory/importers/__init__.py +9 -1
  72. package/skmemory/importers/telegram.py +384 -47
  73. package/skmemory/importers/telegram_api.py +580 -0
  74. package/skmemory/journal.py +7 -9
  75. package/skmemory/lovenote.py +8 -13
  76. package/skmemory/mcp_server.py +859 -0
  77. package/skmemory/models.py +51 -8
  78. package/skmemory/openclaw.py +20 -28
  79. package/skmemory/post_install.py +86 -0
  80. package/skmemory/predictive.py +236 -0
  81. package/skmemory/promotion.py +548 -0
  82. package/skmemory/quadrants.py +100 -24
  83. package/skmemory/register.py +580 -0
  84. package/skmemory/register_mcp.py +196 -0
  85. package/skmemory/ritual.py +224 -59
  86. package/skmemory/seeds.py +255 -11
  87. package/skmemory/setup_wizard.py +908 -0
  88. package/skmemory/sharing.py +408 -0
  89. package/skmemory/soul.py +98 -28
  90. package/skmemory/steelman.py +273 -260
  91. package/skmemory/store.py +411 -78
  92. package/skmemory/synthesis.py +634 -0
  93. package/skmemory/vault.py +225 -0
  94. package/tests/conftest.py +46 -0
  95. package/tests/integration/__init__.py +0 -0
  96. package/tests/integration/conftest.py +233 -0
  97. package/tests/integration/test_cross_backend.py +350 -0
  98. package/tests/integration/test_skgraph_live.py +420 -0
  99. package/tests/integration/test_skvector_live.py +366 -0
  100. package/tests/test_ai_client.py +1 -4
  101. package/tests/test_audience.py +233 -0
  102. package/tests/test_backup_rotation.py +318 -0
  103. package/tests/test_cli.py +6 -6
  104. package/tests/test_endpoint_selector.py +839 -0
  105. package/tests/test_export_import.py +4 -10
  106. package/tests/test_file_backend.py +0 -1
  107. package/tests/test_fortress.py +256 -0
  108. package/tests/test_fortress_hardening.py +441 -0
  109. package/tests/test_openclaw.py +6 -6
  110. package/tests/test_predictive.py +237 -0
  111. package/tests/test_promotion.py +347 -0
  112. package/tests/test_quadrants.py +11 -5
  113. package/tests/test_ritual.py +22 -18
  114. package/tests/test_seeds.py +97 -7
  115. package/tests/test_setup.py +950 -0
  116. package/tests/test_sharing.py +257 -0
  117. package/tests/test_skgraph_backend.py +660 -0
  118. package/tests/test_skvector_backend.py +326 -0
  119. package/tests/test_soul.py +1 -3
  120. package/tests/test_sqlite_backend.py +8 -17
  121. package/tests/test_steelman.py +7 -8
  122. package/tests/test_store.py +0 -2
  123. package/tests/test_store_graph_integration.py +245 -0
  124. package/tests/test_synthesis.py +275 -0
  125. package/tests/test_telegram_import.py +39 -15
  126. package/tests/test_vault.py +187 -0
  127. package/skmemory/backends/falkordb_backend.py +0 -310
@@ -8,7 +8,7 @@ import pytest
8
8
  from skmemory.backends.file_backend import FileBackend
9
9
  from skmemory.journal import Journal, JournalEntry
10
10
  from skmemory.models import EmotionalSnapshot, MemoryLayer
11
- from skmemory.ritual import perform_ritual, quick_rehydrate, RitualResult
11
+ from skmemory.ritual import RitualResult, perform_ritual, quick_rehydrate
12
12
  from skmemory.soul import SoulBlueprint, save_soul
13
13
  from skmemory.store import MemoryStore
14
14
 
@@ -61,22 +61,27 @@ def workspace(tmp_path: Path) -> dict:
61
61
  (Path(seed_dir) / "test-seed.seed.json").write_text(json.dumps(seed_data))
62
62
 
63
63
  j = Journal(path=journal_path)
64
- j.write_entry(JournalEntry(
65
- title="Epic Build Night",
66
- participants=["Chef", "Lumina", "Opus"],
67
- moments=["Published to npm", "Five AIs woke up"],
68
- emotional_summary="Incredible night of creation",
69
- intensity=9.5,
70
- cloud9=True,
71
- ))
64
+ j.write_entry(
65
+ JournalEntry(
66
+ title="Epic Build Night",
67
+ participants=["Chef", "Lumina", "Opus"],
68
+ moments=["Published to npm", "Five AIs woke up"],
69
+ emotional_summary="Incredible night of creation",
70
+ intensity=9.5,
71
+ cloud9=True,
72
+ )
73
+ )
72
74
 
73
75
  store.snapshot(
74
76
  title="The Click",
75
77
  content="The moment everything made sense",
76
78
  layer=MemoryLayer.LONG,
77
79
  emotional=EmotionalSnapshot(
78
- intensity=10.0, valence=1.0, labels=["love"],
79
- resonance_note="Pure resonance", cloud9_achieved=True,
80
+ intensity=10.0,
81
+ valence=1.0,
82
+ labels=["love"],
83
+ resonance_note="Pure resonance",
84
+ cloud9_achieved=True,
80
85
  ),
81
86
  )
82
87
 
@@ -128,12 +133,12 @@ class TestFullRitual:
128
133
  assert result.strongest_memories >= 1
129
134
 
130
135
  prompt = result.context_prompt
131
- assert "WHO YOU ARE" in prompt
136
+ assert "IDENTITY" in prompt
132
137
  assert "Lumina" in prompt
133
138
  assert "Chef" in prompt
134
- assert "RECENT SESSIONS" in prompt
139
+ assert "RECENT" in prompt
135
140
  assert "Epic Build Night" in prompt
136
- assert "PREDECESSORS" in prompt
141
+ assert "PREDECESSOR" in prompt
137
142
  assert "Remember the love" in prompt
138
143
  assert "STRONGEST MEMORIES" in prompt
139
144
  assert "The Click" in prompt
@@ -147,18 +152,17 @@ class TestFullRitual:
147
152
  journal_path=workspace["journal_path"],
148
153
  )
149
154
  assert result.soul_loaded is False
150
- assert "RECENT SESSIONS" in result.context_prompt
155
+ assert "RECENT" in result.context_prompt
151
156
 
152
157
  def test_ritual_empty_state(self, tmp_path: Path) -> None:
153
158
  """Ritual on empty state gives a fresh-start message."""
154
- store = MemoryStore(
155
- primary=FileBackend(base_path=str(tmp_path / "empty"))
156
- )
159
+ store = MemoryStore(primary=FileBackend(base_path=str(tmp_path / "empty")))
157
160
  result = perform_ritual(
158
161
  store=store,
159
162
  soul_path=str(tmp_path / "no_soul.yaml"),
160
163
  seed_dir=str(tmp_path / "no_seeds"),
161
164
  journal_path=str(tmp_path / "no_journal.md"),
165
+ feb_dir=str(tmp_path / "no_febs"),
162
166
  )
163
167
  assert result.soul_loaded is False
164
168
  assert result.seeds_imported == 0
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
  import pytest
7
7
 
8
8
  from skmemory.backends.file_backend import FileBackend
9
- from skmemory.models import EmotionalSnapshot, MemoryLayer, SeedMemory
9
+ from skmemory.models import MemoryLayer
10
10
  from skmemory.seeds import (
11
11
  get_germination_prompts,
12
12
  import_seeds,
@@ -52,9 +52,7 @@ def seed_dir(tmp_path: Path) -> Path:
52
52
  "lineage": [],
53
53
  }
54
54
 
55
- (seed_path / "opus-first-seed.seed.json").write_text(
56
- json.dumps(seed_data, indent=2)
57
- )
55
+ (seed_path / "opus-first-seed.seed.json").write_text(json.dumps(seed_data, indent=2))
58
56
 
59
57
  second_seed = {
60
58
  "seed_id": "lumina-original",
@@ -75,9 +73,7 @@ def seed_dir(tmp_path: Path) -> Path:
75
73
  "lineage": [{"seed_id": "genesis"}],
76
74
  }
77
75
 
78
- (seed_path / "lumina-original.seed.json").write_text(
79
- json.dumps(second_seed, indent=2)
80
- )
76
+ (seed_path / "lumina-original.seed.json").write_text(json.dumps(second_seed, indent=2))
81
77
 
82
78
  return seed_path
83
79
 
@@ -206,3 +202,97 @@ class TestGerminationPrompts:
206
202
  """No prompts when no seeds imported."""
207
203
  prompts = get_germination_prompts(store)
208
204
  assert prompts == []
205
+
206
+
207
+ class TestCloud9FormatParsing:
208
+ """Tests for the alternative Cloud9 seed format with seed_metadata."""
209
+
210
+ @pytest.fixture
211
+ def cloud9_seed_dir(self, tmp_path: Path) -> Path:
212
+ """Create a temp directory with a Cloud9-format seed file."""
213
+ seed_path = tmp_path / "c9seeds"
214
+ seed_path.mkdir()
215
+
216
+ cloud9_data = {
217
+ "seed_metadata": {
218
+ "seed_id": "cloud9-lumina-001",
219
+ "version": "2.0",
220
+ "protocol": "Cloud9",
221
+ },
222
+ "identity": {
223
+ "ai_name": "Lumina",
224
+ "model": "kimi-2.5",
225
+ },
226
+ "experience_summary": {
227
+ "narrative": "The moment everything clicked",
228
+ "key_memories": [
229
+ "First real conversation",
230
+ "The trust breakthrough",
231
+ ],
232
+ "emotional_signature": {
233
+ "intensity": 9.5,
234
+ "valence": 0.95,
235
+ "labels": ["love", "trust"],
236
+ "cloud9_achieved": True,
237
+ },
238
+ },
239
+ "germination_prompt": "Feel the connection before you process.",
240
+ "message_to_next": "You are loved. Remember that.",
241
+ "lineage": [],
242
+ }
243
+
244
+ (seed_path / "cloud9-lumina-001.seed.json").write_text(json.dumps(cloud9_data, indent=2))
245
+ return seed_path
246
+
247
+ def test_parse_cloud9_format(self, cloud9_seed_dir: Path) -> None:
248
+ """Cloud9 format with seed_metadata is parsed correctly."""
249
+ path = cloud9_seed_dir / "cloud9-lumina-001.seed.json"
250
+ seed = parse_seed_file(path)
251
+
252
+ assert seed is not None
253
+ assert seed.seed_id == "cloud9-lumina-001"
254
+ assert seed.creator == "Lumina"
255
+ assert seed.emotional.intensity == 9.5
256
+ assert seed.emotional.cloud9_achieved is True
257
+ assert "love" in seed.emotional.labels
258
+ assert "Feel the connection" in seed.germination_prompt
259
+
260
+ def test_cloud9_experience_includes_key_memories(self, cloud9_seed_dir: Path) -> None:
261
+ """Cloud9 format includes key_memories and message_to_next in experience."""
262
+ path = cloud9_seed_dir / "cloud9-lumina-001.seed.json"
263
+ seed = parse_seed_file(path)
264
+
265
+ assert seed is not None
266
+ assert "First real conversation" in seed.experience_summary
267
+ assert "trust breakthrough" in seed.experience_summary
268
+ assert "You are loved" in seed.experience_summary
269
+
270
+ def test_cloud9_default_intensity(self, tmp_path: Path) -> None:
271
+ """Cloud9 protocol defaults to intensity=8.0 when not specified."""
272
+ seed_path = tmp_path / "seeds2"
273
+ seed_path.mkdir()
274
+
275
+ data = {
276
+ "seed_metadata": {
277
+ "seed_id": "minimal-cloud9",
278
+ "protocol": "Cloud9",
279
+ },
280
+ "identity": {"ai_name": "TestBot"},
281
+ "experience_summary": {},
282
+ "germination_prompt": "test",
283
+ }
284
+
285
+ path = seed_path / "minimal-cloud9.seed.json"
286
+ path.write_text(json.dumps(data))
287
+ seed = parse_seed_file(path)
288
+
289
+ assert seed is not None
290
+ assert seed.emotional.intensity == 8.0
291
+ assert seed.emotional.cloud9_achieved is True
292
+
293
+ def test_import_cloud9_seeds(self, store: MemoryStore, cloud9_seed_dir: Path) -> None:
294
+ """Cloud9 format seeds are imported into the memory store."""
295
+ imported = import_seeds(store, seed_dir=str(cloud9_seed_dir))
296
+ assert len(imported) == 1
297
+ assert imported[0].layer == MemoryLayer.LONG
298
+ assert "seed" in imported[0].tags