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,778 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- NEXO Migration Script: v1.5.x -> v1.6.0
4
-
5
- Upgrades both nexo.db and cognitive.db to the v1.6.0 schema.
6
- Safe to run multiple times (fully idempotent).
7
-
8
- Usage:
9
- python3 migrate-v1.5-to-v1.6.py [--dry-run] [--nexo-home /path/to/nexo]
10
-
11
- What this migration adds:
12
-
13
- nexo.db:
14
- - Table: error_repetitions (guard system)
15
- - Table: guard_checks (guard system)
16
- - Table: session_diary_draft (auto-diary)
17
- - Table: adaptive_log (adaptive personality mode)
18
- - Table: somatic_events (somatic event log)
19
- - Table: maintenance_schedule (maintenance cron tasks)
20
- - Table: diary_archive (permanent diary archive)
21
- - Table: artifact_registry (structured artifact index)
22
- - Table: artifact_aliases (artifact alias search)
23
- - Table: session_checkpoints (compaction continuity)
24
- - Table: schema_migrations (migration tracking)
25
- - Column: learnings.reasoning, prevention, applies_to, status, review_due_at, last_reviewed_at
26
- - Column: followups.reasoning
27
- - Column: task_history.reasoning
28
- - Column: decisions.status, review_due_at, last_reviewed_at
29
- - Column: session_diary.mental_state, domain, user_signals, self_critique, source
30
- - Column: sessions.claude_session_id
31
- - Indexes: 20+ new indexes across tables
32
-
33
- cognitive.db:
34
- - Table: kg_nodes (knowledge graph nodes)
35
- - Table: kg_edges (knowledge graph bi-temporal edges)
36
- - Table: somatic_markers (persistent somatic markers)
37
- - Table: claims (atomic claim graph)
38
- - Table: claim_links (claim relationships)
39
- - Table: trust_event_config (customizable trust deltas)
40
- - Indexes: 12+ new indexes
41
- """
42
-
43
- import argparse
44
- import os
45
- import shutil
46
- import sqlite3
47
- import sys
48
- from datetime import datetime
49
- from pathlib import Path
50
-
51
-
52
- # ── Constants ────────────────────────────────────────────────────────
53
-
54
- VERSION_FROM = "1.5.x"
55
- VERSION_TO = "1.6.0"
56
-
57
-
58
- # ── Helpers ──────────────────────────────────────────────────────────
59
-
60
- def find_nexo_home(override: str = None) -> Path:
61
- """Locate NEXO home directory. Checks in order:
62
- 1. Explicit override path
63
- 2. NEXO_HOME env var
64
- 3. ~/.nexo/
65
- 4. ./ (current directory)
66
- """
67
- candidates = []
68
- if override:
69
- candidates.append(Path(override))
70
- if os.environ.get("NEXO_HOME"):
71
- candidates.append(Path(os.environ["NEXO_HOME"]))
72
- candidates.append(Path.home() / ".nexo")
73
- candidates.append(Path.cwd())
74
-
75
- for c in candidates:
76
- nexo_db = c / "nexo.db"
77
- if nexo_db.exists():
78
- return c
79
-
80
- # If no nexo.db found anywhere, return the first candidate that exists as dir
81
- for c in candidates:
82
- if c.is_dir():
83
- return c
84
-
85
- return candidates[0]
86
-
87
-
88
- def table_exists(conn: sqlite3.Connection, table: str) -> bool:
89
- row = conn.execute(
90
- "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?",
91
- (table,)
92
- ).fetchone()
93
- return row[0] > 0
94
-
95
-
96
- def column_exists(conn: sqlite3.Connection, table: str, column: str) -> bool:
97
- if not table_exists(conn, table):
98
- return False
99
- cols = conn.execute(f"PRAGMA table_info({table})").fetchall()
100
- return any(c[1] == column for c in cols)
101
-
102
-
103
- def index_exists(conn: sqlite3.Connection, index_name: str) -> bool:
104
- row = conn.execute(
105
- "SELECT COUNT(*) FROM sqlite_master WHERE type='index' AND name=?",
106
- (index_name,)
107
- ).fetchone()
108
- return row[0] > 0
109
-
110
-
111
- def add_column(conn: sqlite3.Connection, table: str, column: str, col_type: str, dry_run: bool = False) -> bool:
112
- """Add column if missing. Returns True if added."""
113
- if column_exists(conn, table, column):
114
- return False
115
- sql = f"ALTER TABLE {table} ADD COLUMN {column} {col_type}"
116
- if dry_run:
117
- print(f" [DRY RUN] Would execute: {sql}")
118
- else:
119
- conn.execute(sql)
120
- return True
121
-
122
-
123
- def add_index(conn: sqlite3.Connection, name: str, table: str, columns: str, dry_run: bool = False) -> bool:
124
- """Add index if missing. Returns True if added."""
125
- if index_exists(conn, name):
126
- return False
127
- sql = f"CREATE INDEX IF NOT EXISTS {name} ON {table}({columns})"
128
- if dry_run:
129
- print(f" [DRY RUN] Would execute: {sql}")
130
- else:
131
- conn.execute(sql)
132
- return True
133
-
134
-
135
- def create_table(conn: sqlite3.Connection, table: str, ddl: str, dry_run: bool = False) -> bool:
136
- """Create table if missing. Returns True if created."""
137
- if table_exists(conn, table):
138
- return False
139
- if dry_run:
140
- print(f" [DRY RUN] Would create table: {table}")
141
- else:
142
- conn.execute(ddl)
143
- return True
144
-
145
-
146
- def backup_db(db_path: Path) -> Path:
147
- """Create a timestamped backup. Returns backup path."""
148
- ts = datetime.now().strftime("%Y%m%d-%H%M%S")
149
- backup = db_path.parent / f"{db_path.stem}.backup-{ts}{db_path.suffix}"
150
- shutil.copy2(db_path, backup)
151
- return backup
152
-
153
-
154
- # ── nexo.db migrations ──────────────────────────────────────────────
155
-
156
- def migrate_nexo_db(conn: sqlite3.Connection, dry_run: bool = False) -> dict:
157
- """Apply all v1.5 -> v1.6 schema changes to nexo.db."""
158
- stats = {"tables_created": 0, "columns_added": 0, "indexes_created": 0, "data_seeded": 0}
159
-
160
- # ── schema_migrations tracking table ────────────────────────────
161
- if create_table(conn, "schema_migrations", """
162
- CREATE TABLE IF NOT EXISTS schema_migrations (
163
- version INTEGER PRIMARY KEY,
164
- name TEXT NOT NULL,
165
- applied_at TEXT DEFAULT (datetime('now'))
166
- )
167
- """, dry_run):
168
- stats["tables_created"] += 1
169
- print(" + Created table: schema_migrations")
170
-
171
- # ── Migration 1: learnings columns ──────────────────────────────
172
- print("\n [M1] learnings columns...")
173
- for col, ctype in [
174
- ("reasoning", "TEXT"),
175
- ("prevention", "TEXT DEFAULT ''"),
176
- ("applies_to", "TEXT DEFAULT ''"),
177
- ("status", "TEXT DEFAULT 'active'"),
178
- ("review_due_at", "REAL"),
179
- ("last_reviewed_at", "REAL"),
180
- ]:
181
- if add_column(conn, "learnings", col, ctype, dry_run):
182
- stats["columns_added"] += 1
183
- print(f" + Column: learnings.{col}")
184
-
185
- # ── Migration 2: followups/task_history reasoning ───────────────
186
- print(" [M2] followups & task_history reasoning...")
187
- if add_column(conn, "followups", "reasoning", "TEXT", dry_run):
188
- stats["columns_added"] += 1
189
- print(" + Column: followups.reasoning")
190
- if add_column(conn, "task_history", "reasoning", "TEXT", dry_run):
191
- stats["columns_added"] += 1
192
- print(" + Column: task_history.reasoning")
193
-
194
- # ── Migration 3: decisions review columns + indexes ─────────────
195
- print(" [M3] decisions review columns...")
196
- for col, ctype in [
197
- ("status", "TEXT DEFAULT 'pending_review'"),
198
- ("review_due_at", "TEXT"),
199
- ("last_reviewed_at", "TEXT"),
200
- ]:
201
- if add_column(conn, "decisions", col, ctype, dry_run):
202
- stats["columns_added"] += 1
203
- print(f" + Column: decisions.{col}")
204
- for idx, tbl, col in [
205
- ("idx_decisions_domain", "decisions", "domain"),
206
- ("idx_decisions_created", "decisions", "created_at"),
207
- ("idx_decisions_review_due", "decisions", "review_due_at"),
208
- ]:
209
- if add_index(conn, idx, tbl, col, dry_run):
210
- stats["indexes_created"] += 1
211
- print(f" + Index: {idx}")
212
-
213
- # ── Migration 4: session_diary columns ──────────────────────────
214
- print(" [M4] session_diary columns...")
215
- if add_index(conn, "idx_session_diary_sid", "session_diary", "session_id", dry_run):
216
- stats["indexes_created"] += 1
217
- print(" + Index: idx_session_diary_sid")
218
- for col in ["mental_state", "domain", "user_signals", "self_critique"]:
219
- if add_column(conn, "session_diary", col, "TEXT", dry_run):
220
- stats["columns_added"] += 1
221
- print(f" + Column: session_diary.{col}")
222
-
223
- # ── Migration 5: change_log & learnings indexes ─────────────────
224
- print(" [M5] change_log & learnings indexes...")
225
- for idx, tbl, col in [
226
- ("idx_change_log_created", "change_log", "created_at"),
227
- ("idx_change_log_files", "change_log", "files"),
228
- ("idx_learnings_status", "learnings", "status"),
229
- ("idx_learnings_review_due", "learnings", "review_due_at"),
230
- ]:
231
- if add_index(conn, idx, tbl, col, dry_run):
232
- stats["indexes_created"] += 1
233
- print(f" + Index: {idx}")
234
-
235
- # ── Migration 6: error guard tables ─────────────────────────────
236
- print(" [M6] error guard tables...")
237
- if create_table(conn, "error_repetitions", """
238
- CREATE TABLE IF NOT EXISTS error_repetitions (
239
- id INTEGER PRIMARY KEY AUTOINCREMENT,
240
- new_learning_id INTEGER NOT NULL,
241
- original_learning_id INTEGER NOT NULL,
242
- similarity REAL NOT NULL,
243
- area TEXT NOT NULL,
244
- created_at TEXT DEFAULT (datetime('now'))
245
- )
246
- """, dry_run):
247
- stats["tables_created"] += 1
248
- print(" + Table: error_repetitions")
249
- if create_table(conn, "guard_checks", """
250
- CREATE TABLE IF NOT EXISTS guard_checks (
251
- id INTEGER PRIMARY KEY AUTOINCREMENT,
252
- session_id TEXT,
253
- files TEXT,
254
- area TEXT,
255
- learnings_returned INTEGER DEFAULT 0,
256
- blocking_rules_returned INTEGER DEFAULT 0,
257
- created_at TEXT DEFAULT (datetime('now'))
258
- )
259
- """, dry_run):
260
- stats["tables_created"] += 1
261
- print(" + Table: guard_checks")
262
- for idx, tbl, col in [
263
- ("idx_error_repetitions_area", "error_repetitions", "area"),
264
- ("idx_guard_checks_session", "guard_checks", "session_id"),
265
- ]:
266
- if add_index(conn, idx, tbl, col, dry_run):
267
- stats["indexes_created"] += 1
268
- print(f" + Index: {idx}")
269
-
270
- # ── Migration 7: diary source + draft table ─────────────────────
271
- print(" [M7] diary source & draft...")
272
- if add_column(conn, "session_diary", "source", "TEXT DEFAULT 'claude'", dry_run):
273
- stats["columns_added"] += 1
274
- print(" + Column: session_diary.source")
275
- if create_table(conn, "session_diary_draft", """
276
- CREATE TABLE IF NOT EXISTS session_diary_draft (
277
- sid TEXT PRIMARY KEY,
278
- summary_draft TEXT DEFAULT '',
279
- tasks_seen TEXT DEFAULT '[]',
280
- change_ids TEXT DEFAULT '[]',
281
- decision_ids TEXT DEFAULT '[]',
282
- last_context_hint TEXT DEFAULT '',
283
- heartbeat_count INTEGER DEFAULT 0,
284
- created_at TEXT DEFAULT (datetime('now')),
285
- updated_at TEXT DEFAULT (datetime('now'))
286
- )
287
- """, dry_run):
288
- stats["tables_created"] += 1
289
- print(" + Table: session_diary_draft")
290
-
291
- # ── Migration 8: adaptive_log + somatic_events ──────────────────
292
- print(" [M8] adaptive_log & somatic_events...")
293
- if create_table(conn, "adaptive_log", """
294
- CREATE TABLE IF NOT EXISTS adaptive_log (
295
- id INTEGER PRIMARY KEY AUTOINCREMENT,
296
- timestamp TEXT DEFAULT (datetime('now')),
297
- mode TEXT NOT NULL,
298
- tension_score REAL NOT NULL,
299
- sig_vibe REAL DEFAULT 0,
300
- sig_corrections REAL DEFAULT 0,
301
- sig_brevity REAL DEFAULT 0,
302
- sig_topic REAL DEFAULT 0,
303
- sig_tool_errors REAL DEFAULT 0,
304
- sig_git_diff REAL DEFAULT 0,
305
- context_hint TEXT DEFAULT '',
306
- feedback_event TEXT DEFAULT NULL,
307
- feedback_delta INTEGER DEFAULT NULL,
308
- feedback_ts TEXT DEFAULT NULL
309
- )
310
- """, dry_run):
311
- stats["tables_created"] += 1
312
- print(" + Table: adaptive_log")
313
- if add_index(conn, "idx_adaptive_log_ts", "adaptive_log", "timestamp", dry_run):
314
- stats["indexes_created"] += 1
315
- print(" + Index: idx_adaptive_log_ts")
316
-
317
- if create_table(conn, "somatic_events", """
318
- CREATE TABLE IF NOT EXISTS somatic_events (
319
- id INTEGER PRIMARY KEY AUTOINCREMENT,
320
- timestamp TEXT DEFAULT (datetime('now')),
321
- target TEXT NOT NULL,
322
- target_type TEXT NOT NULL,
323
- event_type TEXT NOT NULL,
324
- delta REAL NOT NULL,
325
- source TEXT DEFAULT '',
326
- projected INTEGER DEFAULT 0
327
- )
328
- """, dry_run):
329
- stats["tables_created"] += 1
330
- print(" + Table: somatic_events")
331
- for idx, tbl, col in [
332
- ("idx_somatic_events_target", "somatic_events", "target"),
333
- ("idx_somatic_events_projected", "somatic_events", "projected"),
334
- ]:
335
- if add_index(conn, idx, tbl, col, dry_run):
336
- stats["indexes_created"] += 1
337
- print(f" + Index: {idx}")
338
-
339
- # ── Migration 9: maintenance_schedule ───────────────────────────
340
- print(" [M9] maintenance_schedule...")
341
- if create_table(conn, "maintenance_schedule", """
342
- CREATE TABLE IF NOT EXISTS maintenance_schedule (
343
- task_name TEXT PRIMARY KEY,
344
- interval_hours REAL NOT NULL,
345
- last_run_at TEXT DEFAULT NULL,
346
- last_duration_ms INTEGER DEFAULT 0,
347
- run_count INTEGER DEFAULT 0
348
- )
349
- """, dry_run):
350
- stats["tables_created"] += 1
351
- print(" + Table: maintenance_schedule")
352
- # Seed default tasks
353
- tasks = [
354
- ('cognitive_decay', 20), ('synthesis', 20), ('self_audit', 144),
355
- ('weight_learning', 20), ('somatic_projection', 20), ('somatic_decay', 20),
356
- ('graph_maintenance', 48),
357
- ]
358
- if not dry_run:
359
- for name, hours in tasks:
360
- conn.execute(
361
- "INSERT OR IGNORE INTO maintenance_schedule (task_name, interval_hours) VALUES (?, ?)",
362
- (name, hours)
363
- )
364
- stats["data_seeded"] += len(tasks)
365
- print(f" + Seeded {len(tasks)} maintenance tasks")
366
- else:
367
- print(f" [DRY RUN] Would seed {len(tasks)} maintenance tasks")
368
-
369
- # ── Migration 10: diary_archive ─────────────────────────────────
370
- print(" [M10] diary_archive...")
371
- if create_table(conn, "diary_archive", """
372
- CREATE TABLE IF NOT EXISTS diary_archive (
373
- id INTEGER PRIMARY KEY,
374
- session_id TEXT NOT NULL,
375
- created_at TEXT NOT NULL,
376
- decisions TEXT NOT NULL,
377
- discarded TEXT,
378
- pending TEXT,
379
- context_next TEXT,
380
- summary TEXT NOT NULL,
381
- mental_state TEXT,
382
- domain TEXT,
383
- user_signals TEXT,
384
- self_critique TEXT DEFAULT '',
385
- source TEXT DEFAULT 'claude',
386
- archived_at TEXT DEFAULT (datetime('now'))
387
- )
388
- """, dry_run):
389
- stats["tables_created"] += 1
390
- print(" + Table: diary_archive")
391
- for idx, tbl, col in [
392
- ("idx_diary_archive_created", "diary_archive", "created_at"),
393
- ("idx_diary_archive_domain", "diary_archive", "domain"),
394
- ]:
395
- if add_index(conn, idx, tbl, col, dry_run):
396
- stats["indexes_created"] += 1
397
- print(f" + Index: {idx}")
398
-
399
- # ── Migration 11: artifact_registry ─────────────────────────────
400
- print(" [M11] artifact_registry...")
401
- if create_table(conn, "artifact_registry", """
402
- CREATE TABLE IF NOT EXISTS artifact_registry (
403
- id INTEGER PRIMARY KEY AUTOINCREMENT,
404
- kind TEXT NOT NULL,
405
- canonical_name TEXT NOT NULL,
406
- aliases TEXT DEFAULT '[]',
407
- description TEXT DEFAULT '',
408
- uri TEXT DEFAULT '',
409
- ports TEXT DEFAULT '[]',
410
- paths TEXT DEFAULT '[]',
411
- run_cmd TEXT DEFAULT '',
412
- repo TEXT DEFAULT '',
413
- domain TEXT DEFAULT '',
414
- state TEXT DEFAULT 'active',
415
- session_id TEXT DEFAULT '',
416
- created_at TEXT DEFAULT (datetime('now')),
417
- last_touched_at TEXT DEFAULT (datetime('now')),
418
- last_verified_at TEXT DEFAULT NULL,
419
- metadata TEXT DEFAULT '{}'
420
- )
421
- """, dry_run):
422
- stats["tables_created"] += 1
423
- print(" + Table: artifact_registry")
424
- if create_table(conn, "artifact_aliases", """
425
- CREATE TABLE IF NOT EXISTS artifact_aliases (
426
- id INTEGER PRIMARY KEY AUTOINCREMENT,
427
- artifact_id INTEGER NOT NULL REFERENCES artifact_registry(id) ON DELETE CASCADE,
428
- phrase TEXT NOT NULL,
429
- source TEXT DEFAULT 'manual',
430
- confidence REAL DEFAULT 1.0,
431
- created_at TEXT DEFAULT (datetime('now')),
432
- UNIQUE(artifact_id, phrase)
433
- )
434
- """, dry_run):
435
- stats["tables_created"] += 1
436
- print(" + Table: artifact_aliases")
437
- for idx, tbl, col in [
438
- ("idx_artifact_state", "artifact_registry", "state"),
439
- ("idx_artifact_kind", "artifact_registry", "kind"),
440
- ("idx_artifact_domain", "artifact_registry", "domain"),
441
- ("idx_artifact_last_touched", "artifact_registry", "last_touched_at"),
442
- ("idx_artifact_aliases_phrase", "artifact_aliases", "phrase"),
443
- ("idx_artifact_aliases_aid", "artifact_aliases", "artifact_id"),
444
- ]:
445
- if add_index(conn, idx, tbl, col, dry_run):
446
- stats["indexes_created"] += 1
447
- print(f" + Index: {idx}")
448
-
449
- # ── Migration 12: session_checkpoints ───────────────────────────
450
- print(" [M12] session_checkpoints...")
451
- if create_table(conn, "session_checkpoints", """
452
- CREATE TABLE IF NOT EXISTS session_checkpoints (
453
- sid TEXT PRIMARY KEY,
454
- task TEXT DEFAULT '',
455
- task_status TEXT DEFAULT 'active',
456
- active_files TEXT DEFAULT '[]',
457
- current_goal TEXT DEFAULT '',
458
- decisions_summary TEXT DEFAULT '',
459
- errors_found TEXT DEFAULT '',
460
- reasoning_thread TEXT DEFAULT '',
461
- next_step TEXT DEFAULT '',
462
- compaction_count INTEGER DEFAULT 0,
463
- created_at TEXT DEFAULT (datetime('now')),
464
- updated_at TEXT DEFAULT (datetime('now'))
465
- )
466
- """, dry_run):
467
- stats["tables_created"] += 1
468
- print(" + Table: session_checkpoints")
469
-
470
- # ── Migration 13: claude_session_id (NEW in v1.6) ───────────────
471
- print(" [M13] sessions.claude_session_id...")
472
- if add_column(conn, "sessions", "claude_session_id", "TEXT DEFAULT ''", dry_run):
473
- stats["columns_added"] += 1
474
- print(" + Column: sessions.claude_session_id")
475
- if add_index(conn, "idx_sessions_claude_sid", "sessions", "claude_session_id", dry_run):
476
- stats["indexes_created"] += 1
477
- print(" + Index: idx_sessions_claude_sid")
478
-
479
- # ── Record migrations in schema_migrations ──────────────────────
480
- if not dry_run and table_exists(conn, "schema_migrations"):
481
- migration_names = [
482
- (1, "learnings_columns"),
483
- (2, "followups_reasoning"),
484
- (3, "decisions_review"),
485
- (4, "session_diary_columns"),
486
- (5, "change_log_indexes"),
487
- (6, "error_guard_tables"),
488
- (7, "diary_source_and_draft"),
489
- (8, "adaptive_log_and_somatic"),
490
- (9, "maintenance_schedule"),
491
- (10, "diary_archive"),
492
- (11, "artifact_registry"),
493
- (12, "session_checkpoints"),
494
- (13, "claude_session_id"),
495
- ]
496
- for version, name in migration_names:
497
- conn.execute(
498
- "INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (?, ?)",
499
- (version, name)
500
- )
501
- print(" + Recorded all 13 migrations in schema_migrations")
502
-
503
- if not dry_run:
504
- conn.commit()
505
-
506
- return stats
507
-
508
-
509
- # ── cognitive.db migrations ─────────────────────────────────────────
510
-
511
- def migrate_cognitive_db(conn: sqlite3.Connection, dry_run: bool = False) -> dict:
512
- """Apply v1.6.0 schema additions to cognitive.db."""
513
- stats = {"tables_created": 0, "columns_added": 0, "indexes_created": 0}
514
-
515
- # ── Knowledge Graph: kg_nodes ───────────────────────────────────
516
- print("\n [KG] Knowledge Graph tables...")
517
- if create_table(conn, "kg_nodes", """
518
- CREATE TABLE IF NOT EXISTS kg_nodes (
519
- id INTEGER PRIMARY KEY AUTOINCREMENT,
520
- node_type TEXT NOT NULL,
521
- node_ref TEXT NOT NULL,
522
- label TEXT NOT NULL,
523
- properties TEXT DEFAULT '{}',
524
- created_at TEXT DEFAULT (datetime('now')),
525
- UNIQUE(node_type, node_ref)
526
- )
527
- """, dry_run):
528
- stats["tables_created"] += 1
529
- print(" + Table: kg_nodes")
530
- for idx, tbl, col in [
531
- ("idx_kg_nodes_type", "kg_nodes", "node_type"),
532
- ("idx_kg_nodes_label", "kg_nodes", "label"),
533
- ]:
534
- if add_index(conn, idx, tbl, col, dry_run):
535
- stats["indexes_created"] += 1
536
- print(f" + Index: {idx}")
537
-
538
- # ── Knowledge Graph: kg_edges ───────────────────────────────────
539
- if create_table(conn, "kg_edges", """
540
- CREATE TABLE IF NOT EXISTS kg_edges (
541
- id INTEGER PRIMARY KEY AUTOINCREMENT,
542
- source_id INTEGER NOT NULL REFERENCES kg_nodes(id),
543
- target_id INTEGER NOT NULL REFERENCES kg_nodes(id),
544
- relation TEXT NOT NULL,
545
- weight REAL DEFAULT 1.0,
546
- confidence REAL DEFAULT 1.0,
547
- valid_from TEXT DEFAULT (datetime('now')),
548
- valid_until TEXT DEFAULT NULL,
549
- source_memory_id TEXT DEFAULT '',
550
- properties TEXT DEFAULT '{}',
551
- created_at TEXT DEFAULT (datetime('now'))
552
- )
553
- """, dry_run):
554
- stats["tables_created"] += 1
555
- print(" + Table: kg_edges")
556
- for idx, tbl, col in [
557
- ("idx_kg_edges_source", "kg_edges", "source_id"),
558
- ("idx_kg_edges_target", "kg_edges", "target_id"),
559
- ("idx_kg_edges_relation", "kg_edges", "relation"),
560
- ]:
561
- if add_index(conn, idx, tbl, col, dry_run):
562
- stats["indexes_created"] += 1
563
- print(f" + Index: {idx}")
564
-
565
- # ── Somatic Markers (persistent) ────────────────────────────────
566
- print(" [SOM] Somatic markers...")
567
- if create_table(conn, "somatic_markers", """
568
- CREATE TABLE IF NOT EXISTS somatic_markers (
569
- id INTEGER PRIMARY KEY AUTOINCREMENT,
570
- target TEXT NOT NULL,
571
- target_type TEXT NOT NULL,
572
- valence REAL DEFAULT 0.0,
573
- arousal REAL DEFAULT 0.5,
574
- confidence REAL DEFAULT 0.5,
575
- event_count INTEGER DEFAULT 0,
576
- last_event_type TEXT DEFAULT '',
577
- last_event_at TEXT DEFAULT NULL,
578
- last_guard_decay_date TEXT DEFAULT NULL,
579
- last_validated_at TEXT DEFAULT NULL,
580
- created_at TEXT DEFAULT (datetime('now')),
581
- updated_at TEXT DEFAULT (datetime('now')),
582
- UNIQUE(target, target_type)
583
- )
584
- """, dry_run):
585
- stats["tables_created"] += 1
586
- print(" + Table: somatic_markers")
587
- if add_index(conn, "idx_somatic_target", "somatic_markers", "target", dry_run):
588
- stats["indexes_created"] += 1
589
- print(" + Index: idx_somatic_target")
590
-
591
- # ── Claim Graph ─────────────────────────────────────────────────
592
- print(" [CLM] Claim graph tables...")
593
- if create_table(conn, "claims", """
594
- CREATE TABLE IF NOT EXISTS claims (
595
- id INTEGER PRIMARY KEY AUTOINCREMENT,
596
- text TEXT NOT NULL,
597
- embedding BLOB,
598
- source_type TEXT NOT NULL DEFAULT '',
599
- source_id TEXT NOT NULL DEFAULT '',
600
- source_memory_store TEXT DEFAULT '',
601
- source_memory_id INTEGER DEFAULT 0,
602
- confidence REAL DEFAULT 1.0,
603
- verification_status TEXT DEFAULT 'unverified',
604
- verified_at TEXT,
605
- domain TEXT DEFAULT '',
606
- created_at TEXT DEFAULT (datetime('now')),
607
- updated_at TEXT DEFAULT (datetime('now'))
608
- )
609
- """, dry_run):
610
- stats["tables_created"] += 1
611
- print(" + Table: claims")
612
- if create_table(conn, "claim_links", """
613
- CREATE TABLE IF NOT EXISTS claim_links (
614
- id INTEGER PRIMARY KEY AUTOINCREMENT,
615
- source_claim_id INTEGER NOT NULL REFERENCES claims(id),
616
- target_claim_id INTEGER NOT NULL REFERENCES claims(id),
617
- relation TEXT NOT NULL,
618
- confidence REAL DEFAULT 1.0,
619
- created_at TEXT DEFAULT (datetime('now')),
620
- UNIQUE(source_claim_id, target_claim_id, relation)
621
- )
622
- """, dry_run):
623
- stats["tables_created"] += 1
624
- print(" + Table: claim_links")
625
- for idx, tbl, col in [
626
- ("idx_claims_source", "claims", "source_type, source_id"),
627
- ("idx_claims_domain", "claims", "domain"),
628
- ("idx_claims_status", "claims", "verification_status"),
629
- ("idx_claim_links_source", "claim_links", "source_claim_id"),
630
- ("idx_claim_links_target", "claim_links", "target_claim_id"),
631
- ]:
632
- if add_index(conn, idx, tbl, col, dry_run):
633
- stats["indexes_created"] += 1
634
- print(f" + Index: {idx}")
635
-
636
- # ── Trust Event Config ──────────────────────────────────────────
637
- print(" [TRS] Trust event config...")
638
- if create_table(conn, "trust_event_config", """
639
- CREATE TABLE IF NOT EXISTS trust_event_config (
640
- event TEXT PRIMARY KEY,
641
- delta REAL NOT NULL,
642
- description TEXT DEFAULT '',
643
- updated_at TEXT DEFAULT (datetime('now'))
644
- )
645
- """, dry_run):
646
- stats["tables_created"] += 1
647
- print(" + Table: trust_event_config")
648
-
649
- if not dry_run:
650
- conn.commit()
651
-
652
- return stats
653
-
654
-
655
- # ── Main ─────────────────────────────────────────────────────────────
656
-
657
- def main():
658
- parser = argparse.ArgumentParser(
659
- description=f"NEXO Migration: {VERSION_FROM} -> {VERSION_TO}",
660
- formatter_class=argparse.RawDescriptionHelpFormatter,
661
- epilog="Run without --dry-run to apply changes. Backups are created automatically."
662
- )
663
- parser.add_argument("--dry-run", action="store_true",
664
- help="Show what would change without modifying anything")
665
- parser.add_argument("--nexo-home", type=str, default=None,
666
- help="Override NEXO home directory (where nexo.db lives)")
667
- parser.add_argument("--skip-cognitive", action="store_true",
668
- help="Skip cognitive.db migration (only migrate nexo.db)")
669
- args = parser.parse_args()
670
-
671
- print(f"{'=' * 60}")
672
- print(f" NEXO Migration: {VERSION_FROM} -> {VERSION_TO}")
673
- print(f"{'=' * 60}")
674
- if args.dry_run:
675
- print(" MODE: DRY RUN (no changes will be made)\n")
676
-
677
- # ── Locate databases ────────────────────────────────────────────
678
- nexo_home = find_nexo_home(args.nexo_home)
679
- nexo_db_path = nexo_home / "nexo.db"
680
- cognitive_db_path = nexo_home / "cognitive.db"
681
-
682
- print(f" NEXO home: {nexo_home}")
683
- print(f" nexo.db: {nexo_db_path} {'(exists)' if nexo_db_path.exists() else '(NOT FOUND)'}")
684
- print(f" cognitive.db: {cognitive_db_path} {'(exists)' if cognitive_db_path.exists() else '(will be created)'}")
685
-
686
- if not nexo_db_path.exists():
687
- print(f"\n ERROR: nexo.db not found at {nexo_db_path}")
688
- print(" Set NEXO_HOME env var or use --nexo-home to specify the correct path.")
689
- sys.exit(1)
690
-
691
- # ── Backup ──────────────────────────────────────────────────────
692
- if not args.dry_run:
693
- print(f"\n Creating backups...")
694
- backup = backup_db(nexo_db_path)
695
- print(f" nexo.db -> {backup}")
696
- if cognitive_db_path.exists():
697
- cog_backup = backup_db(cognitive_db_path)
698
- print(f" cognitive.db -> {cog_backup}")
699
- else:
700
- print("\n [DRY RUN] Skipping backup")
701
-
702
- # ── Migrate nexo.db ─────────────────────────────────────────────
703
- print(f"\n{'─' * 60}")
704
- print(f" Migrating nexo.db...")
705
- print(f"{'─' * 60}")
706
-
707
- try:
708
- conn = sqlite3.connect(str(nexo_db_path), timeout=30)
709
- conn.execute("PRAGMA journal_mode=WAL")
710
- conn.execute("PRAGMA busy_timeout=30000")
711
- conn.execute("PRAGMA foreign_keys=ON")
712
-
713
- nexo_stats = migrate_nexo_db(conn, dry_run=args.dry_run)
714
-
715
- if not args.dry_run:
716
- conn.commit()
717
- conn.close()
718
- except Exception as e:
719
- print(f"\n ERROR migrating nexo.db: {e}")
720
- import traceback
721
- traceback.print_exc()
722
- sys.exit(1)
723
-
724
- # ── Migrate cognitive.db ────────────────────────────────────────
725
- cog_stats = {"tables_created": 0, "columns_added": 0, "indexes_created": 0}
726
- if not args.skip_cognitive:
727
- print(f"\n{'─' * 60}")
728
- print(f" Migrating cognitive.db...")
729
- print(f"{'─' * 60}")
730
-
731
- try:
732
- conn = sqlite3.connect(str(cognitive_db_path), timeout=30)
733
- conn.execute("PRAGMA journal_mode=WAL")
734
- conn.execute("PRAGMA busy_timeout=30000")
735
- conn.execute("PRAGMA foreign_keys=ON")
736
-
737
- cog_stats = migrate_cognitive_db(conn, dry_run=args.dry_run)
738
-
739
- if not args.dry_run:
740
- conn.commit()
741
- conn.close()
742
- except Exception as e:
743
- print(f"\n ERROR migrating cognitive.db: {e}")
744
- import traceback
745
- traceback.print_exc()
746
- sys.exit(1)
747
- else:
748
- print("\n Skipping cognitive.db (--skip-cognitive)")
749
-
750
- # ── Summary ─────────────────────────────────────────────────────
751
- total_tables = nexo_stats["tables_created"] + cog_stats["tables_created"]
752
- total_columns = nexo_stats["columns_added"] + cog_stats.get("columns_added", 0)
753
- total_indexes = nexo_stats["indexes_created"] + cog_stats["indexes_created"]
754
-
755
- print(f"\n{'=' * 60}")
756
- if args.dry_run:
757
- print(" DRY RUN SUMMARY")
758
- else:
759
- print(" MIGRATION COMPLETE")
760
- print(f"{'=' * 60}")
761
- print(f" Tables created: {total_tables}")
762
- print(f" Columns added: {total_columns}")
763
- print(f" Indexes created: {total_indexes}")
764
- if nexo_stats.get("data_seeded"):
765
- print(f" Data seeded: {nexo_stats['data_seeded']} maintenance tasks")
766
-
767
- if total_tables == 0 and total_columns == 0 and total_indexes == 0:
768
- print("\n Database is already at v1.6.0 schema. Nothing to do.")
769
- elif not args.dry_run:
770
- print(f"\n Your database has been upgraded to v1.6.0.")
771
- print(f" Backup saved alongside the original DB file.")
772
-
773
- print()
774
- return 0
775
-
776
-
777
- if __name__ == "__main__":
778
- sys.exit(main())