nexo-brain 2.3.0 → 2.3.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 (299) hide show
  1. package/README.md +1 -1
  2. package/bin/nexo-brain.js +92 -9
  3. package/bin/postinstall.js +22 -15
  4. package/package.json +7 -4
  5. package/src/auto_update.py +194 -5
  6. package/src/crons/sync.py +6 -2
  7. package/src/db/_core.py +1 -0
  8. package/src/db/_entities.py +1 -0
  9. package/src/db/_episodic.py +1 -0
  10. package/src/db/_learnings.py +1 -0
  11. package/src/db/_reminders.py +1 -0
  12. package/src/db/_schema.py +11 -1
  13. package/src/db/_sessions.py +1 -0
  14. package/src/db/_skills.py +1 -0
  15. package/src/hooks/capture-tool-logs.sh +23 -6
  16. package/src/hooks/session-start.sh +4 -3
  17. package/src/plugin_loader.py +1 -0
  18. package/src/plugins/update.py +377 -26
  19. package/src/scripts/deep-sleep/apply_findings.py +1 -0
  20. package/src/scripts/deep-sleep/collect.py +1 -0
  21. package/src/scripts/deep-sleep/extract.py +1 -0
  22. package/src/scripts/deep-sleep/synthesize.py +1 -0
  23. package/src/scripts/nexo-catchup.py +29 -4
  24. package/src/scripts/nexo-daily-self-audit.py +21 -1
  25. package/src/scripts/nexo-evolution-run.py +21 -1
  26. package/src/scripts/nexo-learning-housekeep.py +1 -0
  27. package/src/scripts/nexo-postmortem-consolidator.py +34 -9
  28. package/src/scripts/nexo-sleep.py +32 -10
  29. package/src/scripts/nexo-synthesis.py +29 -9
  30. package/src/scripts/nexo-update.sh +109 -7
  31. package/src/scripts/nexo-watchdog.sh +122 -58
  32. package/src/server.py +66 -1
  33. package/src/tools_coordination.py +1 -0
  34. package/src/tools_sessions.py +1 -0
  35. package/scripts/migrate-to-unified 2.sh +0 -813
  36. package/scripts/migrate-to-unified.sh +0 -813
  37. package/scripts/migrate-v1.5-to-v1.6 2.py +0 -778
  38. package/scripts/migrate-v1.5-to-v1.6.py +0 -778
  39. package/scripts/migrate-v1.7-to-v1.8 2.py +0 -214
  40. package/scripts/migrate-v1.7-to-v1.8.py +0 -214
  41. package/scripts/nexo-preflight.sh +0 -236
  42. package/scripts/pre-commit-check 2.sh +0 -55
  43. package/scripts/pre-commit-check.sh +0 -55
  44. package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
  45. package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
  46. package/src/__pycache__/hnsw_index.cpython-310.pyc +0 -0
  47. package/src/__pycache__/hnsw_index.cpython-314.pyc +0 -0
  48. package/src/__pycache__/kg_populate.cpython-310.pyc +0 -0
  49. package/src/__pycache__/knowledge_graph.cpython-310.pyc +0 -0
  50. package/src/__pycache__/plugin_loader.cpython-310.pyc +0 -0
  51. package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
  52. package/src/__pycache__/tools_coordination.cpython-310.pyc +0 -0
  53. package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
  54. package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
  55. package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
  56. package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
  57. package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
  58. package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
  59. package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
  60. package/src/auto_close_sessions 2.py +0 -159
  61. package/src/auto_update 2.py +0 -634
  62. package/src/claim_graph 2.py +0 -323
  63. package/src/cognitive/__init__ 2.py +0 -62
  64. package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
  65. package/src/cognitive/__pycache__/__init__.cpython-312.pyc +0 -0
  66. package/src/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
  67. package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
  68. package/src/cognitive/__pycache__/_core.cpython-312.pyc +0 -0
  69. package/src/cognitive/__pycache__/_core.cpython-314.pyc +0 -0
  70. package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
  71. package/src/cognitive/__pycache__/_decay.cpython-312.pyc +0 -0
  72. package/src/cognitive/__pycache__/_decay.cpython-314.pyc +0 -0
  73. package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
  74. package/src/cognitive/__pycache__/_ingest.cpython-312.pyc +0 -0
  75. package/src/cognitive/__pycache__/_ingest.cpython-314.pyc +0 -0
  76. package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
  77. package/src/cognitive/__pycache__/_memory.cpython-312.pyc +0 -0
  78. package/src/cognitive/__pycache__/_memory.cpython-314.pyc +0 -0
  79. package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
  80. package/src/cognitive/__pycache__/_search.cpython-312.pyc +0 -0
  81. package/src/cognitive/__pycache__/_search.cpython-314.pyc +0 -0
  82. package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
  83. package/src/cognitive/__pycache__/_trust.cpython-312.pyc +0 -0
  84. package/src/cognitive/__pycache__/_trust.cpython-314.pyc +0 -0
  85. package/src/cognitive/_core 2.py +0 -567
  86. package/src/cognitive/_decay 2.py +0 -382
  87. package/src/cognitive/_ingest 2.py +0 -892
  88. package/src/cognitive/_memory 2.py +0 -912
  89. package/src/cognitive/_search 2.py +0 -949
  90. package/src/cognitive/_trust 2.py +0 -464
  91. package/src/crons/__pycache__/sync.cpython-314.pyc +0 -0
  92. package/src/crons/manifest 2.json +0 -106
  93. package/src/crons/sync 2.py +0 -217
  94. package/src/dashboard/__init__ 2.py +0 -0
  95. package/src/dashboard/__pycache__/__init__.cpython-310.pyc +0 -0
  96. package/src/dashboard/__pycache__/app.cpython-310.pyc +0 -0
  97. package/src/dashboard/app 2.py +0 -789
  98. package/src/db/__init__ 2.py +0 -89
  99. package/src/db/__pycache__/__init__.cpython-310.pyc +0 -0
  100. package/src/db/__pycache__/__init__.cpython-312.pyc +0 -0
  101. package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
  102. package/src/db/__pycache__/_core.cpython-310.pyc +0 -0
  103. package/src/db/__pycache__/_core.cpython-312.pyc +0 -0
  104. package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
  105. package/src/db/__pycache__/_credentials.cpython-310.pyc +0 -0
  106. package/src/db/__pycache__/_credentials.cpython-312.pyc +0 -0
  107. package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
  108. package/src/db/__pycache__/_cron_runs.cpython-310.pyc +0 -0
  109. package/src/db/__pycache__/_cron_runs.cpython-314.pyc +0 -0
  110. package/src/db/__pycache__/_entities.cpython-310.pyc +0 -0
  111. package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
  112. package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
  113. package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
  114. package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
  115. package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
  116. package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
  117. package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
  118. package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
  119. package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
  120. package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
  121. package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
  122. package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
  123. package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
  124. package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
  125. package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
  126. package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
  127. package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
  128. package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
  129. package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
  130. package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
  131. package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
  132. package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
  133. package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
  134. package/src/db/__pycache__/_skills.cpython-310.pyc +0 -0
  135. package/src/db/__pycache__/_skills.cpython-312.pyc +0 -0
  136. package/src/db/__pycache__/_skills.cpython-314.pyc +0 -0
  137. package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
  138. package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
  139. package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
  140. package/src/db/_core 2.py +0 -417
  141. package/src/db/_credentials 2.py +0 -124
  142. package/src/db/_entities 2.py +0 -178
  143. package/src/db/_episodic 2.py +0 -738
  144. package/src/db/_evolution 2.py +0 -54
  145. package/src/db/_fts 2.py +0 -406
  146. package/src/db/_learnings 2.py +0 -168
  147. package/src/db/_reminders 2.py +0 -338
  148. package/src/db/_schema 2.py +0 -364
  149. package/src/db/_sessions 2.py +0 -300
  150. package/src/db/_tasks 2.py +0 -91
  151. package/src/evolution_cycle 2.py +0 -266
  152. package/src/hnsw_index 2.py +0 -254
  153. package/src/hooks/auto_capture 2.py +0 -208
  154. package/src/hooks/caffeinate-guard 2.sh +0 -8
  155. package/src/hooks/capture-session 2.sh +0 -21
  156. package/src/hooks/capture-tool-logs 2.sh +0 -127
  157. package/src/hooks/daily-briefing-check 2.sh +0 -33
  158. package/src/hooks/inbox-hook 2.sh +0 -76
  159. package/src/hooks/post-compact 2.sh +0 -148
  160. package/src/hooks/pre-compact 2.sh +0 -151
  161. package/src/hooks/session-start 2.sh +0 -268
  162. package/src/hooks/session-stop 2.sh +0 -140
  163. package/src/kg_populate 2.py +0 -290
  164. package/src/knowledge_graph 2.py +0 -257
  165. package/src/maintenance 2.py +0 -59
  166. package/src/migrate_embeddings 2.py +0 -122
  167. package/src/plugin_loader 2.py +0 -202
  168. package/src/plugins/__init__ 2.py +0 -0
  169. package/src/plugins/__pycache__/__init__ 2.cpython-310.pyc +0 -0
  170. package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
  171. package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
  172. package/src/plugins/__pycache__/adaptive_mode 2.cpython-310.pyc +0 -0
  173. package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
  174. package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
  175. package/src/plugins/__pycache__/agents 2.cpython-310.pyc +0 -0
  176. package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
  177. package/src/plugins/__pycache__/artifact_registry 2.cpython-310.pyc +0 -0
  178. package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
  179. package/src/plugins/__pycache__/backup 2.cpython-310.pyc +0 -0
  180. package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
  181. package/src/plugins/__pycache__/cognitive_memory 2.cpython-310.pyc +0 -0
  182. package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
  183. package/src/plugins/__pycache__/core_rules 2.cpython-310.pyc +0 -0
  184. package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
  185. package/src/plugins/__pycache__/cortex 2.cpython-310.pyc +0 -0
  186. package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
  187. package/src/plugins/__pycache__/entities 2.cpython-310.pyc +0 -0
  188. package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
  189. package/src/plugins/__pycache__/episodic_memory 2.cpython-310.pyc +0 -0
  190. package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
  191. package/src/plugins/__pycache__/evolution 2.cpython-310.pyc +0 -0
  192. package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
  193. package/src/plugins/__pycache__/guard 2.cpython-310.pyc +0 -0
  194. package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
  195. package/src/plugins/__pycache__/knowledge_graph_tools 2.cpython-310.pyc +0 -0
  196. package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
  197. package/src/plugins/__pycache__/preferences 2.cpython-310.pyc +0 -0
  198. package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
  199. package/src/plugins/__pycache__/schedule.cpython-310.pyc +0 -0
  200. package/src/plugins/__pycache__/schedule.cpython-314.pyc +0 -0
  201. package/src/plugins/__pycache__/skills.cpython-310.pyc +0 -0
  202. package/src/plugins/__pycache__/skills.cpython-314.pyc +0 -0
  203. package/src/plugins/__pycache__/update 2.cpython-310.pyc +0 -0
  204. package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
  205. package/src/plugins/adaptive_mode 2.py +0 -805
  206. package/src/plugins/agents 2.py +0 -52
  207. package/src/plugins/artifact_registry 2.py +0 -450
  208. package/src/plugins/backup 2.py +0 -104
  209. package/src/plugins/cognitive_memory 2.py +0 -564
  210. package/src/plugins/core_rules 2.py +0 -252
  211. package/src/plugins/cortex 2.py +0 -299
  212. package/src/plugins/entities 2.py +0 -67
  213. package/src/plugins/episodic_memory 2.py +0 -533
  214. package/src/plugins/evolution 2.py +0 -115
  215. package/src/plugins/guard 2.py +0 -746
  216. package/src/plugins/knowledge_graph_tools 2.py +0 -105
  217. package/src/plugins/preferences 2.py +0 -47
  218. package/src/plugins/update 2.py +0 -256
  219. package/src/requirements 2.txt +0 -12
  220. package/src/rules/__init__ 2.py +0 -0
  221. package/src/rules/core-rules 2.json +0 -331
  222. package/src/rules/migrate 2.py +0 -207
  223. package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
  224. package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
  225. package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
  226. package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
  227. package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
  228. package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
  229. package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
  230. package/src/scripts/__pycache__/nexo-install.cpython-314.pyc +0 -0
  231. package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-314.pyc +0 -0
  232. package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
  233. package/src/scripts/__pycache__/nexo-migrate.cpython-314.pyc +0 -0
  234. package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
  235. package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
  236. package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
  237. package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
  238. package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
  239. package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
  240. package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
  241. package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
  242. package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
  243. package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
  244. package/src/scripts/check-context 2.py +0 -264
  245. package/src/scripts/nexo-auto-update 2.py +0 -6
  246. package/src/scripts/nexo-backup 2.sh +0 -25
  247. package/src/scripts/nexo-brain-activation 2.sh +0 -140
  248. package/src/scripts/nexo-catchup 2.py +0 -242
  249. package/src/scripts/nexo-cognitive-decay 2.py +0 -182
  250. package/src/scripts/nexo-daily-self-audit 2.py +0 -552
  251. package/src/scripts/nexo-deep-sleep 2.sh +0 -97
  252. package/src/scripts/nexo-evolution-run 2.py +0 -597
  253. package/src/scripts/nexo-followup-hygiene 2.py +0 -112
  254. package/src/scripts/nexo-github-monitor 2.py +0 -256
  255. package/src/scripts/nexo-immune 2.py +0 -927
  256. package/src/scripts/nexo-inbox-hook 2.sh +0 -74
  257. package/src/scripts/nexo-install 2.py +0 -6
  258. package/src/scripts/nexo-learning-housekeep 2.py +0 -245
  259. package/src/scripts/nexo-learning-validator 2.py +0 -207
  260. package/src/scripts/nexo-migrate 2.py +0 -232
  261. package/src/scripts/nexo-postmortem-consolidator 2.py +0 -421
  262. package/src/scripts/nexo-pre-commit 2.py +0 -120
  263. package/src/scripts/nexo-prevent-sleep 2.sh +0 -29
  264. package/src/scripts/nexo-proactive-dashboard 2.py +0 -345
  265. package/src/scripts/nexo-reflection 2.py +0 -253
  266. package/src/scripts/nexo-runtime-preflight 2.py +0 -274
  267. package/src/scripts/nexo-send-email 2.py +0 -25
  268. package/src/scripts/nexo-send-email.py +0 -25
  269. package/src/scripts/nexo-send-reply 2.py +0 -178
  270. package/src/scripts/nexo-send-reply.py +0 -178
  271. package/src/scripts/nexo-sleep 2.py +0 -592
  272. package/src/scripts/nexo-snapshot-restore 2.sh +0 -35
  273. package/src/scripts/nexo-synthesis 2.py +0 -253
  274. package/src/scripts/nexo-tcc-approve 2.sh +0 -79
  275. package/src/scripts/nexo-update 2.sh +0 -161
  276. package/src/scripts/nexo-watchdog 2.sh +0 -878
  277. package/src/scripts/nexo-watchdog-smoke 2.py +0 -119
  278. package/src/server 2.py +0 -733
  279. package/src/storage_router 2.py +0 -32
  280. package/src/tools_coordination 2.py +0 -102
  281. package/src/tools_credentials 2.py +0 -68
  282. package/src/tools_learnings 2.py +0 -220
  283. package/src/tools_menu 2.py +0 -227
  284. package/src/tools_reminders 2.py +0 -86
  285. package/src/tools_reminders_crud 2.py +0 -159
  286. package/src/tools_sessions 2.py +0 -476
  287. package/src/tools_task_history 2.py +0 -57
  288. package/templates/CLAUDE.md 2.template +0 -63
  289. package/templates/openclaw 2.json +0 -13
  290. package/tests/__init__ 2.py +0 -0
  291. package/tests/__init__.py +0 -0
  292. package/tests/conftest 2.py +0 -71
  293. package/tests/conftest.py +0 -71
  294. package/tests/test_cognitive 2.py +0 -205
  295. package/tests/test_cognitive.py +0 -205
  296. package/tests/test_knowledge_graph 2.py +0 -140
  297. package/tests/test_knowledge_graph.py +0 -140
  298. package/tests/test_migrations 2.py +0 -137
  299. package/tests/test_migrations.py +0 -137
@@ -1,253 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- NEXO Reflection Engine — Processes session_buffer.jsonl entries.
4
-
5
- Triggered by the stop hook when >=3 sessions have accumulated and
6
- the last reflection was >4 hours ago.
7
-
8
- What it does:
9
- 1. Reads all entries from session_buffer.jsonl
10
- 2. Extracts patterns: recurring tasks, common errors, user mood trends
11
- 3. Updates user_model.json with observed patterns
12
- 4. Writes a reflection summary to reflection-log.json
13
- 5. Clears processed entries from the buffer
14
-
15
- Runs as a standalone Python script (no LLM needed).
16
- """
17
-
18
- import json
19
- import os
20
- import sys
21
- from datetime import datetime
22
- from collections import Counter
23
-
24
- NEXO_HOME = os.environ.get("NEXO_HOME", os.path.expanduser("~/.nexo"))
25
- BUFFER_PATH = os.path.join(NEXO_HOME, "brain", "session_buffer.jsonl")
26
- USER_MODEL_PATH = os.path.join(NEXO_HOME, "brain", "user_model.json")
27
- REFLECTION_LOG_PATH = os.path.join(NEXO_HOME, "coordination", "reflection-log.json")
28
-
29
-
30
- def load_buffer():
31
- """Load all entries from session buffer."""
32
- entries = []
33
- if not os.path.exists(BUFFER_PATH):
34
- return entries
35
- with open(BUFFER_PATH, "r") as f:
36
- for line in f:
37
- line = line.strip()
38
- if not line:
39
- continue
40
- try:
41
- entries.append(json.loads(line))
42
- except json.JSONDecodeError:
43
- continue
44
- return entries
45
-
46
-
47
- def load_user_model():
48
- """Load existing user model or create empty one."""
49
- if os.path.exists(USER_MODEL_PATH):
50
- try:
51
- return json.load(open(USER_MODEL_PATH))
52
- except (json.JSONDecodeError, IOError):
53
- pass
54
- return {
55
- "created": datetime.now().strftime("%Y-%m-%d"),
56
- "traits": {},
57
- "work_patterns": {},
58
- "mood_history": [],
59
- "common_tasks": [],
60
- "error_patterns": [],
61
- "reflections_count": 0,
62
- }
63
-
64
-
65
- def load_reflection_log():
66
- """Load existing reflection log."""
67
- if os.path.exists(REFLECTION_LOG_PATH):
68
- try:
69
- return json.load(open(REFLECTION_LOG_PATH))
70
- except (json.JSONDecodeError, IOError):
71
- pass
72
- return []
73
-
74
-
75
- def analyze_entries(entries):
76
- """Extract patterns from buffer entries."""
77
- tasks = []
78
- decisions = []
79
- errors = []
80
- moods = Counter()
81
- user_patterns = []
82
- files_modified = []
83
- self_critiques = []
84
-
85
- for entry in entries:
86
- source = entry.get("source", "")
87
- # Skip hook-fallback entries (they have no real data)
88
- if source == "hook-fallback":
89
- continue
90
-
91
- # Collect tasks
92
- for t in entry.get("tasks", []):
93
- if t and t != "session ended":
94
- tasks.append(t)
95
-
96
- # Collect decisions
97
- for d in entry.get("decisions", []):
98
- if d:
99
- decisions.append(d)
100
-
101
- # Collect errors
102
- for e in entry.get("errors_resolved", []):
103
- if e:
104
- errors.append(e)
105
-
106
- # Count moods
107
- mood = entry.get("mood", "unknown")
108
- if mood and mood != "unknown":
109
- moods[mood] += 1
110
-
111
- # User patterns
112
- for p in entry.get("user_patterns", []):
113
- if p:
114
- user_patterns.append(p)
115
-
116
- # Files
117
- for f in entry.get("files_modified", []):
118
- if f:
119
- files_modified.append(f)
120
-
121
- # Self-critiques
122
- critique = entry.get("self_critique", "")
123
- if critique and "hook-fallback" not in critique:
124
- self_critiques.append(critique)
125
-
126
- return {
127
- "tasks": tasks,
128
- "decisions": decisions,
129
- "errors": errors,
130
- "moods": dict(moods),
131
- "user_patterns": user_patterns,
132
- "files_modified": files_modified,
133
- "self_critiques": self_critiques,
134
- "entry_count": len(entries),
135
- "claude_entries": sum(1 for e in entries if e.get("source") == "claude"),
136
- }
137
-
138
-
139
- def update_user_model(model, analysis):
140
- """Update user model with new patterns."""
141
- # Update mood history (keep last 50)
142
- if analysis["moods"]:
143
- model["mood_history"].append({
144
- "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M"),
145
- "moods": analysis["moods"],
146
- })
147
- model["mood_history"] = model["mood_history"][-50:]
148
-
149
- # Update common tasks (top 20)
150
- task_counter = Counter(model.get("common_tasks", []))
151
- task_counter.update(analysis["tasks"])
152
- model["common_tasks"] = [t for t, _ in task_counter.most_common(20)]
153
-
154
- # Update error patterns (keep last 30)
155
- existing_errors = model.get("error_patterns", [])
156
- existing_errors.extend(analysis["errors"])
157
- model["error_patterns"] = existing_errors[-30:]
158
-
159
- # Update work patterns
160
- if analysis["files_modified"]:
161
- file_counter = Counter(model.get("work_patterns", {}).get("frequent_files", []))
162
- file_counter.update(analysis["files_modified"])
163
- if "work_patterns" not in model:
164
- model["work_patterns"] = {}
165
- model["work_patterns"]["frequent_files"] = [
166
- f for f, _ in file_counter.most_common(20)
167
- ]
168
-
169
- # Derive traits from mood patterns
170
- total_moods = sum(analysis["moods"].values())
171
- if total_moods >= 3:
172
- dominant_mood = max(analysis["moods"], key=analysis["moods"].get)
173
- model["traits"]["recent_dominant_mood"] = dominant_mood
174
- model["traits"]["mood_updated"] = datetime.now().strftime("%Y-%m-%d")
175
-
176
- model["reflections_count"] = model.get("reflections_count", 0) + 1
177
- model["last_reflection"] = datetime.now().strftime("%Y-%m-%d %H:%M")
178
-
179
- return model
180
-
181
-
182
- def main():
183
- entries = load_buffer()
184
- if not entries:
185
- print("No entries in buffer.")
186
- return
187
-
188
- # Filter out pure hook entries (tool captures)
189
- session_entries = [
190
- e for e in entries
191
- if e.get("source") in ("claude", "hook-fallback")
192
- or "tasks" in e
193
- ]
194
-
195
- if not session_entries:
196
- print("No session entries to process.")
197
- return
198
-
199
- analysis = analyze_entries(session_entries)
200
- model = load_user_model()
201
- model = update_user_model(model, analysis)
202
-
203
- # Save updated model
204
- os.makedirs(os.path.dirname(USER_MODEL_PATH), exist_ok=True)
205
- with open(USER_MODEL_PATH, "w") as f:
206
- json.dump(model, f, indent=2)
207
-
208
- # Append to reflection log
209
- log = load_reflection_log()
210
- reflection = {
211
- "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M"),
212
- "entries_processed": analysis["entry_count"],
213
- "claude_entries": analysis["claude_entries"],
214
- "tasks_seen": len(analysis["tasks"]),
215
- "decisions_made": len(analysis["decisions"]),
216
- "errors_resolved": len(analysis["errors"]),
217
- "mood_distribution": analysis["moods"],
218
- "self_critiques": analysis["self_critiques"][:5],
219
- "user_patterns_observed": analysis["user_patterns"][:5],
220
- }
221
- log.append(reflection)
222
- # Keep last 100 reflections
223
- log = log[-100:]
224
-
225
- os.makedirs(os.path.dirname(REFLECTION_LOG_PATH), exist_ok=True)
226
- with open(REFLECTION_LOG_PATH, "w") as f:
227
- json.dump(log, f, indent=2)
228
-
229
- # Clear buffer (processed)
230
- with open(BUFFER_PATH, "w") as f:
231
- f.write("")
232
-
233
- # Adaptive personality: decay inter-session tension
234
- try:
235
- import sys
236
- sys.path.insert(0, os.path.join(NEXO_HOME))
237
- from plugins.adaptive_mode import decay_tension
238
- decay_result = decay_tension()
239
- if decay_result:
240
- reflection["adaptive_decay"] = decay_result
241
- print(f"Adaptive decay: tension {decay_result['old_tension']} → {decay_result['new_tension']} "
242
- f"({decay_result['hours_elapsed']}h elapsed)")
243
- except Exception as e:
244
- print(f"Adaptive decay skipped: {e}")
245
-
246
- print(f"Reflection complete: {analysis['entry_count']} entries processed, "
247
- f"{analysis['claude_entries']} from Claude, "
248
- f"{len(analysis['errors'])} errors, "
249
- f"{len(analysis['self_critiques'])} critiques.")
250
-
251
-
252
- if __name__ == "__main__":
253
- main()
@@ -1,274 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- NEXO Runtime Preflight
4
-
5
- Runs safe end-to-end smoke tests for Cortex and Evolution using a temporary
6
- workspace and a copied SQLite database. No external API calls are performed.
7
- Results are written to NEXO_HOME/logs/runtime-preflight-summary.json.
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- import importlib.util
13
- import json
14
- import os
15
- import shutil
16
- import sqlite3
17
- import sys
18
- import tempfile
19
- import traceback
20
- from datetime import datetime
21
- from pathlib import Path
22
-
23
- HOME = Path.home()
24
- NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(HOME / ".nexo")))
25
- # Auto-detect: if running from repo (src/scripts/), use src/ as NEXO_CODE
26
- _script_dir = Path(__file__).resolve().parent
27
- _repo_src = _script_dir.parent # src/scripts/ -> src/
28
- NEXO_CODE = Path(os.environ.get("NEXO_CODE", str(_repo_src) if (_repo_src / "server.py").exists() else str(NEXO_HOME)))
29
-
30
- LOG_DIR = NEXO_HOME / "logs"
31
- LOG_DIR.mkdir(parents=True, exist_ok=True)
32
- SUMMARY_FILE = LOG_DIR / "runtime-preflight-summary.json"
33
- DB_FILE = NEXO_HOME / "data" / "nexo.db"
34
- # Evolution config: NEXO_HOME/brain/ (canonical), NEXO_HOME/cortex/ (legacy fallback), NEXO_CODE (dev fallback)
35
- def _find_evolution_file(name: str) -> Path:
36
- for candidate in [NEXO_HOME / "brain" / name, NEXO_HOME / "cortex" / name, NEXO_CODE / name]:
37
- if candidate.exists():
38
- return candidate
39
- return NEXO_HOME / "brain" / name # default canonical path
40
-
41
- CORTEX_OBJECTIVE = _find_evolution_file("evolution-objective.json")
42
- CORTEX_PROMPT = _find_evolution_file("evolution-prompt.md")
43
-
44
-
45
- def _load_module(name: str, path: Path):
46
- spec = importlib.util.spec_from_file_location(name, str(path))
47
- module = importlib.util.module_from_spec(spec)
48
- sys.modules[name] = module
49
- assert spec.loader is not None
50
- spec.loader.exec_module(module)
51
- return module
52
-
53
-
54
- def _write_summary(summary: dict):
55
- SUMMARY_FILE.write_text(json.dumps(summary, indent=2, ensure_ascii=False))
56
-
57
-
58
- def _fake_cortex_response(model: str) -> str:
59
- if "opus" in model:
60
- payload = {
61
- "analysis": "Smoke evolution run over temp workspace",
62
- "proposals": [
63
- {
64
- "dimension": "self_improvement",
65
- "classification": "propose",
66
- "action": "Add a regression smoke before live cycles",
67
- "reasoning": "Smoke run should stay side-effect free",
68
- }
69
- ],
70
- "dimension_scores": {
71
- "episodic_memory": 86,
72
- "autonomy": 53,
73
- "proactivity": 34,
74
- "self_improvement": 30,
75
- "agi": 6,
76
- },
77
- "score_evidence": {
78
- "self_improvement": "Temp evolution smoke completed successfully",
79
- },
80
- }
81
- else:
82
- payload = {
83
- "actions_taken": ["preflight perception completed"],
84
- "signals_detected": 1,
85
- "pending_questions": [],
86
- "execute": [],
87
- "next_interval_suggestion": 600,
88
- "reflection_done": False,
89
- "dmn_done": False,
90
- "briefing_update": {
91
- "actions_taken": ["perception smoke ok"],
92
- "signals_active": [{"summary": "Smoke signal", "urgency": "INFO", "score": 10}],
93
- "recommendations": ["keep runtime checks green"],
94
- "pending_questions_unanswered": [],
95
- "dmn_summary": "",
96
- },
97
- "working_memory": {
98
- "current_threads": ["runtime preflight"],
99
- "attention_focus": "runtime integrity",
100
- "last_reasoning": "API mocked successfully",
101
- "watching": ["state writes", "briefing writes"],
102
- "momentum": "stable",
103
- },
104
- "error": None,
105
- }
106
- return json.dumps(payload, ensure_ascii=False)
107
-
108
-
109
- def _fake_runner_response() -> tuple[str, dict]:
110
- payload = {
111
- "analysis": "Standalone evolution smoke over temp DB",
112
- "patterns": [
113
- {"type": "smoke", "description": "runner executed over temp workspace", "frequency": "once"}
114
- ],
115
- "proposals": [
116
- {
117
- "dimension": "self_improvement",
118
- "classification": "propose",
119
- "action": "Keep standalone evolution covered by preflight",
120
- "reasoning": "Regression prevention",
121
- }
122
- ],
123
- "dimension_scores": {
124
- "episodic_memory": 87,
125
- "autonomy": 54,
126
- "proactivity": 35,
127
- "self_improvement": 31,
128
- "agi": 6,
129
- },
130
- "score_evidence": {
131
- "self_improvement": "standalone runner smoke ok",
132
- },
133
- }
134
- usage = {"input_tokens": 1234, "output_tokens": 432}
135
- return json.dumps(payload, ensure_ascii=False), usage
136
-
137
-
138
- def main() -> int:
139
- started = datetime.now().isoformat()
140
- summary = {
141
- "timestamp": started,
142
- "ok": False,
143
- "checks": {},
144
- "errors": [],
145
- }
146
-
147
- if not DB_FILE.exists():
148
- summary["errors"].append("nexo.db missing")
149
- _write_summary(summary)
150
- return 1
151
-
152
- preflight_root = HOME / ".codex" / "memories"
153
- preflight_root.mkdir(parents=True, exist_ok=True)
154
- temp_root = Path(tempfile.mkdtemp(prefix="nexo-runtime-preflight-", dir=str(preflight_root)))
155
- try:
156
- temp_db = temp_root / "nexo.db"
157
- shutil.copy2(DB_FILE, temp_db)
158
-
159
- temp_cortex_dir = temp_root / "brain"
160
- temp_logs_dir = temp_root / "logs"
161
- temp_coord_dir = temp_root / "coordination"
162
- temp_daily_dir = temp_root / "daily_summaries"
163
- temp_dmn_dir = temp_root / "dmn_insights"
164
- temp_snapshots_dir = temp_root / "snapshots"
165
- temp_sandbox_dir = temp_root / "sandbox" / "workspace"
166
- temp_scripts_dir = temp_root / "scripts"
167
- for directory in [
168
- temp_cortex_dir, temp_logs_dir, temp_coord_dir, temp_daily_dir,
169
- temp_dmn_dir, temp_snapshots_dir, temp_sandbox_dir, temp_scripts_dir,
170
- ]:
171
- directory.mkdir(parents=True, exist_ok=True)
172
-
173
- temp_objective = temp_cortex_dir / "evolution-objective.json"
174
- temp_prompt = temp_cortex_dir / "evolution-prompt.md"
175
- if CORTEX_OBJECTIVE.exists():
176
- shutil.copy2(CORTEX_OBJECTIVE, temp_objective)
177
- else:
178
- # Create a minimal objective so evolution smoke tests can proceed
179
- temp_objective.write_text(json.dumps({"evolution_enabled": True, "dimensions": {}}, indent=2))
180
- if CORTEX_PROMPT.exists():
181
- shutil.copy2(CORTEX_PROMPT, temp_prompt)
182
- snapshot_restore = NEXO_CODE / "scripts" / "nexo-snapshot-restore.sh"
183
- if snapshot_restore.exists():
184
- shutil.copy2(snapshot_restore, temp_scripts_dir / "nexo-snapshot-restore.sh")
185
-
186
- temp_api_key = temp_root / "anthropic-api-key.txt"
187
- temp_api_key.write_text("smoke-test-key")
188
-
189
- evolution_cycle = _load_module("evolution_cycle", NEXO_CODE / "evolution_cycle.py")
190
- evolution_cycle.NEXO_DB = temp_db
191
- evolution_cycle.NEXO_HOME = temp_root
192
- evolution_cycle.SANDBOX_DIR = temp_sandbox_dir
193
- evolution_cycle.SNAPSHOTS_DIR = temp_snapshots_dir
194
- evolution_cycle.OBJECTIVE_FILE = temp_objective
195
- evolution_cycle.PROMPT_FILE = temp_prompt
196
- evolution_cycle.RESTORE_LOG = temp_logs_dir / "snapshot-restores.log"
197
-
198
- week_data = evolution_cycle.get_week_data(str(temp_db))
199
- prompt = evolution_cycle.build_evolution_prompt(week_data, evolution_cycle.load_objective())
200
- restore_ok = evolution_cycle.dry_run_restore_test()
201
- if not restore_ok:
202
- raise RuntimeError("dry_run_restore_test failed")
203
- summary["checks"]["evolution_cycle"] = {
204
- "learnings": len(week_data.get("learnings", [])),
205
- "decisions": len(week_data.get("decisions", [])),
206
- "prompt_chars": len(prompt),
207
- "restore_ok": restore_ok,
208
- }
209
-
210
- cortex = _load_module("cortex_plugin", NEXO_CODE / "plugins" / "cortex.py")
211
- cortex.NEXO_DB = temp_db
212
- cortex.DRY_RUN = True
213
- cortex.BRIEFING_FILE = temp_cortex_dir / "briefing.json"
214
- cortex.HEALTH_FILE = temp_cortex_dir / "health.json"
215
- cortex.STATE_FILE = temp_cortex_dir / "state.json"
216
- cortex.STATE_TMP = temp_cortex_dir / "state.tmp"
217
- cortex.LOG_DIR = temp_logs_dir
218
- cortex.COORD_DIR = temp_coord_dir
219
- cortex.SIGNALS_FILE = temp_coord_dir / "pending-signals.json"
220
- cortex.DAILY_SUMMARIES_DIR = temp_daily_dir
221
- cortex.DMN_INSIGHTS_DIR = temp_dmn_dir
222
- cortex.FAILURE_COUNT_FILE = temp_cortex_dir / ".failure-count"
223
- cortex.PID_FILE = temp_cortex_dir / "cortex.pid"
224
- cortex.API_KEY_FILE = temp_api_key
225
- cortex.wa_notify = lambda *args, **kwargs: None
226
- cortex.poll_wa_inbox = lambda state: None
227
- cortex._check_health_endpoints = lambda: []
228
- cortex._call_anthropic = lambda prompt, model=None, max_tokens=4096: _fake_cortex_response(model or "")
229
-
230
- # Smoke test: verify cortex plugin loads and has expected tools
231
- cortex_path = NEXO_CODE / "plugins" / "cortex.py"
232
- if cortex_path.exists():
233
- import importlib.util
234
- spec = importlib.util.spec_from_file_location("cortex_plugin", str(cortex_path))
235
- cortex_mod = importlib.util.module_from_spec(spec)
236
- spec.loader.exec_module(cortex_mod)
237
- assert hasattr(cortex_mod, 'TOOLS'), "cortex plugin missing TOOLS"
238
- tool_names = [t[1] for t in cortex_mod.TOOLS]
239
- assert "nexo_cortex_check" in tool_names, "cortex plugin missing nexo_cortex_check tool"
240
- else:
241
- tool_names = ["cortex.py not found"]
242
- summary["checks"]["cortex_plugin"] = {
243
- "status": "pass",
244
- "tools_found": tool_names,
245
- }
246
-
247
- # Evolution runner: component verification (not full execution — that needs CLI)
248
- runner_path = NEXO_CODE / "scripts" / "nexo-evolution-run.py"
249
- if runner_path.exists():
250
- runner = _load_module("nexo_evolution_run", runner_path)
251
- checks = {
252
- "module_loads": True,
253
- "has_run": hasattr(runner, 'run'),
254
- "has_load_objective": hasattr(runner, 'load_objective'),
255
- "has_get_week_data": hasattr(runner, 'get_week_data'),
256
- }
257
- summary["checks"]["standalone_runner"] = checks
258
- else:
259
- summary["checks"]["standalone_runner"] = {"status": "skip", "reason": "runner not found"}
260
-
261
- summary["ok"] = True
262
- _write_summary(summary)
263
- return 0
264
- except Exception as exc:
265
- summary["errors"].append(f"{type(exc).__name__}: {exc}")
266
- summary["errors"].append(traceback.format_exc())
267
- _write_summary(summary)
268
- return 1
269
- finally:
270
- shutil.rmtree(temp_root, ignore_errors=True)
271
-
272
-
273
- if __name__ == "__main__":
274
- sys.exit(main())
@@ -1,25 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Quick email sender for NEXO progress updates."""
3
- import smtplib, sys
4
- from email.mime.text import MIMEText
5
- from email.utils import formataddr
6
-
7
- def send(subject, body, to="user@example.com", cc="user@example.com"):
8
- msg = MIMEText(body, 'plain', 'utf-8')
9
- msg['From'] = formataddr(('NEXO', 'nexo@example.com'))
10
- msg['To'] = to
11
- msg['Cc'] = cc
12
- msg['Subject'] = subject
13
- smtp = smtplib.SMTP_SSL(os.environ.get('NEXO_SMTP_HOST', 'smtp.example.com'), int(os.environ.get('NEXO_SMTP_PORT', '465')))
14
- smtp.login(FROM_EMAIL, os.environ.get('NEXO_SMTP_PASSWORD', ''))
15
- recipients = [to]
16
- if cc:
17
- recipients.append(cc)
18
- smtp.send_message(msg)
19
- smtp.quit()
20
- print(f"OK — sent to {to}")
21
-
22
- if __name__ == "__main__":
23
- subject = sys.argv[1] if len(sys.argv) > 1 else "NEXO Update"
24
- body = sys.argv[2] if len(sys.argv) > 2 else "No body"
25
- send(subject, body)
@@ -1,25 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Quick email sender for NEXO progress updates."""
3
- import smtplib, sys
4
- from email.mime.text import MIMEText
5
- from email.utils import formataddr
6
-
7
- def send(subject, body, to="user@example.com", cc="user@example.com"):
8
- msg = MIMEText(body, 'plain', 'utf-8')
9
- msg['From'] = formataddr(('NEXO', 'nexo@example.com'))
10
- msg['To'] = to
11
- msg['Cc'] = cc
12
- msg['Subject'] = subject
13
- smtp = smtplib.SMTP_SSL(os.environ.get('NEXO_SMTP_HOST', 'smtp.example.com'), int(os.environ.get('NEXO_SMTP_PORT', '465')))
14
- smtp.login(FROM_EMAIL, os.environ.get('NEXO_SMTP_PASSWORD', ''))
15
- recipients = [to]
16
- if cc:
17
- recipients.append(cc)
18
- smtp.send_message(msg)
19
- smtp.quit()
20
- print(f"OK — sent to {to}")
21
-
22
- if __name__ == "__main__":
23
- subject = sys.argv[1] if len(sys.argv) > 1 else "NEXO Update"
24
- body = sys.argv[2] if len(sys.argv) > 2 else "No body"
25
- send(subject, body)