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,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)