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,464 +0,0 @@
1
- """NEXO Cognitive — Trust scoring, sentiment, dissonance."""
2
- import re
3
- import numpy as np
4
- from datetime import datetime, timedelta
5
- from cognitive._core import _get_db, embed, cosine_similarity, _blob_to_array
6
- from cognitive._core import POSITIVE_SIGNALS, NEGATIVE_SIGNALS, URGENCY_SIGNALS
7
-
8
-
9
- # Trust score events — default deltas (overridable via trust_event_config table)
10
- _DEFAULT_TRUST_EVENTS = {
11
- # Positive
12
- "explicit_thanks": +3,
13
- "delegation": +2, # user delegates new task without micromanaging
14
- "paradigm_shift": +2, # user teaches, NEXO learns
15
- "sibling_detected": +3, # NEXO avoided context error on its own
16
- "proactive_action": +2, # NEXO did something useful without being asked
17
- # Negative
18
- "correction": -3, # user corrects NEXO
19
- "repeated_error": -7, # Error on something NEXO already had a learning for
20
- "override": -5, # NEXO's memory was wrong
21
- "correction_fatigue": -10, # Same memory corrected 3+ times
22
- "forgot_followup": -4, # Forgot to mark followup or execute it
23
- }
24
-
25
- # Lazy-loaded from DB (trust_event_config table overrides defaults)
26
- _trust_events_cache = None
27
- _trust_events_cache_ts = 0
28
-
29
-
30
- def get_trust_events() -> dict:
31
- """Get trust events with deltas. DB overrides take priority over defaults."""
32
- global _trust_events_cache, _trust_events_cache_ts
33
- import time
34
- now = time.time()
35
- # Cache for 60s to avoid constant DB reads
36
- if _trust_events_cache is not None and (now - _trust_events_cache_ts) < 60:
37
- return _trust_events_cache
38
-
39
- events = dict(_DEFAULT_TRUST_EVENTS)
40
- try:
41
- db = _get_db()
42
- db.execute("""
43
- CREATE TABLE IF NOT EXISTS trust_event_config (
44
- event TEXT PRIMARY KEY,
45
- delta REAL NOT NULL,
46
- description TEXT DEFAULT '',
47
- updated_at TEXT DEFAULT (datetime('now'))
48
- )
49
- """)
50
- rows = db.execute("SELECT event, delta FROM trust_event_config").fetchall()
51
- for r in rows:
52
- events[r[0]] = r[1]
53
- except Exception:
54
- pass
55
- _trust_events_cache = events
56
- _trust_events_cache_ts = now
57
- return events
58
-
59
- # For backward compat — code that reads TRUST_EVENTS directly
60
- TRUST_EVENTS = _DEFAULT_TRUST_EVENTS
61
-
62
- # Auto-detection patterns for trust events from user text
63
- # Each pattern: (event_name, keywords/phrases that trigger it, min_matches)
64
- TRUST_AUTO_PATTERNS = {
65
- "explicit_thanks": {
66
- "patterns": [
67
- "gracias", "buen trabajo", "bien hecho", "perfecto", "genial",
68
- "excelente", "fenomenal", "great job", "nice work", "thank",
69
- "thanks", "awesome", "amazing", "love it", "me encanta",
70
- ],
71
- "min_matches": 1,
72
- },
73
- "correction": {
74
- "patterns": [
75
- "ya te dije", "ya te lo dije", "otra vez", "te he dicho",
76
- "no es así", "eso no", "mal", "incorrecto", "equivocado",
77
- "no no no", "that's wrong", "te aviso", "te avisé",
78
- "2ª vez", "segunda vez", "te lo repito",
79
- ],
80
- "min_matches": 1,
81
- },
82
- "repeated_error": {
83
- "patterns": [
84
- "otra vez lo mismo", "siempre igual", "ya te lo dije antes",
85
- "cuántas veces", "no aprendes", "same mistake", "again the same",
86
- "ya van", "es la 2", "es la 3", "ya te avisé",
87
- ],
88
- "min_matches": 1,
89
- },
90
- "delegation": {
91
- "patterns": [
92
- "encárgate", "hazlo tú", "dale tú", "te lo dejo",
93
- "manéjalo", "resuélvelo", "handle it", "take care of",
94
- "you decide", "tú decides", "lo que veas", "como veas",
95
- ],
96
- "min_matches": 1,
97
- },
98
- }
99
-
100
-
101
- def auto_detect_trust_events(text: str) -> list[dict]:
102
- """Detect trust events from user text. Returns list of {event, delta, reason}.
103
-
104
- Called automatically by heartbeat. Only fires once per event per heartbeat
105
- to avoid double-counting.
106
- """
107
- if not text or len(text.strip()) < 5:
108
- return []
109
-
110
- text_lower = text.lower()
111
- events = get_trust_events()
112
- detected = []
113
-
114
- for event_name, config in TRUST_AUTO_PATTERNS.items():
115
- matches = [p for p in config["patterns"] if p in text_lower]
116
- if len(matches) >= config["min_matches"]:
117
- delta = events.get(event_name, _DEFAULT_TRUST_EVENTS.get(event_name, 0))
118
- detected.append({
119
- "event": event_name,
120
- "delta": delta,
121
- "reason": f"auto-detected: {', '.join(matches[:3])}",
122
- })
123
-
124
- # Priority: if repeated_error detected, remove correction (it's a superset)
125
- event_names = {d["event"] for d in detected}
126
- if "repeated_error" in event_names and "correction" in event_names:
127
- detected = [d for d in detected if d["event"] != "correction"]
128
- # If explicit_thanks and delegation both detected, keep both (they're independent)
129
-
130
- return detected
131
-
132
- def detect_dissonance(new_instruction: str, min_score: float = 0.65) -> list[dict]:
133
- """Detect cognitive dissonance: find LTM memories that contradict a new instruction.
134
-
135
- When user gives a new instruction that conflicts with established LTM memories
136
- (strength > 0.8), this function surfaces the conflict so NEXO can verbalize it
137
- rather than silently obeying or silently resisting.
138
-
139
- Args:
140
- new_instruction: The new instruction or preference from user
141
- min_score: Minimum cosine similarity to consider as potential conflict
142
-
143
- Returns:
144
- List of conflicting memories with their strength and content
145
- """
146
- db = _get_db()
147
- query_vec = embed(new_instruction[:500])
148
- if np.linalg.norm(query_vec) == 0:
149
- return []
150
-
151
- rows = db.execute(
152
- "SELECT id, content, embedding, source_type, domain, strength, access_count FROM ltm_memories WHERE is_dormant = 0 AND strength > 0.8"
153
- ).fetchall()
154
-
155
- conflicts = []
156
- for row in rows:
157
- vec = _blob_to_array(row["embedding"])
158
- score = cosine_similarity(query_vec, vec)
159
- if score >= min_score:
160
- conflicts.append({
161
- "memory_id": row["id"],
162
- "content": row["content"],
163
- "source_type": row["source_type"],
164
- "domain": row["domain"],
165
- "strength": row["strength"],
166
- "access_count": row["access_count"],
167
- "similarity": round(score, 3),
168
- })
169
-
170
- conflicts.sort(key=lambda x: x["similarity"], reverse=True)
171
- return conflicts[:5]
172
-
173
-
174
- def resolve_dissonance(memory_id: int, resolution: str, context: str = "") -> str:
175
- """Resolve a cognitive dissonance by applying user's decision.
176
-
177
- Args:
178
- memory_id: The LTM memory that conflicts with the new instruction
179
- resolution: One of:
180
- - 'paradigm_shift': user changed his mind permanently. Decay old memory,
181
- new instruction becomes the standard.
182
- - 'exception': This is a one-time override. Keep old memory as standard.
183
- - 'override': Old memory was wrong. Mark as corrupted and decay to dormant.
184
-
185
- Returns:
186
- Status message
187
- """
188
- db = _get_db()
189
- row = db.execute("SELECT * FROM ltm_memories WHERE id = ?", (memory_id,)).fetchone()
190
- if not row:
191
- return f"Memory #{memory_id} not found."
192
-
193
- now = datetime.utcnow().isoformat()
194
-
195
- if resolution == "paradigm_shift":
196
- # Instant decay to 0.3, will naturally fade. New instruction takes over.
197
- db.execute(
198
- "UPDATE ltm_memories SET strength = 0.3, last_accessed = ? WHERE id = ?",
199
- (now, memory_id)
200
- )
201
- msg = f"Paradigm shift: Memory #{memory_id} decayed to 0.3. New standard will replace it."
202
-
203
- elif resolution == "exception":
204
- # Keep memory as-is, just log the exception
205
- msg = f"Exception noted: Memory #{memory_id} remains standard. One-time override applied."
206
-
207
- elif resolution == "override":
208
- # Memory was wrong — mark as corrupted/dormant
209
- db.execute(
210
- "UPDATE ltm_memories SET strength = 0.05, is_dormant = 1, last_accessed = ? WHERE id = ?",
211
- (now, memory_id)
212
- )
213
- msg = f"Override: Memory #{memory_id} marked corrupted and dormant."
214
-
215
- else:
216
- return f"Unknown resolution: {resolution}. Use 'paradigm_shift', 'exception', or 'override'."
217
-
218
- # Log the correction
219
- db.execute(
220
- "INSERT INTO memory_corrections (memory_id, store, correction_type, context) VALUES (?, 'ltm', ?, ?)",
221
- (memory_id, resolution, context[:500])
222
- )
223
- db.commit()
224
-
225
- return msg
226
-
227
-
228
- def check_correction_fatigue() -> list[dict]:
229
- """Find memories corrected 3+ times in the last 7 days — mark as 'under review'.
230
-
231
- These memories are unreliable: user keeps overriding them, suggesting
232
- the memory itself may be wrong or outdated.
233
-
234
- Returns:
235
- List of memories that should be flagged as unreliable
236
- """
237
- db = _get_db()
238
- cutoff = (datetime.utcnow() - timedelta(days=7)).isoformat()
239
-
240
- rows = db.execute("""
241
- SELECT memory_id, COUNT(*) as correction_count,
242
- GROUP_CONCAT(correction_type) as types
243
- FROM memory_corrections
244
- WHERE created_at >= ? AND store = 'ltm'
245
- GROUP BY memory_id
246
- HAVING COUNT(*) >= 3
247
- """, (cutoff,)).fetchall()
248
-
249
- fatigued = []
250
- for row in rows:
251
- mem = db.execute(
252
- "SELECT content, strength, source_type, domain FROM ltm_memories WHERE id = ?",
253
- (row["memory_id"],)
254
- ).fetchone()
255
- if mem:
256
- fatigued.append({
257
- "memory_id": row["memory_id"],
258
- "corrections_7d": row["correction_count"],
259
- "types": row["types"],
260
- "content": mem["content"][:200],
261
- "strength": mem["strength"],
262
- "source_type": mem["source_type"],
263
- "domain": mem["domain"],
264
- })
265
-
266
- # Auto-mark as under review: decay strength to 0.2
267
- db.execute(
268
- "UPDATE ltm_memories SET strength = MIN(strength, 0.2), tags = CASE WHEN tags LIKE '%under_review%' THEN tags ELSE tags || ',under_review' END WHERE id = ?",
269
- (row["memory_id"],)
270
- )
271
-
272
- if fatigued:
273
- db.commit()
274
-
275
- return fatigued
276
-
277
- def detect_sentiment(text: str) -> dict:
278
- """Analyze user's text for sentiment signals.
279
-
280
- Returns detected sentiment, intensity, and action guidance for NEXO.
281
- Not a model — keyword + heuristic based. Fast and deterministic.
282
- """
283
- if not text:
284
- return {"sentiment": "neutral", "intensity": 0.5, "signals": [], "guidance": ""}
285
-
286
- text_lower = text.lower()
287
- words = set(text_lower.split())
288
-
289
- positive_hits = [s for s in POSITIVE_SIGNALS if s in text_lower]
290
- negative_hits = [s for s in NEGATIVE_SIGNALS if s in text_lower]
291
- urgency_hits = [s for s in URGENCY_SIGNALS if s in text_lower]
292
-
293
- # Heuristics
294
- is_short = len(text) < 30
295
- has_caps = any(c.isupper() for c in text[1:]) if len(text) > 1 else False # ignore first char
296
- has_exclamation = "!" in text
297
- all_caps_words = sum(1 for w in text.split() if w.isupper() and len(w) > 1)
298
-
299
- # Score
300
- pos_score = len(positive_hits)
301
- neg_score = len(negative_hits)
302
-
303
- # Caps/short boost negative
304
- if all_caps_words >= 2:
305
- neg_score += 2
306
- if is_short and neg_score > 0:
307
- neg_score += 1 # Short + negative = terse frustration
308
-
309
- if urgency_hits:
310
- neg_score += 1 # Urgency often means something is wrong
311
-
312
- # Determine sentiment
313
- if neg_score > pos_score and neg_score >= 1:
314
- sentiment = "negative"
315
- intensity = min(1.0, 0.3 + neg_score * 0.15)
316
- if intensity > 0.7:
317
- guidance = "MODE: Ultra-concise. Zero explanations. Solve and show result."
318
- else:
319
- guidance = "MODE: Concise. Less context, more direct action."
320
- elif pos_score > neg_score and pos_score >= 1:
321
- sentiment = "positive"
322
- intensity = min(1.0, 0.3 + pos_score * 0.15)
323
- guidance = "MODE: Normal. Good time to suggest backlog ideas or improvements."
324
- elif urgency_hits:
325
- sentiment = "urgent"
326
- intensity = 0.8
327
- guidance = "MODE: Immediate action. No preambles."
328
- else:
329
- sentiment = "neutral"
330
- intensity = 0.5
331
- guidance = ""
332
-
333
- return {
334
- "sentiment": sentiment,
335
- "intensity": round(intensity, 2),
336
- "signals": positive_hits + negative_hits + urgency_hits,
337
- "guidance": guidance,
338
- }
339
-
340
-
341
- def log_sentiment(text: str) -> dict:
342
- """Detect and log user's sentiment. Returns the detection result."""
343
- result = detect_sentiment(text)
344
- if result["sentiment"] != "neutral":
345
- db = _get_db()
346
- db.execute(
347
- "INSERT INTO sentiment_log (sentiment, intensity, signals) VALUES (?, ?, ?)",
348
- (result["sentiment"], result["intensity"], ",".join(result["signals"]))
349
- )
350
- db.commit()
351
- return result
352
-
353
-
354
- def get_trust_score() -> float:
355
- """Get current trust score. Starts at 50, range 0-100."""
356
- db = _get_db()
357
- row = db.execute("SELECT score FROM trust_score ORDER BY id DESC LIMIT 1").fetchone()
358
- if row is None:
359
- # Initialize
360
- db.execute(
361
- "INSERT INTO trust_score (score, event, delta, context) VALUES (50, 'init', 0, 'Initial trust score')"
362
- )
363
- db.commit()
364
- return 50.0
365
- return row[0]
366
-
367
-
368
- def _annotate_adaptive_log(event: str, delta: float):
369
- """Retroactively annotate the most recent adaptive_log entry with trust feedback."""
370
- try:
371
- from db import get_db
372
- conn = get_db()
373
- conn.execute(
374
- "UPDATE adaptive_log SET feedback_event = ?, feedback_delta = ?, "
375
- "feedback_ts = datetime('now') "
376
- "WHERE id = (SELECT id FROM adaptive_log "
377
- "WHERE feedback_event IS NULL "
378
- "AND timestamp >= datetime('now', '-5 minutes') "
379
- "ORDER BY id DESC LIMIT 1)",
380
- (event, int(delta))
381
- )
382
- conn.commit()
383
- except Exception:
384
- pass
385
-
386
-
387
- def adjust_trust(event: str, context: str = "", custom_delta: float = None) -> dict:
388
- """Adjust trust score based on an event.
389
-
390
- Args:
391
- event: Event type from TRUST_EVENTS or custom
392
- context: Description of what happened
393
- custom_delta: Override the default point value
394
-
395
- Returns:
396
- Dict with old_score, delta, new_score, event
397
- """
398
- db = _get_db()
399
- old_score = get_trust_score()
400
-
401
- events = get_trust_events()
402
- delta = custom_delta if custom_delta is not None else events.get(event, 0)
403
- if delta == 0 and custom_delta is None:
404
- return {"old_score": old_score, "delta": 0, "new_score": old_score, "event": event, "error": "unknown event"}
405
-
406
- new_score = max(0.0, min(100.0, old_score + delta))
407
-
408
- db.execute(
409
- "INSERT INTO trust_score (score, event, delta, context) VALUES (?, ?, ?, ?)",
410
- (new_score, event, delta, context[:500])
411
- )
412
- db.commit()
413
-
414
- # Annotate adaptive log for learned weights
415
- _annotate_adaptive_log(event, delta)
416
-
417
- # Somatic event logging for repeated_error events (append-only in nexo.db)
418
- if event == "repeated_error" and context:
419
- try:
420
- from db import get_db as get_nexo_db
421
- area = context.split(":")[0].strip() if ":" in context else "unknown"
422
- get_nexo_db().execute(
423
- "INSERT INTO somatic_events (target, target_type, event_type, delta, source) VALUES (?, ?, ?, ?, ?)",
424
- (area, "area", "repeated_error", 0.20, f"trust:{event}")
425
- )
426
- get_nexo_db().commit()
427
- except Exception:
428
- pass
429
-
430
- return {
431
- "old_score": round(old_score, 1),
432
- "delta": delta,
433
- "new_score": round(new_score, 1),
434
- "event": event,
435
- }
436
-
437
-
438
- def get_trust_history(days: int = 7) -> dict:
439
- """Get trust score history and sentiment summary."""
440
- db = _get_db()
441
- cutoff = (datetime.utcnow() - timedelta(days=days)).isoformat()
442
-
443
- # Trust events
444
- events = db.execute(
445
- "SELECT event, delta, score, context, created_at FROM trust_score WHERE created_at >= ? ORDER BY id",
446
- (cutoff,)
447
- ).fetchall()
448
-
449
- # Sentiment distribution
450
- sentiments = db.execute(
451
- "SELECT sentiment, COUNT(*) as cnt, AVG(intensity) as avg_int FROM sentiment_log WHERE created_at >= ? GROUP BY sentiment",
452
- (cutoff,)
453
- ).fetchall()
454
-
455
- current = get_trust_score()
456
- start_score = events[0]["score"] - events[0]["delta"] if events else current
457
-
458
- return {
459
- "current_score": round(current, 1),
460
- "period_start_score": round(start_score, 1),
461
- "net_change": round(current - start_score, 1),
462
- "events": [{"event": e["event"], "delta": e["delta"], "score": round(e["score"], 1), "context": e["context"][:100], "at": e["created_at"]} for e in events],
463
- "sentiment_distribution": {s["sentiment"]: {"count": s["cnt"], "avg_intensity": round(s["avg_int"], 2)} for s in sentiments},
464
- }
@@ -1,106 +0,0 @@
1
- {
2
- "$schema": "NEXO cron manifest — synced by nexo_update to LaunchAgents (macOS) or systemd timers (Linux)",
3
- "version": 2,
4
- "crons": [
5
- {
6
- "id": "deep-sleep",
7
- "script": "scripts/nexo-deep-sleep.sh",
8
- "type": "shell",
9
- "schedule": {"hour": 4, "minute": 30},
10
- "description": "Overnight session analysis — 4 phases: collect, extract, synthesize, apply",
11
- "core": true
12
- },
13
- {
14
- "id": "sleep",
15
- "script": "scripts/nexo-sleep.py",
16
- "schedule": {"hour": 4, "minute": 0},
17
- "description": "Nightly memory consolidation and dream cycle",
18
- "core": true
19
- },
20
- {
21
- "id": "cognitive-decay",
22
- "script": "scripts/nexo-cognitive-decay.py",
23
- "schedule": {"hour": 3, "minute": 0},
24
- "description": "Memory decay — reduce strength of unaccessed memories",
25
- "core": true
26
- },
27
- {
28
- "id": "learning-housekeep",
29
- "script": "scripts/nexo-learning-housekeep.py",
30
- "schedule": {"hour": 3, "minute": 15},
31
- "description": "Archive stale learnings, deduplicate, validate",
32
- "core": true
33
- },
34
- {
35
- "id": "immune",
36
- "script": "scripts/nexo-immune.py",
37
- "interval_seconds": 1800,
38
- "description": "Health monitor — checks MCP, DB, services, auto-repairs",
39
- "core": true
40
- },
41
- {
42
- "id": "watchdog",
43
- "script": "scripts/nexo-watchdog.sh",
44
- "type": "shell",
45
- "interval_seconds": 1800,
46
- "description": "System health checks — snapshots, logs, alerts",
47
- "core": true
48
- },
49
- {
50
- "id": "self-audit",
51
- "script": "scripts/nexo-daily-self-audit.py",
52
- "schedule": {"hour": 7, "minute": 0},
53
- "description": "Daily self-audit — validates learnings, protocols, drift",
54
- "core": true
55
- },
56
- {
57
- "id": "postmortem",
58
- "script": "scripts/nexo-postmortem-consolidator.py",
59
- "schedule": {"hour": 23, "minute": 30},
60
- "description": "Consolidate session post-mortems into patterns",
61
- "core": true
62
- },
63
- {
64
- "id": "evolution",
65
- "script": "scripts/nexo-evolution-run.py",
66
- "schedule": {"hour": 5, "minute": 0, "weekday": 0},
67
- "description": "Weekly self-improvement cycle — propose and evaluate changes",
68
- "core": true
69
- },
70
- {
71
- "id": "followup-hygiene",
72
- "script": "scripts/nexo-followup-hygiene.py",
73
- "schedule": {"hour": 5, "minute": 0},
74
- "description": "Clean stale followups, archive completed, validate dates",
75
- "core": true
76
- },
77
- {
78
- "id": "synthesis",
79
- "script": "scripts/nexo-synthesis.py",
80
- "interval_seconds": 7200,
81
- "description": "Periodic synthesis — cross-reference learnings, decisions, changes",
82
- "core": true
83
- },
84
- {
85
- "id": "auto-close-sessions",
86
- "script": "scripts/nexo-auto-close-sessions.py",
87
- "interval_seconds": 300,
88
- "description": "Close stale sessions that lost their parent process",
89
- "core": true
90
- },
91
- {
92
- "id": "github-monitor",
93
- "script": "scripts/nexo-github-monitor.py",
94
- "schedule": {"hour": 8, "minute": 0},
95
- "description": "Monitor GitHub repo — issues, PRs, stars, auto-respond",
96
- "core": true
97
- },
98
- {
99
- "id": "catchup",
100
- "script": "scripts/nexo-catchup.py",
101
- "schedule": {"hour": 8, "minute": 30},
102
- "description": "Morning catchup briefing for the user",
103
- "core": true
104
- }
105
- ]
106
- }