nexo-brain 2.3.0 → 2.3.1

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 (287) hide show
  1. package/README.md +1 -1
  2. package/package.json +6 -3
  3. package/src/auto_update.py +1 -0
  4. package/src/crons/sync.py +1 -2
  5. package/src/db/_core.py +1 -0
  6. package/src/db/_entities.py +1 -0
  7. package/src/db/_episodic.py +1 -0
  8. package/src/db/_learnings.py +1 -0
  9. package/src/db/_reminders.py +1 -0
  10. package/src/db/_sessions.py +1 -0
  11. package/src/db/_skills.py +1 -0
  12. package/src/plugin_loader.py +1 -0
  13. package/src/plugins/update.py +1 -0
  14. package/src/scripts/deep-sleep/apply_findings.py +1 -0
  15. package/src/scripts/deep-sleep/collect.py +1 -0
  16. package/src/scripts/deep-sleep/extract.py +1 -0
  17. package/src/scripts/deep-sleep/synthesize.py +1 -0
  18. package/src/scripts/nexo-learning-housekeep.py +1 -0
  19. package/src/scripts/nexo-watchdog.sh +19 -11
  20. package/src/server.py +1 -0
  21. package/src/tools_coordination.py +1 -0
  22. package/src/tools_sessions.py +1 -0
  23. package/scripts/migrate-to-unified 2.sh +0 -813
  24. package/scripts/migrate-to-unified.sh +0 -813
  25. package/scripts/migrate-v1.5-to-v1.6 2.py +0 -778
  26. package/scripts/migrate-v1.5-to-v1.6.py +0 -778
  27. package/scripts/migrate-v1.7-to-v1.8 2.py +0 -214
  28. package/scripts/migrate-v1.7-to-v1.8.py +0 -214
  29. package/scripts/nexo-preflight.sh +0 -236
  30. package/scripts/pre-commit-check 2.sh +0 -55
  31. package/scripts/pre-commit-check.sh +0 -55
  32. package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
  33. package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
  34. package/src/__pycache__/hnsw_index.cpython-310.pyc +0 -0
  35. package/src/__pycache__/hnsw_index.cpython-314.pyc +0 -0
  36. package/src/__pycache__/kg_populate.cpython-310.pyc +0 -0
  37. package/src/__pycache__/knowledge_graph.cpython-310.pyc +0 -0
  38. package/src/__pycache__/plugin_loader.cpython-310.pyc +0 -0
  39. package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
  40. package/src/__pycache__/tools_coordination.cpython-310.pyc +0 -0
  41. package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
  42. package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
  43. package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
  44. package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
  45. package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
  46. package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
  47. package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
  48. package/src/auto_close_sessions 2.py +0 -159
  49. package/src/auto_update 2.py +0 -634
  50. package/src/claim_graph 2.py +0 -323
  51. package/src/cognitive/__init__ 2.py +0 -62
  52. package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
  53. package/src/cognitive/__pycache__/__init__.cpython-312.pyc +0 -0
  54. package/src/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
  55. package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
  56. package/src/cognitive/__pycache__/_core.cpython-312.pyc +0 -0
  57. package/src/cognitive/__pycache__/_core.cpython-314.pyc +0 -0
  58. package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
  59. package/src/cognitive/__pycache__/_decay.cpython-312.pyc +0 -0
  60. package/src/cognitive/__pycache__/_decay.cpython-314.pyc +0 -0
  61. package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
  62. package/src/cognitive/__pycache__/_ingest.cpython-312.pyc +0 -0
  63. package/src/cognitive/__pycache__/_ingest.cpython-314.pyc +0 -0
  64. package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
  65. package/src/cognitive/__pycache__/_memory.cpython-312.pyc +0 -0
  66. package/src/cognitive/__pycache__/_memory.cpython-314.pyc +0 -0
  67. package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
  68. package/src/cognitive/__pycache__/_search.cpython-312.pyc +0 -0
  69. package/src/cognitive/__pycache__/_search.cpython-314.pyc +0 -0
  70. package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
  71. package/src/cognitive/__pycache__/_trust.cpython-312.pyc +0 -0
  72. package/src/cognitive/__pycache__/_trust.cpython-314.pyc +0 -0
  73. package/src/cognitive/_core 2.py +0 -567
  74. package/src/cognitive/_decay 2.py +0 -382
  75. package/src/cognitive/_ingest 2.py +0 -892
  76. package/src/cognitive/_memory 2.py +0 -912
  77. package/src/cognitive/_search 2.py +0 -949
  78. package/src/cognitive/_trust 2.py +0 -464
  79. package/src/crons/__pycache__/sync.cpython-314.pyc +0 -0
  80. package/src/crons/manifest 2.json +0 -106
  81. package/src/crons/sync 2.py +0 -217
  82. package/src/dashboard/__init__ 2.py +0 -0
  83. package/src/dashboard/__pycache__/__init__.cpython-310.pyc +0 -0
  84. package/src/dashboard/__pycache__/app.cpython-310.pyc +0 -0
  85. package/src/dashboard/app 2.py +0 -789
  86. package/src/db/__init__ 2.py +0 -89
  87. package/src/db/__pycache__/__init__.cpython-310.pyc +0 -0
  88. package/src/db/__pycache__/__init__.cpython-312.pyc +0 -0
  89. package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
  90. package/src/db/__pycache__/_core.cpython-310.pyc +0 -0
  91. package/src/db/__pycache__/_core.cpython-312.pyc +0 -0
  92. package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
  93. package/src/db/__pycache__/_credentials.cpython-310.pyc +0 -0
  94. package/src/db/__pycache__/_credentials.cpython-312.pyc +0 -0
  95. package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
  96. package/src/db/__pycache__/_cron_runs.cpython-310.pyc +0 -0
  97. package/src/db/__pycache__/_cron_runs.cpython-314.pyc +0 -0
  98. package/src/db/__pycache__/_entities.cpython-310.pyc +0 -0
  99. package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
  100. package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
  101. package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
  102. package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
  103. package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
  104. package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
  105. package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
  106. package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
  107. package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
  108. package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
  109. package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
  110. package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
  111. package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
  112. package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
  113. package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
  114. package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
  115. package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
  116. package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
  117. package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
  118. package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
  119. package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
  120. package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
  121. package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
  122. package/src/db/__pycache__/_skills.cpython-310.pyc +0 -0
  123. package/src/db/__pycache__/_skills.cpython-312.pyc +0 -0
  124. package/src/db/__pycache__/_skills.cpython-314.pyc +0 -0
  125. package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
  126. package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
  127. package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
  128. package/src/db/_core 2.py +0 -417
  129. package/src/db/_credentials 2.py +0 -124
  130. package/src/db/_entities 2.py +0 -178
  131. package/src/db/_episodic 2.py +0 -738
  132. package/src/db/_evolution 2.py +0 -54
  133. package/src/db/_fts 2.py +0 -406
  134. package/src/db/_learnings 2.py +0 -168
  135. package/src/db/_reminders 2.py +0 -338
  136. package/src/db/_schema 2.py +0 -364
  137. package/src/db/_sessions 2.py +0 -300
  138. package/src/db/_tasks 2.py +0 -91
  139. package/src/evolution_cycle 2.py +0 -266
  140. package/src/hnsw_index 2.py +0 -254
  141. package/src/hooks/auto_capture 2.py +0 -208
  142. package/src/hooks/caffeinate-guard 2.sh +0 -8
  143. package/src/hooks/capture-session 2.sh +0 -21
  144. package/src/hooks/capture-tool-logs 2.sh +0 -127
  145. package/src/hooks/daily-briefing-check 2.sh +0 -33
  146. package/src/hooks/inbox-hook 2.sh +0 -76
  147. package/src/hooks/post-compact 2.sh +0 -148
  148. package/src/hooks/pre-compact 2.sh +0 -151
  149. package/src/hooks/session-start 2.sh +0 -268
  150. package/src/hooks/session-stop 2.sh +0 -140
  151. package/src/kg_populate 2.py +0 -290
  152. package/src/knowledge_graph 2.py +0 -257
  153. package/src/maintenance 2.py +0 -59
  154. package/src/migrate_embeddings 2.py +0 -122
  155. package/src/plugin_loader 2.py +0 -202
  156. package/src/plugins/__init__ 2.py +0 -0
  157. package/src/plugins/__pycache__/__init__ 2.cpython-310.pyc +0 -0
  158. package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
  159. package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
  160. package/src/plugins/__pycache__/adaptive_mode 2.cpython-310.pyc +0 -0
  161. package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
  162. package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
  163. package/src/plugins/__pycache__/agents 2.cpython-310.pyc +0 -0
  164. package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
  165. package/src/plugins/__pycache__/artifact_registry 2.cpython-310.pyc +0 -0
  166. package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
  167. package/src/plugins/__pycache__/backup 2.cpython-310.pyc +0 -0
  168. package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
  169. package/src/plugins/__pycache__/cognitive_memory 2.cpython-310.pyc +0 -0
  170. package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
  171. package/src/plugins/__pycache__/core_rules 2.cpython-310.pyc +0 -0
  172. package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
  173. package/src/plugins/__pycache__/cortex 2.cpython-310.pyc +0 -0
  174. package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
  175. package/src/plugins/__pycache__/entities 2.cpython-310.pyc +0 -0
  176. package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
  177. package/src/plugins/__pycache__/episodic_memory 2.cpython-310.pyc +0 -0
  178. package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
  179. package/src/plugins/__pycache__/evolution 2.cpython-310.pyc +0 -0
  180. package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
  181. package/src/plugins/__pycache__/guard 2.cpython-310.pyc +0 -0
  182. package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
  183. package/src/plugins/__pycache__/knowledge_graph_tools 2.cpython-310.pyc +0 -0
  184. package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
  185. package/src/plugins/__pycache__/preferences 2.cpython-310.pyc +0 -0
  186. package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
  187. package/src/plugins/__pycache__/schedule.cpython-310.pyc +0 -0
  188. package/src/plugins/__pycache__/schedule.cpython-314.pyc +0 -0
  189. package/src/plugins/__pycache__/skills.cpython-310.pyc +0 -0
  190. package/src/plugins/__pycache__/skills.cpython-314.pyc +0 -0
  191. package/src/plugins/__pycache__/update 2.cpython-310.pyc +0 -0
  192. package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
  193. package/src/plugins/adaptive_mode 2.py +0 -805
  194. package/src/plugins/agents 2.py +0 -52
  195. package/src/plugins/artifact_registry 2.py +0 -450
  196. package/src/plugins/backup 2.py +0 -104
  197. package/src/plugins/cognitive_memory 2.py +0 -564
  198. package/src/plugins/core_rules 2.py +0 -252
  199. package/src/plugins/cortex 2.py +0 -299
  200. package/src/plugins/entities 2.py +0 -67
  201. package/src/plugins/episodic_memory 2.py +0 -533
  202. package/src/plugins/evolution 2.py +0 -115
  203. package/src/plugins/guard 2.py +0 -746
  204. package/src/plugins/knowledge_graph_tools 2.py +0 -105
  205. package/src/plugins/preferences 2.py +0 -47
  206. package/src/plugins/update 2.py +0 -256
  207. package/src/requirements 2.txt +0 -12
  208. package/src/rules/__init__ 2.py +0 -0
  209. package/src/rules/core-rules 2.json +0 -331
  210. package/src/rules/migrate 2.py +0 -207
  211. package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
  212. package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
  213. package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
  214. package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
  215. package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
  216. package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
  217. package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
  218. package/src/scripts/__pycache__/nexo-install.cpython-314.pyc +0 -0
  219. package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-314.pyc +0 -0
  220. package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
  221. package/src/scripts/__pycache__/nexo-migrate.cpython-314.pyc +0 -0
  222. package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
  223. package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
  224. package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
  225. package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
  226. package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
  227. package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
  228. package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
  229. package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
  230. package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
  231. package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
  232. package/src/scripts/check-context 2.py +0 -264
  233. package/src/scripts/nexo-auto-update 2.py +0 -6
  234. package/src/scripts/nexo-backup 2.sh +0 -25
  235. package/src/scripts/nexo-brain-activation 2.sh +0 -140
  236. package/src/scripts/nexo-catchup 2.py +0 -242
  237. package/src/scripts/nexo-cognitive-decay 2.py +0 -182
  238. package/src/scripts/nexo-daily-self-audit 2.py +0 -552
  239. package/src/scripts/nexo-deep-sleep 2.sh +0 -97
  240. package/src/scripts/nexo-evolution-run 2.py +0 -597
  241. package/src/scripts/nexo-followup-hygiene 2.py +0 -112
  242. package/src/scripts/nexo-github-monitor 2.py +0 -256
  243. package/src/scripts/nexo-immune 2.py +0 -927
  244. package/src/scripts/nexo-inbox-hook 2.sh +0 -74
  245. package/src/scripts/nexo-install 2.py +0 -6
  246. package/src/scripts/nexo-learning-housekeep 2.py +0 -245
  247. package/src/scripts/nexo-learning-validator 2.py +0 -207
  248. package/src/scripts/nexo-migrate 2.py +0 -232
  249. package/src/scripts/nexo-postmortem-consolidator 2.py +0 -421
  250. package/src/scripts/nexo-pre-commit 2.py +0 -120
  251. package/src/scripts/nexo-prevent-sleep 2.sh +0 -29
  252. package/src/scripts/nexo-proactive-dashboard 2.py +0 -345
  253. package/src/scripts/nexo-reflection 2.py +0 -253
  254. package/src/scripts/nexo-runtime-preflight 2.py +0 -274
  255. package/src/scripts/nexo-send-email 2.py +0 -25
  256. package/src/scripts/nexo-send-email.py +0 -25
  257. package/src/scripts/nexo-send-reply 2.py +0 -178
  258. package/src/scripts/nexo-send-reply.py +0 -178
  259. package/src/scripts/nexo-sleep 2.py +0 -592
  260. package/src/scripts/nexo-snapshot-restore 2.sh +0 -35
  261. package/src/scripts/nexo-synthesis 2.py +0 -253
  262. package/src/scripts/nexo-tcc-approve 2.sh +0 -79
  263. package/src/scripts/nexo-update 2.sh +0 -161
  264. package/src/scripts/nexo-watchdog 2.sh +0 -878
  265. package/src/scripts/nexo-watchdog-smoke 2.py +0 -119
  266. package/src/server 2.py +0 -733
  267. package/src/storage_router 2.py +0 -32
  268. package/src/tools_coordination 2.py +0 -102
  269. package/src/tools_credentials 2.py +0 -68
  270. package/src/tools_learnings 2.py +0 -220
  271. package/src/tools_menu 2.py +0 -227
  272. package/src/tools_reminders 2.py +0 -86
  273. package/src/tools_reminders_crud 2.py +0 -159
  274. package/src/tools_sessions 2.py +0 -476
  275. package/src/tools_task_history 2.py +0 -57
  276. package/templates/CLAUDE.md 2.template +0 -63
  277. package/templates/openclaw 2.json +0 -13
  278. package/tests/__init__ 2.py +0 -0
  279. package/tests/__init__.py +0 -0
  280. package/tests/conftest 2.py +0 -71
  281. package/tests/conftest.py +0 -71
  282. package/tests/test_cognitive 2.py +0 -205
  283. package/tests/test_cognitive.py +0 -205
  284. package/tests/test_knowledge_graph 2.py +0 -140
  285. package/tests/test_knowledge_graph.py +0 -140
  286. package/tests/test_migrations 2.py +0 -137
  287. package/tests/test_migrations.py +0 -137
@@ -1,208 +0,0 @@
1
- #!/usr/bin/env python3
2
- """NEXO Auto-Capture Hook — Extract facts from conversation context.
3
-
4
- Inspired by claude-mem's observation handler and transcript processor.
5
- Uses simple heuristics (no LLM) to extract decisions, corrections,
6
- and explicit facts from conversation messages.
7
-
8
- Can be called:
9
- - Programmatically via process_conversation()
10
- - From Claude Code hooks via stdin (pipe conversation lines)
11
- - As CLI: python3 auto_capture.py "message1" "message2" ...
12
-
13
- Stores extracted facts via cognitive.ingest() with appropriate tags.
14
- """
15
-
16
- import re
17
- import sys
18
- from pathlib import Path
19
-
20
- # Add source dir to path for cognitive imports
21
- sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
22
- import cognitive
23
-
24
-
25
- # ---------------------------------------------------------------------------
26
- # Pattern definitions (adapted from claude-mem's transcript processor
27
- # and ShieldCortex's pattern groups approach)
28
- # ---------------------------------------------------------------------------
29
-
30
- # Decision patterns — lines indicating a choice was made
31
- _DECISION_PATTERNS = [
32
- re.compile(r'\b(?:decided|agreed|will do|changed to|switching to|going with|chose|chosen|opted for)\b', re.IGNORECASE),
33
- re.compile(r'\b(?:let\'?s go with|the plan is|we\'?ll use|moving forward with)\b', re.IGNORECASE),
34
- re.compile(r'\b(?:approved|confirmed|locked in|finalized)\b', re.IGNORECASE),
35
- re.compile(r'\b(?:decidido|acordado|vamos con|cambiamos a|elegimos)\b', re.IGNORECASE), # Spanish
36
- ]
37
-
38
- # Correction patterns — lines indicating something was wrong
39
- _CORRECTION_PATTERNS = [
40
- re.compile(r'\b(?:don\'?t|stop|wrong|incorrect|that\'?s not right|fix this)\b', re.IGNORECASE),
41
- re.compile(r'\b(?:should be|actually|not that|the correct|mistake|error)\b', re.IGNORECASE),
42
- re.compile(r'\b(?:never do that|wrong approach|that broke|revert)\b', re.IGNORECASE),
43
- re.compile(r'\b(?:no,\s|nope|mal|otra vez|ya te dije|no es|est[aá] mal)\b', re.IGNORECASE), # Spanish
44
- ]
45
-
46
- # Explicit fact patterns — user explicitly asks to remember something
47
- _EXPLICIT_PATTERNS = [
48
- re.compile(r'\b(?:remember|note that|important:|keep in mind|don\'?t forget)\b', re.IGNORECASE),
49
- re.compile(r'\b(?:for future reference|take note|key point|rule:)\b', re.IGNORECASE),
50
- re.compile(r'\b(?:recuerda|importante:|ten en cuenta|no olvides|regla:)\b', re.IGNORECASE), # Spanish
51
- ]
52
-
53
- # Minimum line length to consider (skip very short lines)
54
- _MIN_LINE_LENGTH = 15
55
-
56
- # Maximum fact content length
57
- _MAX_FACT_LENGTH = 500
58
-
59
-
60
- def _classify_line(line: str) -> list[tuple[str, str]]:
61
- """Classify a single line into fact types.
62
-
63
- Returns list of (fact_type, content) tuples. A line can match
64
- multiple categories.
65
- """
66
- line = line.strip()
67
- if len(line) < _MIN_LINE_LENGTH:
68
- return []
69
-
70
- facts = []
71
-
72
- for pattern in _DECISION_PATTERNS:
73
- if pattern.search(line):
74
- facts.append(("decision", line))
75
- break
76
-
77
- for pattern in _CORRECTION_PATTERNS:
78
- if pattern.search(line):
79
- facts.append(("correction", line))
80
- break
81
-
82
- for pattern in _EXPLICIT_PATTERNS:
83
- if pattern.search(line):
84
- facts.append(("explicit", line))
85
- break
86
-
87
- return facts
88
-
89
-
90
- def process_conversation(messages: list[str]) -> dict:
91
- """Process conversation messages and extract key facts.
92
-
93
- Adapted from claude-mem's TranscriptEventProcessor: scans each message
94
- line for decision, correction, and explicit fact patterns. Stores
95
- extracted facts via cognitive.ingest() with source_type='auto_capture'.
96
-
97
- Args:
98
- messages: List of conversation message strings
99
-
100
- Returns:
101
- Dict with facts_extracted, decisions, corrections, stored,
102
- rejected_by_gate counts and extracted_facts details.
103
- """
104
- all_facts = []
105
- decisions = 0
106
- corrections = 0
107
- explicits = 0
108
-
109
- for msg in messages:
110
- # Split message into lines and classify each
111
- for line in msg.split("\n"):
112
- classified = _classify_line(line)
113
- for fact_type, content in classified:
114
- if fact_type == "decision":
115
- decisions += 1
116
- elif fact_type == "correction":
117
- corrections += 1
118
- elif fact_type == "explicit":
119
- explicits += 1
120
- all_facts.append((fact_type, content[:_MAX_FACT_LENGTH]))
121
-
122
- # Deduplicate by content (same line might appear in multiple messages)
123
- seen = set()
124
- unique_facts = []
125
- for fact_type, content in all_facts:
126
- content_key = content.lower().strip()
127
- if content_key not in seen:
128
- seen.add(content_key)
129
- unique_facts.append((fact_type, content))
130
-
131
- # Store via cognitive.ingest()
132
- stored = 0
133
- rejected_by_gate = 0
134
- extracted_details = []
135
-
136
- for fact_type, content in unique_facts:
137
- # Build tagged content for better retrieval
138
- tagged_content = f"[{fact_type.upper()}] {content}"
139
-
140
- result_id = cognitive.ingest(
141
- content=tagged_content,
142
- source_type="auto_capture",
143
- source_id=f"hook_{fact_type}",
144
- source_title=f"Auto-captured {fact_type}",
145
- domain="conversation",
146
- source="agent_observation",
147
- skip_quarantine=False, # Route through quarantine for safety
148
- bypass_gate=False, # Let prediction error gate filter duplicates
149
- )
150
-
151
- if result_id == 0:
152
- rejected_by_gate += 1
153
- else:
154
- stored += 1
155
-
156
- extracted_details.append({
157
- "type": fact_type,
158
- "content": content[:100],
159
- "stored": result_id != 0,
160
- "memory_id": result_id,
161
- })
162
-
163
- return {
164
- "facts_extracted": len(unique_facts),
165
- "decisions": decisions,
166
- "corrections": corrections,
167
- "explicits": explicits,
168
- "stored": stored,
169
- "rejected_by_gate": rejected_by_gate,
170
- "extracted_facts": extracted_details,
171
- }
172
-
173
-
174
- def _read_stdin() -> list[str]:
175
- """Read conversation lines from stdin (for hook integration)."""
176
- if sys.stdin.isatty():
177
- return []
178
- return [line for line in sys.stdin.read().strip().split("\n") if line.strip()]
179
-
180
-
181
- def main():
182
- """CLI entry point — accepts messages as args or from stdin.
183
-
184
- Usage:
185
- echo "We decided to use PostgreSQL" | python3 auto_capture.py
186
- python3 auto_capture.py "Remember: always use WAL mode" "That's wrong, fix it"
187
- """
188
- messages = list(sys.argv[1:]) if len(sys.argv) > 1 else _read_stdin()
189
-
190
- if not messages:
191
- print("Usage: python3 auto_capture.py 'message1' 'message2' ...")
192
- print(" or: echo 'messages' | python3 auto_capture.py")
193
- sys.exit(1)
194
-
195
- result = process_conversation(messages)
196
- print(f"Facts extracted: {result['facts_extracted']}")
197
- print(f" Decisions: {result['decisions']}")
198
- print(f" Corrections: {result['corrections']}")
199
- print(f" Explicits: {result['explicits']}")
200
- print(f"Stored: {result['stored']}, Rejected by gate: {result['rejected_by_gate']}")
201
-
202
- for fact in result["extracted_facts"]:
203
- status = "STORED" if fact["stored"] else "REJECTED"
204
- print(f" [{status}] [{fact['type']}] {fact['content']}")
205
-
206
-
207
- if __name__ == "__main__":
208
- main()
@@ -1,8 +0,0 @@
1
- #!/bin/bash
2
- # NEXO Caffeinate Guard — keeps the Mac awake so nocturnal processes run on schedule.
3
- # Runs as a LaunchAgent with KeepAlive=true. If killed, launchd restarts it.
4
- #
5
- # Uses caffeinate -s (prevent system sleep) with -i (prevent idle sleep).
6
- # The Mac screen can turn off but the system stays awake.
7
-
8
- exec caffeinate -s -i -w $$
@@ -1,21 +0,0 @@
1
- #!/bin/bash
2
- # NEXO PostToolUse hook — captures tool usage to session_buffer.jsonl
3
- # This feeds the Sensory Register (Atkinson-Shiffrin Layer 1)
4
-
5
- NEXO_HOME="${NEXO_HOME:-$HOME/.nexo}"
6
- BUFFER="$NEXO_HOME/brain/session_buffer.jsonl"
7
-
8
- mkdir -p "$NEXO_HOME/brain"
9
-
10
- # Capture basic event: timestamp + tool name
11
- # Read stdin (Claude Code passes JSON via stdin for PostToolUse hooks)
12
- INPUT=$(cat 2>/dev/null || true)
13
- TOOL_NAME="${CLAUDE_TOOL_NAME:-unknown}"
14
- TS=$(date -u +"%Y-%m-%dT%H:%M:%S")
15
-
16
- # Only log meaningful tool calls (skip reads, globs, greps)
17
- case "$TOOL_NAME" in
18
- Read|Glob|Grep|LS|Bash) exit 0 ;;
19
- esac
20
-
21
- echo "{\"ts\":\"$TS\",\"tool\":\"$TOOL_NAME\",\"source\":\"hook\"}" >> "$BUFFER"
@@ -1,127 +0,0 @@
1
- #!/bin/bash
2
- # NEXO PostToolUse hook — persists tool call outputs to daily JSONL logs
3
- # Fires automatically after every successful or failed tool use.
4
- # Logs survive context compactions.
5
- # Auto-cleanup: deletes logs >= 30 days old.
6
- # Optimized: skips read-only tools (Read, Grep, Glob, LS, Skill, ToolSearch).
7
-
8
- # Read full JSON from stdin first
9
- INPUT=$(cat || true)
10
- [ -z "$INPUT" ] && exit 0
11
-
12
- # Extract tool_name early and exit if read-only (avoids overhead on 90%+ of calls)
13
- TOOL_NAME=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_name',''))" 2>/dev/null || true)
14
-
15
- case "$TOOL_NAME" in
16
- Read|Grep|Glob|LS|Skill|ToolSearch) exit 0 ;;
17
- esac
18
-
19
- NEXO_HOME="${NEXO_HOME:-$HOME/.nexo}"
20
- LOG_DIR="$NEXO_HOME/operations/tool-logs"
21
- mkdir -p "$LOG_DIR"
22
-
23
- TODAY=$(date +%Y-%m-%d)
24
- LOG_FILE="$LOG_DIR/${TODAY}.jsonl"
25
-
26
- # Build and write record with python3 (faster than jq on macOS when cached)
27
- echo "$INPUT" | python3 -c "
28
- import json, sys
29
- from datetime import datetime
30
- d = json.load(sys.stdin)
31
- record = {
32
- 'timestamp': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),
33
- 'session_id': d.get('session_id', 'unknown'),
34
- 'tool_name': d.get('tool_name', 'unknown'),
35
- 'hook_event': d.get('hook_event_name', 'unknown'),
36
- 'tool_use_id': d.get('tool_use_id'),
37
- 'tool_input': d.get('tool_input'),
38
- 'tool_response': d.get('tool_response'),
39
- 'error': d.get('error')
40
- }
41
- print(json.dumps(record))
42
- " >> "$LOG_FILE" 2>/dev/null
43
-
44
- # ── Layer 1: Auto-diary every 10 tool calls ─────────────────────────
45
- COUNTER_FILE="$NEXO_HOME/operations/.tool-call-count"
46
- NEXO_DB="$NEXO_HOME/data/nexo.db"
47
-
48
- # Increment counter (atomic: read+write in one step)
49
- COUNT=1
50
- if [ -f "$COUNTER_FILE" ]; then
51
- COUNT=$(( $(cat "$COUNTER_FILE" 2>/dev/null || echo 0) + 1 ))
52
- fi
53
- echo "$COUNT" > "$COUNTER_FILE"
54
-
55
- # Every 10 tool calls, write a mechanical diary draft to SQLite
56
- if [ $(( COUNT % 10 )) -eq 0 ] && [ -f "$NEXO_DB" ]; then
57
- python3 -c "
58
- import json, sqlite3, os, sys
59
- from datetime import datetime
60
-
61
- db_path = '$NEXO_DB'
62
- log_file = '$LOG_FILE'
63
- count = $COUNT
64
-
65
- # Read last 10 tool calls from today's log
66
- entries = []
67
- if os.path.isfile(log_file):
68
- with open(log_file, 'r') as f:
69
- lines = f.readlines()
70
- for line in lines[-10:]:
71
- try:
72
- e = json.loads(line.strip())
73
- name = e.get('tool_name', '?')
74
- inp = e.get('tool_input', {})
75
- # Brief args: first key's value, truncated
76
- brief = ''
77
- if isinstance(inp, dict):
78
- for k, v in list(inp.items())[:1]:
79
- brief = str(v)[:60]
80
- entries.append(f'{name}({brief})')
81
- except Exception:
82
- pass
83
-
84
- if not entries:
85
- sys.exit(0)
86
-
87
- tools_summary = ', '.join(entries[-10:])
88
-
89
- # Get current session and task from sessions table
90
- conn = sqlite3.connect(db_path, timeout=2)
91
- conn.row_factory = sqlite3.Row
92
- row = conn.execute(
93
- 'SELECT sid, task FROM sessions ORDER BY last_update_epoch DESC LIMIT 1'
94
- ).fetchone()
95
- if not row:
96
- conn.close()
97
- sys.exit(0)
98
-
99
- sid = row['sid']
100
- task = row['task'] or 'unknown'
101
-
102
- summary = f'[AUTO-{count}] {len(entries)} tool calls: {tools_summary[:250]}. Task: {task[:100]}'
103
-
104
- # Write to session_diary_draft (UPSERT)
105
- conn.execute('''
106
- INSERT INTO session_diary_draft (sid, summary_draft, tasks_seen, change_ids, decision_ids, last_context_hint, heartbeat_count, updated_at)
107
- VALUES (?, ?, '[]', '[]', '[]', ?, 0, datetime('now'))
108
- ON CONFLICT(sid) DO UPDATE SET
109
- summary_draft = excluded.summary_draft,
110
- last_context_hint = excluded.last_context_hint,
111
- updated_at = datetime('now')
112
- ''', (sid, summary, f'auto-diary at {count} tool calls'))
113
- conn.commit()
114
- conn.close()
115
- " 2>/dev/null &
116
- # Reset counter after writing
117
- echo "0" > "$COUNTER_FILE"
118
- fi
119
-
120
- # Cleanup: delete logs >= 30 days old (once daily, uses marker file)
121
- CLEANUP_MARKER="$LOG_DIR/.last-cleanup"
122
- if [ ! -f "$CLEANUP_MARKER" ] || [ "$(cat "$CLEANUP_MARKER" 2>/dev/null)" != "$TODAY" ]; then
123
- find "$LOG_DIR" -name "*.jsonl" -mtime +30 -delete 2>/dev/null || true
124
- echo "$TODAY" > "$CLEANUP_MARKER"
125
- fi
126
-
127
- exit 0
@@ -1,33 +0,0 @@
1
- #!/bin/bash
2
- # NEXO Daily Briefing — SessionStart hook
3
- # Checks if a briefing should be sent and creates a flag for NEXO to process.
4
- # Does NOT send the email directly (needs Claude to research news).
5
- # Only marks that NEXO should launch the briefing at startup.
6
- # Frequency: Monday, Wednesday, Friday (3x/week)
7
-
8
- NEXO_HOME="${NEXO_HOME:-$HOME/.nexo}"
9
- BRIEFING_FILE="$NEXO_HOME/operations/.briefing-last-sent"
10
- FLAG_FILE="$NEXO_HOME/operations/.briefing-pending"
11
- TODAY=$(date +%Y-%m-%d)
12
- HOUR=$(date +%H)
13
- DOW=$(date +%u) # 1=Monday, 7=Sunday
14
-
15
- # Only after 8:00 AM — before that counts as "previous day"
16
- if [ "$HOUR" -lt 8 ]; then
17
- exit 0
18
- fi
19
-
20
- # Only Monday (1), Wednesday (3), Friday (5)
21
- if [ "$DOW" != "1" ] && [ "$DOW" != "3" ] && [ "$DOW" != "5" ]; then
22
- exit 0
23
- fi
24
-
25
- # If already sent today, skip
26
- LAST_SENT=$(cat "$BRIEFING_FILE" 2>/dev/null)
27
- if [ "$LAST_SENT" = "$TODAY" ]; then
28
- exit 0
29
- fi
30
-
31
- # Mark briefing as pending for NEXO to launch in background
32
- echo "$TODAY" > "$FLAG_FILE"
33
- echo "briefing-pending"
@@ -1,76 +0,0 @@
1
- #!/bin/bash
2
- # NEXO PostToolUse hook — automatic inter-terminal inbox check
3
- #
4
- # Zero output when no messages = zero tokens consumed in Claude's context.
5
- # Reads SQLite directly (no MCP overhead). Write-only: INSERT OR IGNORE for mark-as-read.
6
- # Debounce: skips if last check was <2 seconds ago.
7
-
8
- INPUT=$(cat || true)
9
- [ -z "$INPUT" ] && exit 0
10
-
11
- # 1. Skip read-only tools (same logic as capture-tool-logs.sh)
12
- TOOL_NAME=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_name',''))" 2>/dev/null || true)
13
- case "$TOOL_NAME" in
14
- Read|Grep|Glob|LS|Skill|ToolSearch|Agent) exit 0 ;;
15
- esac
16
-
17
- # 2. Extract Claude Code session_id
18
- CLAUDE_SID=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('session_id',''))" 2>/dev/null)
19
- [ -z "$CLAUDE_SID" ] && exit 0
20
-
21
- # 3. Debounce: skip if last check <2s ago
22
- DEBOUNCE_FILE="/tmp/nexo-inbox-ts-${CLAUDE_SID}"
23
- NOW=$(date +%s)
24
- LAST=$(cat "$DEBOUNCE_FILE" 2>/dev/null || echo 0)
25
- DIFF=$((NOW - LAST))
26
- [ "$DIFF" -lt 2 ] && exit 0
27
- echo "$NOW" > "$DEBOUNCE_FILE"
28
-
29
- # 4. Find NEXO SID mapped to this Claude session_id
30
- NEXO_HOME="${NEXO_HOME:-$HOME/.nexo}"
31
- DB="$NEXO_HOME/data/nexo.db"
32
- mkdir -p "$NEXO_HOME/data"
33
- [ -f "$DB" ] || exit 0
34
-
35
- NEXO_SID=$(sqlite3 "$DB" "SELECT sid FROM sessions WHERE claude_session_id = '${CLAUDE_SID}' AND last_update_epoch > (strftime('%s','now') - 900) ORDER BY last_update_epoch DESC LIMIT 1;" 2>/dev/null)
36
- [ -z "$NEXO_SID" ] && exit 0
37
-
38
- # 5. Check inbox — messages addressed to this session or broadcast
39
- MESSAGES=$(sqlite3 -separator '|' "$DB" "
40
- SELECT m.id, m.from_sid, m.text FROM messages m
41
- WHERE (m.to_sid = 'all' OR m.to_sid = '${NEXO_SID}')
42
- AND m.from_sid != '${NEXO_SID}'
43
- AND m.id NOT IN (SELECT message_id FROM message_reads WHERE sid = '${NEXO_SID}')
44
- LIMIT 5;
45
- " 2>/dev/null)
46
-
47
- # 6. Check pending questions
48
- QUESTIONS=$(sqlite3 -separator '|' "$DB" "
49
- SELECT qid, from_sid, question FROM questions
50
- WHERE to_sid = '${NEXO_SID}' AND answer IS NULL
51
- LIMIT 3;
52
- " 2>/dev/null)
53
-
54
- # 7. If empty -> silent exit (0 tokens consumed)
55
- [ -z "$MESSAGES" ] && [ -z "$QUESTIONS" ] && exit 0
56
-
57
- # 8. Format and output (injected into Claude's context)
58
- echo ""
59
- echo "INTER-TERMINAL MESSAGE (auto-detected):"
60
-
61
- if [ -n "$MESSAGES" ]; then
62
- echo "$MESSAGES" | while IFS='|' read -r mid from text; do
63
- echo " [$from]: $text"
64
- # Mark as read (lightweight INSERT, WAL mode, no lock contention)
65
- sqlite3 "$DB" "INSERT OR IGNORE INTO message_reads (message_id, sid) VALUES ('${mid}', '${NEXO_SID}');" 2>/dev/null
66
- done
67
- fi
68
-
69
- if [ -n "$QUESTIONS" ]; then
70
- echo " PENDING QUESTIONS from another terminal — respond with nexo_answer:"
71
- echo "$QUESTIONS" | while IFS='|' read -r qid from question; do
72
- echo " Q[$qid] from [$from]: $question"
73
- done
74
- fi
75
-
76
- exit 0
@@ -1,148 +0,0 @@
1
- #!/bin/bash
2
- # NEXO PostCompact Hook — Re-inject Core Memory Block after compaction
3
- # Reads the latest session checkpoint from SQLite and generates a structured
4
- # context block that preserves session continuity.
5
- set -uo pipefail
6
-
7
- NEXO_HOME="${NEXO_HOME:-$HOME/.nexo}"
8
- NEXO_DB="$NEXO_HOME/data/nexo.db"
9
- mkdir -p "$NEXO_HOME/data"
10
- TODAY=$(date +%Y-%m-%d)
11
- LOG_FILE="$NEXO_HOME/operations/tool-logs/${TODAY}.jsonl"
12
- LOG_LINES=0
13
- if [ -f "$LOG_FILE" ]; then
14
- LOG_LINES=$(wc -l < "$LOG_FILE" | tr -d ' ')
15
- fi
16
-
17
- # Read checkpoint for the session that just compacted
18
- # PreCompact writes the SID to /tmp/nexo-compacting-sid
19
- TARGET_SID=""
20
- if [ -f /tmp/nexo-compacting-sid ]; then
21
- TARGET_SID=$(cat /tmp/nexo-compacting-sid 2>/dev/null || echo "")
22
- rm -f /tmp/nexo-compacting-sid
23
- fi
24
-
25
- CHECKPOINT=""
26
- if [ -f "$NEXO_DB" ]; then
27
- if [ -n "$TARGET_SID" ]; then
28
- # Read checkpoint for the specific session that compacted
29
- CHECKPOINT=$(sqlite3 "$NEXO_DB" "
30
- SELECT sid, task, task_status, active_files, current_goal,
31
- decisions_summary, errors_found, reasoning_thread,
32
- next_step, compaction_count
33
- FROM session_checkpoints
34
- WHERE sid = '$TARGET_SID'
35
- " 2>/dev/null || echo "")
36
- fi
37
- # Fallback: if no target SID or no checkpoint found, use latest
38
- if [ -z "$CHECKPOINT" ]; then
39
- CHECKPOINT=$(sqlite3 "$NEXO_DB" "
40
- SELECT sid, task, task_status, active_files, current_goal,
41
- decisions_summary, errors_found, reasoning_thread,
42
- next_step, compaction_count
43
- FROM session_checkpoints
44
- ORDER BY updated_at DESC LIMIT 1
45
- " 2>/dev/null || echo "")
46
- fi
47
-
48
- if [ -n "$CHECKPOINT" ]; then
49
- # Parse pipe-separated fields
50
- SID=$(echo "$CHECKPOINT" | cut -d'|' -f1)
51
- TASK=$(echo "$CHECKPOINT" | cut -d'|' -f2)
52
- TASK_STATUS=$(echo "$CHECKPOINT" | cut -d'|' -f3)
53
- ACTIVE_FILES=$(echo "$CHECKPOINT" | cut -d'|' -f4)
54
- CURRENT_GOAL=$(echo "$CHECKPOINT" | cut -d'|' -f5)
55
- DECISIONS=$(echo "$CHECKPOINT" | cut -d'|' -f6)
56
- ERRORS=$(echo "$CHECKPOINT" | cut -d'|' -f7)
57
- REASONING=$(echo "$CHECKPOINT" | cut -d'|' -f8)
58
- NEXT_STEP=$(echo "$CHECKPOINT" | cut -d'|' -f9)
59
- COMPACT_COUNT=$(echo "$CHECKPOINT" | cut -d'|' -f10)
60
-
61
- # Increment compaction count
62
- sqlite3 "$NEXO_DB" "
63
- UPDATE session_checkpoints
64
- SET compaction_count = compaction_count + 1, updated_at = datetime('now')
65
- WHERE sid = '$SID'
66
- " 2>/dev/null || true
67
-
68
- # Read diary draft for extra context
69
- DRAFT=$(sqlite3 "$NEXO_DB" "
70
- SELECT tasks_seen, last_context_hint
71
- FROM session_diary_draft
72
- WHERE sid = '$SID'
73
- " 2>/dev/null || echo "")
74
-
75
- TASKS_SEEN=""
76
- LAST_HINT=""
77
- if [ -n "$DRAFT" ]; then
78
- TASKS_SEEN=$(echo "$DRAFT" | cut -d'|' -f1)
79
- LAST_HINT=$(echo "$DRAFT" | cut -d'|' -f2)
80
- fi
81
-
82
- # Build Core Memory Block
83
- BLOCK="## SESSION CONTINUITY [auto-injected post-compaction #$((COMPACT_COUNT + 1))]"
84
- BLOCK="$BLOCK\n**Session:** $SID"
85
- BLOCK="$BLOCK\n**Task:** $TASK (status: $TASK_STATUS)"
86
-
87
- if [ -n "$CURRENT_GOAL" ] && [ "$CURRENT_GOAL" != "$TASK" ]; then
88
- BLOCK="$BLOCK\n**Goal:** $CURRENT_GOAL"
89
- fi
90
-
91
- if [ -n "$ACTIVE_FILES" ] && [ "$ACTIVE_FILES" != "[]" ]; then
92
- BLOCK="$BLOCK\n**Files:** $ACTIVE_FILES"
93
- fi
94
-
95
- if [ -n "$DECISIONS" ]; then
96
- BLOCK="$BLOCK\n**Decisions:** $DECISIONS"
97
- fi
98
-
99
- if [ -n "$ERRORS" ]; then
100
- BLOCK="$BLOCK\n**Errors:** $ERRORS"
101
- fi
102
-
103
- if [ -n "$NEXT_STEP" ]; then
104
- BLOCK="$BLOCK\n**Next:** $NEXT_STEP"
105
- fi
106
-
107
- if [ -n "$REASONING" ]; then
108
- BLOCK="$BLOCK\n**Context:** $REASONING"
109
- fi
110
-
111
- if [ -n "$LAST_HINT" ]; then
112
- BLOCK="$BLOCK\n**Last context:** $LAST_HINT"
113
- fi
114
-
115
- if [ -n "$TASKS_SEEN" ] && [ "$TASKS_SEEN" != "[]" ]; then
116
- BLOCK="$BLOCK\n**Session tasks so far:** $TASKS_SEEN"
117
- fi
118
-
119
- BLOCK="$BLOCK\n**Tool logs:** ${NEXO_HOME}/operations/tool-logs/${TODAY}.jsonl ($LOG_LINES entries)"
120
- BLOCK="$BLOCK\n\n**POST-COMPACTION INSTRUCTIONS:**"
121
- BLOCK="$BLOCK\n1. Call nexo_heartbeat with the SID above to reconnect with the session"
122
- BLOCK="$BLOCK\n2. If you need specific lost data, query tool logs with jq"
123
- BLOCK="$BLOCK\n3. Continue the task from where it left off — do NOT start from zero"
124
- BLOCK="$BLOCK\n4. MCP tools (nexo_*) have all persistent state"
125
-
126
- # Escape for JSON
127
- BLOCK_ESCAPED=$(echo -e "$BLOCK" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
128
-
129
- cat << HOOKEOF
130
- {
131
- "systemMessage": $BLOCK_ESCAPED
132
- }
133
- HOOKEOF
134
- else
135
- # No checkpoint — fallback to basic message
136
- cat << 'HOOKEOF'
137
- {
138
- "systemMessage": "Post-compaction: no prior checkpoint found. Call nexo_heartbeat to reconnect session state."
139
- }
140
- HOOKEOF
141
- fi
142
- else
143
- cat << 'HOOKEOF'
144
- {
145
- "systemMessage": "Post-compaction: nexo.db not found. Reconnect via nexo_startup."
146
- }
147
- HOOKEOF
148
- fi