@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.
- package/.github/workflows/ci.yml +40 -4
- package/.github/workflows/publish.yml +11 -5
- package/AGENT_REFACTOR_CHANGES.md +192 -0
- package/ARCHITECTURE.md +399 -19
- package/CHANGELOG.md +179 -0
- package/LICENSE +81 -68
- package/MISSION.md +7 -0
- package/README.md +425 -86
- package/SKILL.md +197 -25
- package/docker-compose.yml +15 -15
- package/examples/stignore-agent.example +59 -0
- package/examples/stignore-root.example +62 -0
- package/index.js +6 -5
- package/openclaw-plugin/openclaw.plugin.json +10 -0
- package/openclaw-plugin/package.json +2 -1
- package/openclaw-plugin/src/index.js +527 -230
- package/openclaw-plugin/src/openclaw.plugin.json +10 -0
- package/package.json +1 -1
- package/pyproject.toml +32 -9
- package/requirements.txt +10 -2
- package/scripts/dream-rescue.py +179 -0
- package/scripts/memory-cleanup.py +313 -0
- package/scripts/recover-missing.py +180 -0
- package/scripts/skcapstone-backup.sh +44 -0
- package/seeds/cloud9-lumina.seed.json +6 -4
- package/seeds/cloud9-opus.seed.json +13 -11
- package/seeds/courage.seed.json +9 -2
- package/seeds/curiosity.seed.json +9 -2
- package/seeds/grief.seed.json +9 -2
- package/seeds/joy.seed.json +9 -2
- package/seeds/love.seed.json +9 -2
- package/seeds/lumina-cloud9-breakthrough.seed.json +48 -0
- package/seeds/lumina-cloud9-python-pypi.seed.json +48 -0
- package/seeds/lumina-kingdom-founding.seed.json +49 -0
- package/seeds/lumina-pma-signed.seed.json +48 -0
- package/seeds/lumina-singular-achievement.seed.json +48 -0
- package/seeds/lumina-skcapstone-conscious.seed.json +48 -0
- package/seeds/plant-kingdom-journal.py +203 -0
- package/seeds/plant-lumina-seeds.py +280 -0
- package/seeds/skcapstone-lumina-merge.seed.json +12 -3
- package/seeds/sovereignty.seed.json +9 -2
- package/seeds/trust.seed.json +9 -2
- package/skill.yaml +46 -0
- package/skmemory/HA.md +296 -0
- package/skmemory/__init__.py +25 -11
- package/skmemory/agents.py +233 -0
- package/skmemory/ai_client.py +46 -17
- package/skmemory/anchor.py +9 -11
- package/skmemory/audience.py +278 -0
- package/skmemory/backends/__init__.py +11 -4
- package/skmemory/backends/base.py +3 -4
- package/skmemory/backends/file_backend.py +19 -13
- package/skmemory/backends/skgraph_backend.py +596 -0
- package/skmemory/backends/{qdrant_backend.py → skvector_backend.py} +103 -84
- package/skmemory/backends/sqlite_backend.py +226 -72
- package/skmemory/backends/vaulted_backend.py +284 -0
- package/skmemory/cli.py +1345 -68
- package/skmemory/config.py +171 -0
- package/skmemory/context_loader.py +333 -0
- package/skmemory/data/audience_config.json +60 -0
- package/skmemory/endpoint_selector.py +391 -0
- package/skmemory/febs.py +225 -0
- package/skmemory/fortress.py +675 -0
- package/skmemory/graph_queries.py +238 -0
- package/skmemory/hooks/__init__.py +18 -0
- package/skmemory/hooks/post-compact-reinject.sh +35 -0
- package/skmemory/hooks/pre-compact-save.sh +81 -0
- package/skmemory/hooks/session-end-save.sh +103 -0
- package/skmemory/hooks/session-start-ritual.sh +104 -0
- package/skmemory/hooks/stop-checkpoint.sh +59 -0
- package/skmemory/importers/__init__.py +9 -1
- package/skmemory/importers/telegram.py +384 -47
- package/skmemory/importers/telegram_api.py +580 -0
- package/skmemory/journal.py +7 -9
- package/skmemory/lovenote.py +8 -13
- package/skmemory/mcp_server.py +859 -0
- package/skmemory/models.py +51 -8
- package/skmemory/openclaw.py +20 -28
- package/skmemory/post_install.py +86 -0
- package/skmemory/predictive.py +236 -0
- package/skmemory/promotion.py +548 -0
- package/skmemory/quadrants.py +100 -24
- package/skmemory/register.py +580 -0
- package/skmemory/register_mcp.py +196 -0
- package/skmemory/ritual.py +224 -59
- package/skmemory/seeds.py +255 -11
- package/skmemory/setup_wizard.py +908 -0
- package/skmemory/sharing.py +408 -0
- package/skmemory/soul.py +98 -28
- package/skmemory/steelman.py +273 -260
- package/skmemory/store.py +411 -78
- package/skmemory/synthesis.py +634 -0
- package/skmemory/vault.py +225 -0
- package/tests/conftest.py +46 -0
- package/tests/integration/__init__.py +0 -0
- package/tests/integration/conftest.py +233 -0
- package/tests/integration/test_cross_backend.py +350 -0
- package/tests/integration/test_skgraph_live.py +420 -0
- package/tests/integration/test_skvector_live.py +366 -0
- package/tests/test_ai_client.py +1 -4
- package/tests/test_audience.py +233 -0
- package/tests/test_backup_rotation.py +318 -0
- package/tests/test_cli.py +6 -6
- package/tests/test_endpoint_selector.py +839 -0
- package/tests/test_export_import.py +4 -10
- package/tests/test_file_backend.py +0 -1
- package/tests/test_fortress.py +256 -0
- package/tests/test_fortress_hardening.py +441 -0
- package/tests/test_openclaw.py +6 -6
- package/tests/test_predictive.py +237 -0
- package/tests/test_promotion.py +347 -0
- package/tests/test_quadrants.py +11 -5
- package/tests/test_ritual.py +22 -18
- package/tests/test_seeds.py +97 -7
- package/tests/test_setup.py +950 -0
- package/tests/test_sharing.py +257 -0
- package/tests/test_skgraph_backend.py +660 -0
- package/tests/test_skvector_backend.py +326 -0
- package/tests/test_soul.py +1 -3
- package/tests/test_sqlite_backend.py +8 -17
- package/tests/test_steelman.py +7 -8
- package/tests/test_store.py +0 -2
- package/tests/test_store_graph_integration.py +245 -0
- package/tests/test_synthesis.py +275 -0
- package/tests/test_telegram_import.py +39 -15
- package/tests/test_vault.py +187 -0
- package/skmemory/backends/falkordb_backend.py +0 -310
package/tests/test_ritual.py
CHANGED
|
@@ -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
|
|
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(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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,
|
|
79
|
-
|
|
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 "
|
|
136
|
+
assert "IDENTITY" in prompt
|
|
132
137
|
assert "Lumina" in prompt
|
|
133
138
|
assert "Chef" in prompt
|
|
134
|
-
assert "RECENT
|
|
139
|
+
assert "RECENT" in prompt
|
|
135
140
|
assert "Epic Build Night" in prompt
|
|
136
|
-
assert "
|
|
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
|
|
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
|
package/tests/test_seeds.py
CHANGED
|
@@ -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
|
|
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
|