nexo-brain 2.2.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 (256) hide show
  1. package/README.md +5 -5
  2. package/package.json +6 -3
  3. package/src/auto_update.py +26 -0
  4. package/src/crons/manifest.json +6 -13
  5. package/src/crons/sync.py +150 -6
  6. package/src/db/__init__.py +13 -0
  7. package/src/db/_core.py +1 -0
  8. package/src/db/_cron_runs.py +74 -0
  9. package/src/db/_entities.py +1 -0
  10. package/src/db/_episodic.py +41 -6
  11. package/src/db/_learnings.py +1 -0
  12. package/src/db/_reminders.py +1 -0
  13. package/src/db/_schema.py +64 -0
  14. package/src/db/_sessions.py +1 -0
  15. package/src/db/_skills.py +515 -0
  16. package/src/hooks/session-stop.sh +13 -101
  17. package/src/plugin_loader.py +1 -0
  18. package/src/plugins/episodic_memory.py +5 -3
  19. package/src/plugins/schedule.py +212 -0
  20. package/src/plugins/skills.py +264 -0
  21. package/src/plugins/update.py +1 -0
  22. package/src/scripts/deep-sleep/apply_findings.py +111 -8
  23. package/src/scripts/deep-sleep/collect.py +34 -11
  24. package/src/scripts/deep-sleep/extract-prompt.md +38 -0
  25. package/src/scripts/deep-sleep/extract.py +81 -8
  26. package/src/scripts/deep-sleep/synthesize-prompt.md +29 -1
  27. package/src/scripts/deep-sleep/synthesize.py +4 -1
  28. package/src/scripts/nexo-catchup.py +65 -29
  29. package/src/scripts/nexo-cron-wrapper.sh +53 -0
  30. package/src/scripts/nexo-daily-self-audit.py +4 -2
  31. package/src/scripts/nexo-deep-sleep.sh +66 -77
  32. package/src/scripts/nexo-evolution-run.py +13 -0
  33. package/src/scripts/nexo-learning-housekeep.py +157 -1
  34. package/src/scripts/nexo-learning-validator.py +19 -0
  35. package/src/scripts/nexo-postmortem-consolidator.py +3 -2
  36. package/src/scripts/nexo-sleep.py +16 -11
  37. package/src/scripts/nexo-synthesis.py +46 -3
  38. package/src/scripts/nexo-watchdog.sh +91 -30
  39. package/src/server.py +6 -1
  40. package/src/tools_coordination.py +1 -0
  41. package/src/tools_sessions.py +1 -0
  42. package/scripts/migrate-to-unified 2.sh +0 -813
  43. package/scripts/migrate-to-unified.sh +0 -813
  44. package/scripts/migrate-v1.5-to-v1.6 2.py +0 -778
  45. package/scripts/migrate-v1.5-to-v1.6.py +0 -778
  46. package/scripts/migrate-v1.7-to-v1.8 2.py +0 -214
  47. package/scripts/migrate-v1.7-to-v1.8.py +0 -214
  48. package/scripts/pre-commit-check 2.sh +0 -55
  49. package/scripts/pre-commit-check.sh +0 -55
  50. package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
  51. package/src/__pycache__/hnsw_index.cpython-310.pyc +0 -0
  52. package/src/__pycache__/kg_populate.cpython-310.pyc +0 -0
  53. package/src/__pycache__/knowledge_graph.cpython-310.pyc +0 -0
  54. package/src/__pycache__/plugin_loader.cpython-310.pyc +0 -0
  55. package/src/__pycache__/tools_coordination.cpython-310.pyc +0 -0
  56. package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
  57. package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
  58. package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
  59. package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
  60. package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
  61. package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
  62. package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
  63. package/src/auto_close_sessions 2.py +0 -159
  64. package/src/auto_update 2.py +0 -634
  65. package/src/claim_graph 2.py +0 -323
  66. package/src/cognitive/__init__ 2.py +0 -62
  67. package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
  68. package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
  69. package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
  70. package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
  71. package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
  72. package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
  73. package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
  74. package/src/cognitive/_core 2.py +0 -567
  75. package/src/cognitive/_decay 2.py +0 -382
  76. package/src/cognitive/_ingest 2.py +0 -892
  77. package/src/cognitive/_memory 2.py +0 -912
  78. package/src/cognitive/_search 2.py +0 -949
  79. package/src/cognitive/_trust 2.py +0 -464
  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__/_entities.cpython-310.pyc +0 -0
  97. package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
  98. package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
  99. package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
  100. package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
  101. package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
  102. package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
  103. package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
  104. package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
  105. package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
  106. package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
  107. package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
  108. package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
  109. package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
  110. package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
  111. package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
  112. package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
  113. package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
  114. package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
  115. package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
  116. package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
  117. package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
  118. package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
  119. package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
  120. package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
  121. package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
  122. package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
  123. package/src/db/_core 2.py +0 -417
  124. package/src/db/_credentials 2.py +0 -124
  125. package/src/db/_entities 2.py +0 -178
  126. package/src/db/_episodic 2.py +0 -738
  127. package/src/db/_evolution 2.py +0 -54
  128. package/src/db/_fts 2.py +0 -406
  129. package/src/db/_learnings 2.py +0 -168
  130. package/src/db/_reminders 2.py +0 -338
  131. package/src/db/_schema 2.py +0 -364
  132. package/src/db/_sessions 2.py +0 -300
  133. package/src/db/_tasks 2.py +0 -91
  134. package/src/evolution_cycle 2.py +0 -266
  135. package/src/hnsw_index 2.py +0 -254
  136. package/src/hooks/auto_capture 2.py +0 -208
  137. package/src/hooks/caffeinate-guard 2.sh +0 -8
  138. package/src/hooks/capture-session 2.sh +0 -21
  139. package/src/hooks/capture-tool-logs 2.sh +0 -127
  140. package/src/hooks/daily-briefing-check 2.sh +0 -33
  141. package/src/hooks/inbox-hook 2.sh +0 -76
  142. package/src/hooks/post-compact 2.sh +0 -148
  143. package/src/hooks/pre-compact 2.sh +0 -151
  144. package/src/hooks/session-start 2.sh +0 -268
  145. package/src/hooks/session-stop 2.sh +0 -140
  146. package/src/kg_populate 2.py +0 -290
  147. package/src/knowledge_graph 2.py +0 -257
  148. package/src/maintenance 2.py +0 -59
  149. package/src/migrate_embeddings 2.py +0 -122
  150. package/src/plugin_loader 2.py +0 -202
  151. package/src/plugins/__init__ 2.py +0 -0
  152. package/src/plugins/__pycache__/__init__ 2.cpython-310.pyc +0 -0
  153. package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
  154. package/src/plugins/__pycache__/adaptive_mode 2.cpython-310.pyc +0 -0
  155. package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
  156. package/src/plugins/__pycache__/agents 2.cpython-310.pyc +0 -0
  157. package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
  158. package/src/plugins/__pycache__/artifact_registry 2.cpython-310.pyc +0 -0
  159. package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
  160. package/src/plugins/__pycache__/backup 2.cpython-310.pyc +0 -0
  161. package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
  162. package/src/plugins/__pycache__/cognitive_memory 2.cpython-310.pyc +0 -0
  163. package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
  164. package/src/plugins/__pycache__/core_rules 2.cpython-310.pyc +0 -0
  165. package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
  166. package/src/plugins/__pycache__/cortex 2.cpython-310.pyc +0 -0
  167. package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
  168. package/src/plugins/__pycache__/entities 2.cpython-310.pyc +0 -0
  169. package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
  170. package/src/plugins/__pycache__/episodic_memory 2.cpython-310.pyc +0 -0
  171. package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
  172. package/src/plugins/__pycache__/evolution 2.cpython-310.pyc +0 -0
  173. package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
  174. package/src/plugins/__pycache__/guard 2.cpython-310.pyc +0 -0
  175. package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
  176. package/src/plugins/__pycache__/knowledge_graph_tools 2.cpython-310.pyc +0 -0
  177. package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
  178. package/src/plugins/__pycache__/preferences 2.cpython-310.pyc +0 -0
  179. package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
  180. package/src/plugins/__pycache__/update 2.cpython-310.pyc +0 -0
  181. package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
  182. package/src/plugins/adaptive_mode 2.py +0 -805
  183. package/src/plugins/agents 2.py +0 -52
  184. package/src/plugins/artifact_registry 2.py +0 -450
  185. package/src/plugins/backup 2.py +0 -104
  186. package/src/plugins/cognitive_memory 2.py +0 -564
  187. package/src/plugins/core_rules 2.py +0 -252
  188. package/src/plugins/cortex 2.py +0 -299
  189. package/src/plugins/entities 2.py +0 -67
  190. package/src/plugins/episodic_memory 2.py +0 -533
  191. package/src/plugins/evolution 2.py +0 -115
  192. package/src/plugins/guard 2.py +0 -746
  193. package/src/plugins/knowledge_graph_tools 2.py +0 -105
  194. package/src/plugins/preferences 2.py +0 -47
  195. package/src/plugins/update 2.py +0 -256
  196. package/src/requirements 2.txt +0 -12
  197. package/src/rules/__init__ 2.py +0 -0
  198. package/src/rules/core-rules 2.json +0 -331
  199. package/src/rules/migrate 2.py +0 -207
  200. package/src/scripts/check-context 2.py +0 -264
  201. package/src/scripts/nexo-auto-update 2.py +0 -6
  202. package/src/scripts/nexo-backup 2.sh +0 -25
  203. package/src/scripts/nexo-brain-activation 2.sh +0 -140
  204. package/src/scripts/nexo-catchup 2.py +0 -242
  205. package/src/scripts/nexo-cognitive-decay 2.py +0 -182
  206. package/src/scripts/nexo-daily-self-audit 2.py +0 -552
  207. package/src/scripts/nexo-deep-sleep 2.sh +0 -97
  208. package/src/scripts/nexo-evolution-run 2.py +0 -597
  209. package/src/scripts/nexo-followup-hygiene 2.py +0 -112
  210. package/src/scripts/nexo-github-monitor 2.py +0 -256
  211. package/src/scripts/nexo-github-monitor.py +0 -256
  212. package/src/scripts/nexo-immune 2.py +0 -927
  213. package/src/scripts/nexo-inbox-hook 2.sh +0 -74
  214. package/src/scripts/nexo-install 2.py +0 -6
  215. package/src/scripts/nexo-learning-housekeep 2.py +0 -245
  216. package/src/scripts/nexo-learning-validator 2.py +0 -207
  217. package/src/scripts/nexo-migrate 2.py +0 -232
  218. package/src/scripts/nexo-postmortem-consolidator 2.py +0 -421
  219. package/src/scripts/nexo-pre-commit 2.py +0 -120
  220. package/src/scripts/nexo-prevent-sleep 2.sh +0 -29
  221. package/src/scripts/nexo-proactive-dashboard 2.py +0 -345
  222. package/src/scripts/nexo-reflection 2.py +0 -253
  223. package/src/scripts/nexo-runtime-preflight 2.py +0 -274
  224. package/src/scripts/nexo-send-email 2.py +0 -25
  225. package/src/scripts/nexo-send-email.py +0 -25
  226. package/src/scripts/nexo-send-reply 2.py +0 -178
  227. package/src/scripts/nexo-send-reply.py +0 -178
  228. package/src/scripts/nexo-sleep 2.py +0 -592
  229. package/src/scripts/nexo-snapshot-restore 2.sh +0 -35
  230. package/src/scripts/nexo-synthesis 2.py +0 -253
  231. package/src/scripts/nexo-tcc-approve 2.sh +0 -79
  232. package/src/scripts/nexo-update 2.sh +0 -161
  233. package/src/scripts/nexo-watchdog 2.sh +0 -878
  234. package/src/scripts/nexo-watchdog-smoke 2.py +0 -119
  235. package/src/server 2.py +0 -733
  236. package/src/storage_router 2.py +0 -32
  237. package/src/tools_coordination 2.py +0 -102
  238. package/src/tools_credentials 2.py +0 -68
  239. package/src/tools_learnings 2.py +0 -220
  240. package/src/tools_menu 2.py +0 -227
  241. package/src/tools_reminders 2.py +0 -86
  242. package/src/tools_reminders_crud 2.py +0 -159
  243. package/src/tools_sessions 2.py +0 -476
  244. package/src/tools_task_history 2.py +0 -57
  245. package/templates/CLAUDE.md 2.template +0 -63
  246. package/templates/openclaw 2.json +0 -13
  247. package/tests/__init__ 2.py +0 -0
  248. package/tests/__init__.py +0 -0
  249. package/tests/conftest 2.py +0 -71
  250. package/tests/conftest.py +0 -71
  251. package/tests/test_cognitive 2.py +0 -205
  252. package/tests/test_cognitive.py +0 -205
  253. package/tests/test_knowledge_graph 2.py +0 -140
  254. package/tests/test_knowledge_graph.py +0 -140
  255. package/tests/test_migrations 2.py +0 -137
  256. 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
- }