loop-memory 0.4.10__tar.gz → 0.4.12__tar.gz

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 (156) hide show
  1. {loop_memory-0.4.10 → loop_memory-0.4.12}/PKG-INFO +16 -1
  2. {loop_memory-0.4.10 → loop_memory-0.4.12}/README.md +15 -0
  3. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/commands/cognitive.py +46 -0
  4. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/main.py +4 -0
  5. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/app.py +6 -0
  6. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/routes/system.py +78 -0
  7. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/storage/sqlite_store.py +582 -2
  8. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory.egg-info/PKG-INFO +16 -1
  9. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory.egg-info/SOURCES.txt +4 -0
  10. {loop_memory-0.4.10 → loop_memory-0.4.12}/pyproject.toml +1 -1
  11. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_agents_init.py +3 -3
  12. loop_memory-0.4.12/tests/test_audit_triggers_2026_09_27.py +188 -0
  13. loop_memory-0.4.12/tests/test_busy_timeout_2026_09_24.py +110 -0
  14. loop_memory-0.4.12/tests/test_healthz_readyz_2026_09_24.py +80 -0
  15. loop_memory-0.4.12/tests/test_import_okf_2026_09_27.py +425 -0
  16. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_supersession_chain.py +1 -1
  17. {loop_memory-0.4.10 → loop_memory-0.4.12}/LICENSE +0 -0
  18. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/__init__.py +0 -0
  19. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/backends/__init__.py +0 -0
  20. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/backends/embedding.py +0 -0
  21. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/backends/sentence_embedder.py +0 -0
  22. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/backends/vector_store.py +0 -0
  23. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/__init__.py +0 -0
  24. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/_common.py +0 -0
  25. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/commands/__init__.py +0 -0
  26. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/commands/diag.py +0 -0
  27. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/commands/graph.py +0 -0
  28. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/commands/hooks.py +0 -0
  29. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/commands/read.py +0 -0
  30. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/commands/rules.py +0 -0
  31. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/commands/serve.py +0 -0
  32. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/cli/commands/write.py +0 -0
  33. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/engine/__init__.py +0 -0
  34. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/engine/loop.py +0 -0
  35. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/engine/reflect.py +0 -0
  36. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/examples/__init__.py +0 -0
  37. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/examples/demo.py +0 -0
  38. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/export/__init__.py +0 -0
  39. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/export/memory_md.py +0 -0
  40. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/graph/__init__.py +0 -0
  41. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/graph/build.py +0 -0
  42. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/graph/extract.py +0 -0
  43. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/ingest/__init__.py +0 -0
  44. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/ingest/loader.py +0 -0
  45. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/ingest/pipeline.py +0 -0
  46. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/jobs/__init__.py +0 -0
  47. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/jobs/cognitive.py +0 -0
  48. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/jobs/compact.py +0 -0
  49. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/jobs/consolidate.py +0 -0
  50. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/jobs/contradiction.py +0 -0
  51. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/jobs/evolution.py +0 -0
  52. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/jobs/graph.py +0 -0
  53. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/jobs/llm_compact_pass.py +0 -0
  54. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/jobs/llm_consolidate.py +0 -0
  55. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/jobs/scheduler.py +0 -0
  56. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/llm/__init__.py +0 -0
  57. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/llm/base.py +0 -0
  58. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/llm/openai_adapter.py +0 -0
  59. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/llm/providers.py +0 -0
  60. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/mcp/__init__.py +0 -0
  61. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/memory/__init__.py +0 -0
  62. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/memory/types.py +0 -0
  63. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/privacy/__init__.py +0 -0
  64. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/privacy/private.py +0 -0
  65. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/privacy/redact.py +0 -0
  66. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/py.typed +0 -0
  67. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/sdk.py +0 -0
  68. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/sdk_extensions.py +0 -0
  69. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/security/__init__.py +0 -0
  70. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/security/secrets.py +0 -0
  71. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/__init__.py +0 -0
  72. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/handlers.py +0 -0
  73. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/routes/_shared.py +0 -0
  74. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/routes/admin.py +0 -0
  75. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/routes/cognitive.py +0 -0
  76. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/routes/export.py +0 -0
  77. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/routes/graph.py +0 -0
  78. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/routes/insights.py +0 -0
  79. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/routes/memories.py +0 -0
  80. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/routes/sessions.py +0 -0
  81. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/routes/wiki.py +0 -0
  82. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/static/__init__.py +0 -0
  83. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/static/index.html +0 -0
  84. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/serve/watcher.py +0 -0
  85. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/storage/__init__.py +0 -0
  86. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/storage/retrieval.py +0 -0
  87. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/storage/snapshot.py +0 -0
  88. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/wiki/__init__.py +0 -0
  89. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/wiki/backfill.py +0 -0
  90. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/wiki/classifier.py +0 -0
  91. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/wiki/prompts.py +0 -0
  92. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory/wiki/scope.py +0 -0
  93. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory.egg-info/dependency_links.txt +0 -0
  94. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory.egg-info/entry_points.txt +0 -0
  95. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory.egg-info/requires.txt +0 -0
  96. {loop_memory-0.4.10 → loop_memory-0.4.12}/loop_memory.egg-info/top_level.txt +0 -0
  97. {loop_memory-0.4.10 → loop_memory-0.4.12}/setup.cfg +0 -0
  98. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_admin_ingest_route.py +0 -0
  99. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_agent_memory_api.py +0 -0
  100. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_agent_memory_sdk.py +0 -0
  101. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_auth_token_rotate.py +0 -0
  102. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cache_cap_2026_09_23.py +0 -0
  103. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cli_audit_supersede.py +0 -0
  104. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cli_export_okf.py +0 -0
  105. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cli_init.py +0 -0
  106. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cli_memory_stats.py +0 -0
  107. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cli_recall_as_of.py +0 -0
  108. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cli_recall_paths.py +0 -0
  109. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cli_recall_verbose.py +0 -0
  110. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cli_rules.py +0 -0
  111. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cli_snapshot.py +0 -0
  112. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cli_v7.py +0 -0
  113. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_cli_version.py +0 -0
  114. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_contradictions.py +0 -0
  115. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_evolution.py +0 -0
  116. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_evolution_quality.py +0 -0
  117. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_expanduser.py +0 -0
  118. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_export_ask.py +0 -0
  119. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_export_okf.py +0 -0
  120. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_graph.py +0 -0
  121. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_ingest.py +0 -0
  122. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_llm_consolidator.py +0 -0
  123. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_llm_fingerprint_not_persisted.py +0 -0
  124. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_llm_providers.py +0 -0
  125. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_llm_test_endpoint.py +0 -0
  126. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_loop.py +0 -0
  127. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_mcp.py +0 -0
  128. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_memories_pagination.py +0 -0
  129. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_memory_stats.py +0 -0
  130. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_openclaw_loader.py +0 -0
  131. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_package_version.py +0 -0
  132. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_path_safety_2026_09_23.py +0 -0
  133. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_recall_as_of.py +0 -0
  134. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_recall_paths.py +0 -0
  135. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_recall_provenance.py +0 -0
  136. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_reflection.py +0 -0
  137. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_repo_framing.py +0 -0
  138. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_routes_audit_2026_09_13.py +0 -0
  139. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_routes_audit_2026_09_20.py +0 -0
  140. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_score_api.py +0 -0
  141. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_scoring_v2.py +0 -0
  142. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_secrets.py +0 -0
  143. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_serve_app.py +0 -0
  144. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_serve_handlers.py +0 -0
  145. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_session_order.py +0 -0
  146. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_short_query.py +0 -0
  147. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_snapshot.py +0 -0
  148. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_store.py +0 -0
  149. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_subgraph_hops_2026_09_23.py +0 -0
  150. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_summarization.py +0 -0
  151. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_universal_memory.py +0 -0
  152. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_vector_store.py +0 -0
  153. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_watcher.py +0 -0
  154. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_wiki_classifier.py +0 -0
  155. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_wiki_export_escape.py +0 -0
  156. {loop_memory-0.4.10 → loop_memory-0.4.12}/tests/test_wiki_prompts.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: loop-memory
3
- Version: 0.4.10
3
+ Version: 0.4.12
4
4
  Summary: A general-purpose, local memory system for every AI agent you run. Loop Memory auto-captures conversations from Codex / Claude / Hermes / OpenClaw, scores them by importance × recency × usage × feedback, distils them into a curated wiki, and serves everything from a single web UI.
5
5
  Author: Loop Memory contributors <loop-memory@users.noreply.github.com>
6
6
  License-Expression: MIT
@@ -66,6 +66,21 @@ Dynamic: license-file
66
66
 
67
67
  ---
68
68
 
69
+ > **What's new in 0.4.12** — **OKF bundle import** + **DB-level audit enforcement** (audit 2026-09-27). The new `loop-memory import-okf <in_dir>` (and `POST /api/import/okf`) is the symmetric counterpart to `export-okf` (0.4.9) — bring an OKF v0.2 bundle back into Loop Memory as wiki pages, with idempotent upsert on `(scope, slug)`, optional `--dry-run` and `--skip-conflicts`. Source: `thecolourfoundation/rune` HN launch (MIT, 2026-09-25) + `okf-memory/okf-agent-memory` Issues #12-#15 + `Deja-Vu` (MIT, 2026-09-24) + Google OKF v0.2 spec (Apache-2.0, 2026-09-01). The new `MemoryStore._install_audit_triggers()` installs 12 SQLite triggers on `memories` / `wiki_pages` / `relations` / `consolidation_runs` so the `audit_log` table cannot be skipped — even a raw `sqlite3` writer that bypasses the Python wrapper still appends an audit row with `actor='db_trigger'`. New `MemoryStore.list_audit_log()` reads the log; `GET /api/stats` reports `audit_log` + `audit_triggers` counts. Source: `cogstate-eng/memory-eternal v0.4.3` `enforceAudit()` pattern (MIT, 2026-09-23). 31 new regression cases. [Full CHANGELOG →](./CHANGELOG.md#0412---2026-09-27)
70
+
71
+ > **What's new in 0.4.11** — **operational probes** +
72
+ **SQLite write-contention fix** (audit 2026-09-24). New
73
+ ``GET /api/healthz`` (liveness) + ``GET /api/readyz``
74
+ (readiness) endpoints let Kubernetes / launchd keepalive
75
+ polls probe the serve without provisioning a Bearer token;
76
+ ``/api/readyz`` returns 503 when the SQLite store is
77
+ unreachable. Also fixed a real concurrency bug: every
78
+ store connection now sets ``PRAGMA busy_timeout=5000``
79
+ (was 0 ms) so the watcher + serve + scheduler threads no
80
+ longer fail with ``database is locked`` when the
81
+ consolidator writes concurrently. 7 new regression cases.
82
+ [Full CHANGELOG →](./CHANGELOG.md#0411---2026-09-24)
83
+
69
84
  > **What's new in 0.4.10** — **path-safety guard** for the
70
85
  five export / snapshot endpoints. ``MemoryStore`` already
71
86
  validated query / wiki content; the new
@@ -22,6 +22,21 @@
22
22
 
23
23
  ---
24
24
 
25
+ > **What's new in 0.4.12** — **OKF bundle import** + **DB-level audit enforcement** (audit 2026-09-27). The new `loop-memory import-okf <in_dir>` (and `POST /api/import/okf`) is the symmetric counterpart to `export-okf` (0.4.9) — bring an OKF v0.2 bundle back into Loop Memory as wiki pages, with idempotent upsert on `(scope, slug)`, optional `--dry-run` and `--skip-conflicts`. Source: `thecolourfoundation/rune` HN launch (MIT, 2026-09-25) + `okf-memory/okf-agent-memory` Issues #12-#15 + `Deja-Vu` (MIT, 2026-09-24) + Google OKF v0.2 spec (Apache-2.0, 2026-09-01). The new `MemoryStore._install_audit_triggers()` installs 12 SQLite triggers on `memories` / `wiki_pages` / `relations` / `consolidation_runs` so the `audit_log` table cannot be skipped — even a raw `sqlite3` writer that bypasses the Python wrapper still appends an audit row with `actor='db_trigger'`. New `MemoryStore.list_audit_log()` reads the log; `GET /api/stats` reports `audit_log` + `audit_triggers` counts. Source: `cogstate-eng/memory-eternal v0.4.3` `enforceAudit()` pattern (MIT, 2026-09-23). 31 new regression cases. [Full CHANGELOG →](./CHANGELOG.md#0412---2026-09-27)
26
+
27
+ > **What's new in 0.4.11** — **operational probes** +
28
+ **SQLite write-contention fix** (audit 2026-09-24). New
29
+ ``GET /api/healthz`` (liveness) + ``GET /api/readyz``
30
+ (readiness) endpoints let Kubernetes / launchd keepalive
31
+ polls probe the serve without provisioning a Bearer token;
32
+ ``/api/readyz`` returns 503 when the SQLite store is
33
+ unreachable. Also fixed a real concurrency bug: every
34
+ store connection now sets ``PRAGMA busy_timeout=5000``
35
+ (was 0 ms) so the watcher + serve + scheduler threads no
36
+ longer fail with ``database is locked`` when the
37
+ consolidator writes concurrently. 7 new regression cases.
38
+ [Full CHANGELOG →](./CHANGELOG.md#0411---2026-09-24)
39
+
25
40
  > **What's new in 0.4.10** — **path-safety guard** for the
26
41
  five export / snapshot endpoints. ``MemoryStore`` already
27
42
  validated query / wiki content; the new
@@ -119,6 +119,23 @@ def _build_parser() -> argparse.ArgumentParser:
119
119
  eo.add_argument("--scope", default=None,
120
120
  help="Filter: 'global' or a single source token (e.g. codex)")
121
121
 
122
+ # Audit 2026-09-27: OKF v0.2 bundle import
123
+ # (thecolourfoundation/rune + okf-memory/okf-agent-memory Issues
124
+ # #12-#15 + Deja-Vu HN front-page). Symmetric counterpart to
125
+ # ``export-okf`` (0.4.9): bring an OKF bundle back into Loop
126
+ # Memory as wiki pages. Idempotent upsert on (scope, slug).
127
+ io = sub.add_parser(
128
+ "import-okf",
129
+ help="Import an OKF v0.2 bundle directory as wiki pages")
130
+ io.add_argument("in_dir",
131
+ help="Bundle directory containing index.md and pages/")
132
+ io.add_argument("--scope", default=None,
133
+ help="Scope tag for imported pages (default: bundle's index.md, then 'global')")
134
+ io.add_argument("--dry-run", action="store_true",
135
+ help="Parse every file but do not write")
136
+ io.add_argument("--skip-conflicts", action="store_true",
137
+ help="Don't overwrite an existing (scope, slug) row")
138
+
122
139
  return p
123
140
 
124
141
 
@@ -375,3 +392,32 @@ def run_export_okf(args) -> int:
375
392
  s = MemoryStore(ns.db)
376
393
  r = s.export_okf(ns.out_dir, scope_filter=ns.scope)
377
394
  return _emit(r)
395
+
396
+
397
+ def run_import_okf(args) -> int:
398
+ """``loop-memory import-okf <in_dir> [--scope S] [--dry-run] [--skip-conflicts]``
399
+
400
+ Audit 2026-09-27: OKF v0.2 bundle import
401
+ (thecolourfoundation/rune + okf-memory/okf-agent-memory Issues
402
+ #12-#15 + Deja-Vu HN front-page). Returns the summary dict
403
+ from ``MemoryStore.import_okf`` so the caller can confirm what
404
+ landed where. ``--dry-run`` parses every file but writes nothing;
405
+ ``--skip-conflicts`` turns INSERT-vs-existing conflicts into
406
+ a ``skipped`` counter rather than an UPDATE.
407
+ """
408
+ import argparse as _ap
409
+ p = _ap.ArgumentParser(prog="loop-memory import-okf")
410
+ p.add_argument("in_dir")
411
+ p.add_argument("--scope", default=None)
412
+ p.add_argument("--dry-run", action="store_true")
413
+ p.add_argument("--skip-conflicts", action="store_true")
414
+ p.add_argument("--db", default=os.environ.get("LOOP_MEMORY_DB", DEFAULT_DB))
415
+ ns = p.parse_args(args)
416
+ s = MemoryStore(ns.db)
417
+ r = s.import_okf(
418
+ ns.in_dir,
419
+ scope=ns.scope,
420
+ dry_run=ns.dry_run,
421
+ skip_conflicts=ns.skip_conflicts,
422
+ )
423
+ return _emit(r)
@@ -26,6 +26,7 @@ Usage:
26
26
  loop-memory export <out_dir> # write a MEMORY.md bundle (v7)
27
27
  loop-memory export-bundle <out_dir> # explicit v7 bundle alias
28
28
  loop-memory import <in_dir> # re-hydrate a bundle
29
+ loop-memory import-okf <in_dir> [--scope S] [--dry-run] [--skip-conflicts] # ingest an OKF v0.2 bundle as wiki pages (audit 2026-09-27)
29
30
  loop-memory fork [--branch-tag T] # snapshot every wiki page
30
31
  loop-memory graph-edge <src> <dst> [--kind K] [--weight W] # push a relation
31
32
  loop-memory subgraph <query> # print a small subgraph
@@ -128,6 +129,8 @@ COMMANDS = {
128
129
  # Audit 2026-09-20 — OKF v0.2 export (akitaonrails/ai-memory 2.0
129
130
  # + okf-agent-memory + Google OKF spec).
130
131
  "export-okf": cognitive_cmd.run_export_okf,
132
+ # Audit 2026-09-27: OKF v0.2 bundle import (rune + okf-agent-memory #12-#15 + Deja-Vu).
133
+ "import-okf": cognitive_cmd.run_import_okf,
131
134
  "rules": rules_cmd.run_rules,
132
135
  "version": _run_version,
133
136
  }
@@ -172,6 +175,7 @@ COMMAND_HELP: dict[str, str] = {
172
175
  "memory-stats": "loop-memory memory-stats <id> [--prefix] # per-memory lifetime stats (audit 2026-09-06, agentmemory v1.3.0).",
173
176
  "snapshot": "loop-memory snapshot <out.memory.sqlite> # write a portable SQLite snapshot (audit 2026-09-06, codexa-memory v0.2.0).",
174
177
  "export-okf": "loop-memory export-okf <out_dir> [--scope S] # write an OKF v0.2 bundle (audit 2026-09-20).",
178
+ "import-okf": "loop-memory import-okf <in_dir> [--scope S] [--dry-run] [--skip-conflicts] # ingest an OKF v0.2 bundle as wiki pages (audit 2026-09-27).",
175
179
  "restore": "loop-memory restore <in.memory.sqlite> # re-hydrate a portable SQLite snapshot.",
176
180
  # Audit 2026-09-13 — L0 outline recall (tigerless-labs/agent-memory v0.3.0).
177
181
  "recall-paths": "loop-memory recall-paths <query> [--limit N] # cheap L0 outline (id+abstract+score) instead of full text (audit 2026-09-13).",
@@ -176,6 +176,12 @@ def create_app(store: MemoryStore, static_dir: Path | None = None, scheduler=Non
176
176
  '/api/pipeline', '/api/weekly-report', '/api/llm-audit',
177
177
  '/api/source-health', '/api/write-guard', '/api/diag',
178
178
  '/api/install-hooks', '/api/insights',
179
+ # Audit 2026-09-24: probes are intentionally public so a
180
+ # load balancer / keepalive script doesn't need a Bearer
181
+ # token to ask "is the process up?". The DB-touching
182
+ # ``/api/readyz`` only exposes a coarse health signal
183
+ # (UP / DOWN), no data.
184
+ '/api/healthz', '/api/readyz',
179
185
  ))
180
186
 
181
187
  def _is_public_path(path: str) -> bool:
@@ -36,6 +36,41 @@ def register(app: FastAPI, store: MemoryStore, scheduler: Optional[Any] = None,
36
36
  """
37
37
  if static_dir is None:
38
38
  static_dir = Path(__file__).parent.parent / "static"
39
+ @app.get("/api/healthz")
40
+ def healthz():
41
+ """Liveness probe.
42
+
43
+ Audit 2026-09-24. Returns 200 as long as the process is
44
+ responsive. **Does not** touch the SQLite store — so the
45
+ probe cannot deadlock behind a slow writer and cannot give
46
+ a false negative when the DB is briefly busy. Use this for
47
+ ``livenessProbe`` in Kubernetes / launchd keepalive scripts.
48
+ """
49
+ return {"status": "ok", "ts": time.time()}
50
+
51
+ @app.get("/api/readyz")
52
+ def readyz():
53
+ """Readiness probe.
54
+
55
+ Audit 2026-09-24. Verifies the SQLite store is reachable AND
56
+ responsive (one trivial ``PRAGMA quick_check`` round-trip
57
+ inside a 1-second budget). Returns ``{"status": "ok"}`` on
58
+ success, or ``{"status": "degraded", "error": "..."}`` with
59
+ HTTP 503 when the DB is unhealthy. Use this for
60
+ ``readinessProbe`` so a degraded loop-memory is taken out of
61
+ a load-balancer rotation until it recovers.
62
+ """
63
+ import sqlite3 as _sq3
64
+ try:
65
+ with _sq3.connect(str(store.path), timeout=1.0) as c:
66
+ c.execute("PRAGMA quick_check").fetchone()
67
+ return {"status": "ok", "ts": time.time()}
68
+ except Exception as e:
69
+ return JSONResponse(
70
+ {"status": "degraded", "error": str(e)},
71
+ status_code=503,
72
+ )
73
+
39
74
  @app.get("/")
40
75
  def index():
41
76
  """Serve the dashboard HTML with the i18n JSONs inlined.
@@ -450,6 +485,49 @@ def register(app: FastAPI, store: MemoryStore, scheduler: Optional[Any] = None,
450
485
  raise HTTPException(500, f"export-okf failed: {e}")
451
486
  return r
452
487
 
488
+ @app.post("/api/import/okf")
489
+ def import_okf(body: dict):
490
+ """Audit 2026-09-27: OKF v0.2 bundle import.
491
+
492
+ Body shape::
493
+
494
+ {
495
+ "in_dir": "/abs/path/to/bundle",
496
+ "scope": "global", # optional
497
+ "dry_run": false, # optional
498
+ "skip_conflicts": false # optional
499
+ }
500
+
501
+ Walks ``<in_dir>/index.md`` + ``<in_dir>/pages/*.md`` and
502
+ upserts each page as a wiki row keyed on ``(scope, slug)``.
503
+ ``in_dir`` is validated by ``_safe_resolve_path`` (live DB
504
+ + system dirs are still refused; per-user temp is OK).
505
+ Returns the same summary dict as the CLI handler.
506
+ """
507
+ from ._shared import _safe_resolve_path as _safe_path
508
+ in_dir = (body.get("in_dir") or "").strip()
509
+ if not in_dir:
510
+ raise HTTPException(400, "in_dir is required")
511
+ scope = body.get("scope")
512
+ if scope is not None:
513
+ scope = str(scope).strip() or None
514
+ dry_run = bool(body.get("dry_run", False))
515
+ skip_conflicts = bool(body.get("skip_conflicts", False))
516
+ try:
517
+ safe_dir = _safe_path(in_dir, kind="read", live_db_path=str(store.path))
518
+ except ValueError as e:
519
+ raise HTTPException(400, str(e))
520
+ try:
521
+ r = store.import_okf(
522
+ safe_dir,
523
+ scope=scope,
524
+ dry_run=dry_run,
525
+ skip_conflicts=skip_conflicts,
526
+ )
527
+ except Exception as e:
528
+ raise HTTPException(500, f"import-okf failed: {e}")
529
+ return r
530
+
453
531
  @app.get("/api/recall/outline")
454
532
  def recall_outline(query: str = "", limit: int = 12,
455
533
  include: str = "memories,wiki,entities",