nexo-brain 5.3.19 → 5.3.21

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 (211) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/bin/nexo-brain.js +52 -10
  3. package/package.json +1 -1
  4. package/src/auto_update.py +11 -8
  5. package/src/dashboard/static/favicon 2.svg +32 -0
  6. package/src/dashboard/static/nexo-logo 2.png +0 -0
  7. package/src/dashboard/static/nexo-logo 2.svg +40 -0
  8. package/src/dashboard/static/style 2.css +2458 -0
  9. package/src/dashboard/templates/adaptive 2.html +118 -0
  10. package/src/dashboard/templates/artifacts 2.html +133 -0
  11. package/src/dashboard/templates/backups 2.html +136 -0
  12. package/src/dashboard/templates/base 2.html +417 -0
  13. package/src/dashboard/templates/calendar 2.html +591 -0
  14. package/src/dashboard/templates/chat 2.html +356 -0
  15. package/src/dashboard/templates/claims 2.html +259 -0
  16. package/src/dashboard/templates/cortex 2.html +321 -0
  17. package/src/dashboard/templates/credentials 2.html +128 -0
  18. package/src/dashboard/templates/crons 2.html +370 -0
  19. package/src/dashboard/templates/dashboard 2.html +494 -0
  20. package/src/dashboard/templates/dreams 2.html +252 -0
  21. package/src/dashboard/templates/email 2.html +160 -0
  22. package/src/dashboard/templates/evolution 2.html +189 -0
  23. package/src/dashboard/templates/feed 2.html +249 -0
  24. package/src/dashboard/templates/followup_health 2.html +170 -0
  25. package/src/dashboard/templates/graph 2.html +201 -0
  26. package/src/dashboard/templates/guard 2.html +259 -0
  27. package/src/dashboard/templates/inbox 2.html +251 -0
  28. package/src/dashboard/templates/memory 2.html +420 -0
  29. package/src/dashboard/templates/operations 2.html +608 -0
  30. package/src/dashboard/templates/plugins 2.html +185 -0
  31. package/src/dashboard/templates/protocol 2.html +199 -0
  32. package/src/dashboard/templates/rules 2.html +246 -0
  33. package/src/dashboard/templates/sentiment 2.html +247 -0
  34. package/src/dashboard/templates/sessions 2.html +218 -0
  35. package/src/dashboard/templates/skills 2.html +329 -0
  36. package/src/dashboard/templates/somatic 2.html +73 -0
  37. package/src/dashboard/templates/triggers 2.html +133 -0
  38. package/src/dashboard/templates/trust 2.html +360 -0
  39. package/src/db/__init__ 2.py +259 -0
  40. package/src/db/_core 2.py +437 -0
  41. package/src/db/_credentials 2.py +124 -0
  42. package/src/db/_episodic 2.py +762 -0
  43. package/src/db/_evolution 2.py +54 -0
  44. package/src/db/_fts 2.py +406 -0
  45. package/src/db/_goal_profiles 2.py +376 -0
  46. package/src/db/_hot_context 2.py +660 -0
  47. package/src/db/_outcomes 2.py +800 -0
  48. package/src/db/_personal_scripts 2.py +582 -0
  49. package/src/db/_sessions 2.py +330 -0
  50. package/src/db/_tasks 2.py +91 -0
  51. package/src/db/_watchers 2.py +173 -0
  52. package/src/doctor/formatters 2.py +52 -0
  53. package/src/doctor/models 2.py +69 -0
  54. package/src/doctor/planes 2.py +87 -0
  55. package/src/doctor/providers/__init__ 2.py +1 -0
  56. package/src/doctor/providers/deep 2.py +367 -0
  57. package/src/evolution_cycle 2.py +519 -0
  58. package/src/hooks/auto_capture 2.py +208 -0
  59. package/src/hooks/caffeinate-guard 2.sh +8 -0
  60. package/src/hooks/capture-session 2.sh +21 -0
  61. package/src/hooks/capture-tool-logs 2.sh +158 -0
  62. package/src/hooks/daily-briefing-check 2.sh +33 -0
  63. package/src/hooks/heartbeat-enforcement 2.py +90 -0
  64. package/src/hooks/heartbeat-posttool 2.sh +18 -0
  65. package/src/hooks/inbox-hook 2.sh +76 -0
  66. package/src/hooks/post-compact 2.sh +152 -0
  67. package/src/hooks/pre-compact 2.sh +169 -0
  68. package/src/hooks/protocol-guardrail 2.sh +10 -0
  69. package/src/hooks/protocol-pretool-guardrail 2.sh +9 -0
  70. package/src/hooks/session-stop 2.sh +52 -0
  71. package/src/kg_populate 2.py +292 -0
  72. package/src/maintenance 2.py +53 -0
  73. package/src/memory_backends 2.py +71 -0
  74. package/src/migrate_embeddings 2.py +124 -0
  75. package/src/nexo_sdk 2.py +103 -0
  76. package/src/observability 2.py +199 -0
  77. package/src/plugin_loader 2.py +217 -0
  78. package/src/plugins/__init__ 2.py +0 -0
  79. package/src/plugins/artifact_registry 2.py +450 -0
  80. package/src/plugins/backup 2.py +127 -0
  81. package/src/plugins/claims_tools 2.py +119 -0
  82. package/src/plugins/cognitive_memory 2.py +609 -0
  83. package/src/plugins/core_rules 2.py +252 -0
  84. package/src/plugins/cortex 2.py +1155 -0
  85. package/src/plugins/entities 2.py +67 -0
  86. package/src/plugins/episodic_memory 2.py +560 -0
  87. package/src/plugins/evolution 2.py +167 -0
  88. package/src/plugins/goal_engine 2.py +142 -0
  89. package/src/plugins/guard 2.py +862 -0
  90. package/src/plugins/impact 2.py +29 -0
  91. package/src/plugins/knowledge_graph_tools 2.py +137 -0
  92. package/src/plugins/media_memory_tools 2.py +98 -0
  93. package/src/plugins/memory_export 2.py +196 -0
  94. package/src/plugins/outcomes 2.py +130 -0
  95. package/src/plugins/personal_scripts 2.py +117 -0
  96. package/src/plugins/preferences 2.py +47 -0
  97. package/src/plugins/protocol 2.py +1449 -0
  98. package/src/plugins/simple_api 2.py +106 -0
  99. package/src/plugins/skills 2.py +341 -0
  100. package/src/plugins/state_watchers 2.py +79 -0
  101. package/src/plugins/update 2.py +986 -0
  102. package/src/plugins/user_state_tools 2.py +43 -0
  103. package/src/plugins/workflow 2.py +588 -0
  104. package/src/protocol_settings 2.py +59 -0
  105. package/src/public_contribution 2.py +466 -0
  106. package/src/public_evolution_queue 2.py +241 -0
  107. package/src/requirements 2.txt +14 -0
  108. package/src/retroactive_learnings 2.py +373 -0
  109. package/src/rules/__init__ 2.py +0 -0
  110. package/src/rules/core-rules 2.json +331 -0
  111. package/src/rules/migrate 2.py +207 -0
  112. package/src/runtime_power 2.py +874 -0
  113. package/src/script_registry 2.py +1559 -0
  114. package/src/scripts/check-context 2.py +272 -0
  115. package/src/scripts/deep-sleep/apply_findings 2.py +2327 -0
  116. package/src/scripts/deep-sleep/collect 2.py +928 -0
  117. package/src/scripts/deep-sleep/extract 2.py +330 -0
  118. package/src/scripts/deep-sleep/extract-prompt 2.md +285 -0
  119. package/src/scripts/deep-sleep/synthesize 2.py +312 -0
  120. package/src/scripts/deep-sleep/synthesize-prompt 2.md +336 -0
  121. package/src/scripts/nexo-agent-run 2.py +75 -0
  122. package/src/scripts/nexo-auto-update 2.py +6 -0
  123. package/src/scripts/nexo-backup 2.sh +25 -0
  124. package/src/scripts/nexo-brain-activation 2.sh +140 -0
  125. package/src/scripts/nexo-catchup 2.py +300 -0
  126. package/src/scripts/nexo-cognitive-decay 2.py +257 -0
  127. package/src/scripts/nexo-cortex-cycle 2.py +293 -0
  128. package/src/scripts/nexo-cron-wrapper 2.sh +53 -0
  129. package/src/scripts/nexo-daily-self-audit 2.py +2161 -0
  130. package/src/scripts/nexo-dashboard 2.sh +29 -0
  131. package/src/scripts/nexo-deep-sleep 2.sh +86 -0
  132. package/src/scripts/nexo-evolution-run 2.py +1664 -0
  133. package/src/scripts/nexo-followup-hygiene 2.py +139 -0
  134. package/src/scripts/nexo-hook-record 2.py +42 -0
  135. package/src/scripts/nexo-immune 2.py +936 -0
  136. package/src/scripts/nexo-impact-scorer 2.py +117 -0
  137. package/src/scripts/nexo-inbox-hook 2.sh +74 -0
  138. package/src/scripts/nexo-install 2.py +6 -0
  139. package/src/scripts/nexo-learning-housekeep 2.py +401 -0
  140. package/src/scripts/nexo-learning-validator 2.py +266 -0
  141. package/src/scripts/nexo-migrate 2.py +260 -0
  142. package/src/scripts/nexo-outcome-checker 2.py +127 -0
  143. package/src/scripts/nexo-postmortem-consolidator 2.py +456 -0
  144. package/src/scripts/nexo-pre-commit 2.py +120 -0
  145. package/src/scripts/nexo-prevent-sleep 2.sh +35 -0
  146. package/src/scripts/nexo-proactive-dashboard 2.py +354 -0
  147. package/src/scripts/nexo-reflection 2.py +256 -0
  148. package/src/scripts/nexo-runtime-preflight 2.py +274 -0
  149. package/src/scripts/nexo-sleep 2.py +631 -0
  150. package/src/scripts/nexo-snapshot-restore 2.sh +35 -0
  151. package/src/scripts/nexo-sync-clients 2.py +16 -0
  152. package/src/scripts/nexo-synthesis 2.py +475 -0
  153. package/src/scripts/nexo-tcc-approve 2.sh +79 -0
  154. package/src/scripts/nexo-update 2.sh +306 -0
  155. package/src/scripts/nexo-watchdog 2.sh +1207 -0
  156. package/src/scripts/nexo-watchdog-smoke 2.py +119 -0
  157. package/src/scripts/rehydrate_learnings_from_archive 2.py +245 -0
  158. package/src/server 2.py +1296 -0
  159. package/src/skills/run-nexo-audit-phase/guide 2.md +43 -0
  160. package/src/skills/run-nexo-audit-phase/skill 2.json +59 -0
  161. package/src/skills/run-nexo-core-fix-cycle/guide 2.md +17 -0
  162. package/src/skills/run-nexo-core-fix-cycle/script 2.py +276 -0
  163. package/src/skills/run-nexo-core-fix-cycle/skill 2.json +58 -0
  164. package/src/skills/run-release-final-audit/guide 2.md +16 -0
  165. package/src/skills/run-release-final-audit/script 2.py +259 -0
  166. package/src/skills/run-release-final-audit/skill 2.json +77 -0
  167. package/src/skills/run-runtime-doctor/guide 2.md +12 -0
  168. package/src/skills/run-runtime-doctor/script 2.py +21 -0
  169. package/src/skills/run-runtime-doctor/skill 2.json +25 -0
  170. package/src/skills_runtime 2.py +932 -0
  171. package/src/state_watchers_runtime 2.py +475 -0
  172. package/src/storage_router 2.py +32 -0
  173. package/src/system_catalog 2.py +786 -0
  174. package/src/tools_coordination 2.py +103 -0
  175. package/src/tools_credentials 2.py +68 -0
  176. package/src/tools_drive 2.py +487 -0
  177. package/src/tools_hot_context 2.py +163 -0
  178. package/src/tools_learnings 2.py +612 -0
  179. package/src/tools_menu 2.py +229 -0
  180. package/src/tools_reminders 2.py +88 -0
  181. package/src/tools_reminders_crud 2.py +363 -0
  182. package/src/tools_sessions 2.py +1054 -0
  183. package/src/tools_system_catalog 2.py +19 -0
  184. package/src/tools_task_history 2.py +57 -0
  185. package/src/tools_transcripts 2.py +98 -0
  186. package/src/transcript_utils 2.py +412 -0
  187. package/src/user_context 2.py +46 -0
  188. package/src/user_data_portability 2.py +328 -0
  189. package/src/user_state_model 2.py +170 -0
  190. package/templates/CLAUDE.md 2.template +108 -0
  191. package/templates/CODEX.AGENTS.md 2.template +66 -0
  192. package/templates/launchagents/README 2.md +132 -0
  193. package/templates/launchagents/com.nexo.auto-close-sessions 2.plist +39 -0
  194. package/templates/launchagents/com.nexo.catchup 2.plist +39 -0
  195. package/templates/launchagents/com.nexo.cognitive-decay 2.plist +40 -0
  196. package/templates/launchagents/com.nexo.dashboard 2.plist +43 -0
  197. package/templates/launchagents/com.nexo.deep-sleep 2.plist +43 -0
  198. package/templates/launchagents/com.nexo.evolution 2.plist +44 -0
  199. package/templates/launchagents/com.nexo.followup-hygiene 2.plist +45 -0
  200. package/templates/launchagents/com.nexo.immune 2.plist +41 -0
  201. package/templates/launchagents/com.nexo.postmortem 2.plist +45 -0
  202. package/templates/launchagents/com.nexo.self-audit 2.plist +47 -0
  203. package/templates/launchagents/com.nexo.synthesis 2.plist +45 -0
  204. package/templates/launchagents/com.nexo.watchdog 2.plist +37 -0
  205. package/templates/nexo_helper 2.py +301 -0
  206. package/templates/openclaw 2.json +13 -0
  207. package/templates/plugin-template 2.py +40 -0
  208. package/templates/script-template 2.py +59 -0
  209. package/templates/script-template 2.sh +13 -0
  210. package/templates/skill-script-template 2.py +48 -0
  211. package/templates/skill-template 2.md +33 -0
@@ -0,0 +1,360 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Trust Deep-Dive{% endblock %}
4
+ {% block page_title %}Trust Deep-Dive{% endblock %}
5
+
6
+ {% block head %}
7
+ <style>
8
+ .gauge-ring {
9
+ transition: stroke-dashoffset 1.2s cubic-bezier(0.22, 1, 0.36, 1);
10
+ }
11
+ .score-value {
12
+ transition: all 0.8s ease;
13
+ }
14
+ @keyframes pulseGlow {
15
+ 0%, 100% { filter: drop-shadow(0 0 8px rgba(124, 58, 237, 0.3)); }
16
+ 50% { filter: drop-shadow(0 0 20px rgba(124, 58, 237, 0.6)); }
17
+ }
18
+ .gauge-container {
19
+ animation: pulseGlow 3s ease-in-out infinite;
20
+ }
21
+ .event-card {
22
+ transition: all 0.2s ease;
23
+ animation: slideIn 0.3s ease-out forwards;
24
+ opacity: 0;
25
+ }
26
+ .event-card:hover {
27
+ border-color: rgba(124, 58, 237, 0.3);
28
+ transform: translateX(2px);
29
+ }
30
+ .chart-container {
31
+ position: relative;
32
+ }
33
+ .delta-positive { color: #34d399; }
34
+ .delta-negative { color: #f87171; }
35
+ .delta-badge-positive { background: rgba(52, 211, 153, 0.1); border-color: rgba(52, 211, 153, 0.2); color: #34d399; }
36
+ .delta-badge-negative { background: rgba(248, 113, 113, 0.1); border-color: rgba(248, 113, 113, 0.2); color: #f87171; }
37
+ @keyframes slideIn {
38
+ from { opacity: 0; transform: translateX(-8px); }
39
+ to { opacity: 1; transform: translateX(0); }
40
+ }
41
+ .filter-btn.active {
42
+ background: rgba(124, 58, 237, 0.2);
43
+ color: #a78bfa;
44
+ border-color: rgba(124, 58, 237, 0.3);
45
+ }
46
+ </style>
47
+ {% endblock %}
48
+
49
+ {% block header_actions %}
50
+ <div class="flex items-center gap-1">
51
+ <button class="filter-btn active text-[10px] px-2 py-1 rounded border border-slate-700 text-slate-400 hover:text-slate-200 transition-colors" data-filter="all" onclick="setFilter('all')">All</button>
52
+ <button class="filter-btn text-[10px] px-2 py-1 rounded border border-slate-700 text-slate-400 hover:text-slate-200 transition-colors" data-filter="positive" onclick="setFilter('positive')">Positive</button>
53
+ <button class="filter-btn text-[10px] px-2 py-1 rounded border border-slate-700 text-slate-400 hover:text-slate-200 transition-colors" data-filter="negative" onclick="setFilter('negative')">Negative</button>
54
+ </div>
55
+ {% endblock %}
56
+
57
+ {% block content %}
58
+ <!-- Gauge + Stats Row -->
59
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-5 mb-6">
60
+ <!-- Trust Gauge -->
61
+ <div class="bg-slate-900/50 border border-slate-800/50 rounded-xl p-6 flex flex-col items-center justify-center">
62
+ <div class="text-xs uppercase tracking-wider text-slate-500 font-semibold mb-4">Current Trust Score</div>
63
+ <div class="gauge-container relative" style="width: 200px; height: 200px;">
64
+ <svg viewBox="0 0 200 200" class="w-full h-full">
65
+ <!-- Background ring -->
66
+ <circle cx="100" cy="100" r="85" fill="none" stroke="rgba(51,65,85,0.3)" stroke-width="10"
67
+ stroke-dasharray="534" stroke-dashoffset="0" stroke-linecap="round"
68
+ transform="rotate(-90 100 100)"/>
69
+ <!-- Score ring -->
70
+ <circle id="gauge-ring" cx="100" cy="100" r="85" fill="none" stroke="url(#gaugeGradient)" stroke-width="10"
71
+ stroke-dasharray="534" stroke-dashoffset="534" stroke-linecap="round"
72
+ transform="rotate(-90 100 100)" class="gauge-ring"/>
73
+ <!-- Gradient -->
74
+ <defs>
75
+ <linearGradient id="gaugeGradient" x1="0%" y1="0%" x2="100%" y2="100%">
76
+ <stop offset="0%" stop-color="#7C3AED"/>
77
+ <stop offset="50%" stop-color="#a78bfa"/>
78
+ <stop offset="100%" stop-color="#34d399"/>
79
+ </linearGradient>
80
+ </defs>
81
+ </svg>
82
+ <div class="absolute inset-0 flex flex-col items-center justify-center">
83
+ <span id="gauge-score" class="text-5xl font-display font-bold text-white score-value">--</span>
84
+ <span class="text-xs text-slate-500 mt-1">/ 100</span>
85
+ </div>
86
+ </div>
87
+ </div>
88
+
89
+ <!-- Weekly Stats -->
90
+ <div class="bg-slate-900/50 border border-slate-800/50 rounded-xl p-5 flex flex-col justify-center">
91
+ <div class="text-xs uppercase tracking-wider text-slate-500 font-semibold mb-4">This Week</div>
92
+ <div class="space-y-4">
93
+ <div>
94
+ <div class="flex items-center justify-between mb-1">
95
+ <span class="text-xs text-slate-400">Average Score</span>
96
+ <span class="text-sm font-mono font-semibold text-slate-200" id="stat-avg">--</span>
97
+ </div>
98
+ <div class="h-1 bg-slate-800 rounded-full overflow-hidden">
99
+ <div class="h-full bg-violet-500 rounded-full transition-all duration-700" id="stat-avg-bar" style="width:0%"></div>
100
+ </div>
101
+ </div>
102
+ <div class="flex items-center justify-between">
103
+ <span class="text-xs text-slate-400">Biggest Gain</span>
104
+ <span class="text-sm font-mono font-semibold delta-positive" id="stat-gain">--</span>
105
+ </div>
106
+ <div class="flex items-center justify-between">
107
+ <span class="text-xs text-slate-400">Biggest Drop</span>
108
+ <span class="text-sm font-mono font-semibold delta-negative" id="stat-drop">--</span>
109
+ </div>
110
+ <div class="flex items-center justify-between">
111
+ <span class="text-xs text-slate-400">Total Events</span>
112
+ <span class="text-sm font-mono font-semibold text-slate-200" id="stat-events">--</span>
113
+ </div>
114
+ </div>
115
+ </div>
116
+
117
+ <!-- Score History Chart -->
118
+ <div class="bg-slate-900/50 border border-slate-800/50 rounded-xl p-5">
119
+ <div class="text-xs uppercase tracking-wider text-slate-500 font-semibold mb-3">Score History</div>
120
+ <div class="chart-container" style="height: 180px;">
121
+ <canvas id="history-chart" width="400" height="180"></canvas>
122
+ </div>
123
+ </div>
124
+ </div>
125
+
126
+ <!-- Event Timeline -->
127
+ <div>
128
+ <h2 class="text-xs uppercase tracking-wider text-slate-500 font-semibold mb-3 flex items-center gap-2">
129
+ <svg class="w-4 h-4 text-violet-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
130
+ Trust Events
131
+ <span class="text-slate-600 font-mono" id="event-count"></span>
132
+ </h2>
133
+ <div class="space-y-2" id="event-timeline">
134
+ <div class="text-xs text-slate-600 text-center py-8">Loading trust events...</div>
135
+ </div>
136
+ </div>
137
+ {% endblock %}
138
+
139
+ {% block scripts %}
140
+ <script>
141
+ const REFRESH_MS = 60000;
142
+ let allEvents = [];
143
+ let currentFilter = 'all';
144
+
145
+ function setFilter(filter) {
146
+ currentFilter = filter;
147
+ document.querySelectorAll('.filter-btn').forEach(b => {
148
+ b.classList.toggle('active', b.dataset.filter === filter);
149
+ });
150
+ renderEvents();
151
+ }
152
+
153
+ function updateGauge(score) {
154
+ const ring = document.getElementById('gauge-ring');
155
+ const scoreEl = document.getElementById('gauge-score');
156
+ const circumference = 534; // 2 * PI * 85
157
+ const offset = circumference - (score / 100) * circumference;
158
+ ring.style.strokeDashoffset = offset;
159
+ scoreEl.textContent = Math.round(score);
160
+ }
161
+
162
+ function drawChart(history) {
163
+ const canvas = document.getElementById('history-chart');
164
+ const ctx = canvas.getContext('2d');
165
+ const dpr = window.devicePixelRatio || 1;
166
+ const rect = canvas.getBoundingClientRect();
167
+ canvas.width = rect.width * dpr;
168
+ canvas.height = rect.height * dpr;
169
+ ctx.scale(dpr, dpr);
170
+ const w = rect.width;
171
+ const h = rect.height;
172
+
173
+ ctx.clearRect(0, 0, w, h);
174
+
175
+ if (!history || history.length < 2) {
176
+ ctx.fillStyle = '#475569';
177
+ ctx.font = '12px "Space Grotesk"';
178
+ ctx.textAlign = 'center';
179
+ ctx.fillText('Not enough data for chart', w / 2, h / 2);
180
+ return;
181
+ }
182
+
183
+ const scores = history.map(h => h.score);
184
+ const minScore = Math.max(0, Math.min(...scores) - 5);
185
+ const maxScore = Math.min(100, Math.max(...scores) + 5);
186
+ const range = maxScore - minScore || 1;
187
+
188
+ const padX = 10, padY = 15;
189
+ const chartW = w - padX * 2;
190
+ const chartH = h - padY * 2;
191
+
192
+ // Grid lines
193
+ ctx.strokeStyle = 'rgba(51, 65, 85, 0.3)';
194
+ ctx.lineWidth = 1;
195
+ for (let i = 0; i <= 4; i++) {
196
+ const y = padY + (chartH / 4) * i;
197
+ ctx.beginPath();
198
+ ctx.moveTo(padX, y);
199
+ ctx.lineTo(w - padX, y);
200
+ ctx.stroke();
201
+ }
202
+
203
+ // Gradient fill
204
+ const gradient = ctx.createLinearGradient(0, padY, 0, h - padY);
205
+ gradient.addColorStop(0, 'rgba(124, 58, 237, 0.15)');
206
+ gradient.addColorStop(1, 'rgba(124, 58, 237, 0)');
207
+
208
+ // Line path
209
+ const points = history.map((item, i) => ({
210
+ x: padX + (i / (history.length - 1)) * chartW,
211
+ y: padY + chartH - ((item.score - minScore) / range) * chartH
212
+ }));
213
+
214
+ // Fill area
215
+ ctx.beginPath();
216
+ ctx.moveTo(points[0].x, h - padY);
217
+ points.forEach(p => ctx.lineTo(p.x, p.y));
218
+ ctx.lineTo(points[points.length - 1].x, h - padY);
219
+ ctx.closePath();
220
+ ctx.fillStyle = gradient;
221
+ ctx.fill();
222
+
223
+ // Line
224
+ ctx.beginPath();
225
+ ctx.moveTo(points[0].x, points[0].y);
226
+ for (let i = 1; i < points.length; i++) {
227
+ const prev = points[i - 1];
228
+ const curr = points[i];
229
+ const cpx = (prev.x + curr.x) / 2;
230
+ ctx.bezierCurveTo(cpx, prev.y, cpx, curr.y, curr.x, curr.y);
231
+ }
232
+ ctx.strokeStyle = '#7C3AED';
233
+ ctx.lineWidth = 2;
234
+ ctx.stroke();
235
+
236
+ // Dots
237
+ points.forEach((p, i) => {
238
+ if (i === points.length - 1 || history.length <= 20 || i % Math.ceil(history.length / 15) === 0) {
239
+ ctx.beginPath();
240
+ ctx.arc(p.x, p.y, 3, 0, Math.PI * 2);
241
+ ctx.fillStyle = '#7C3AED';
242
+ ctx.fill();
243
+ ctx.strokeStyle = '#030712';
244
+ ctx.lineWidth = 1.5;
245
+ ctx.stroke();
246
+ }
247
+ });
248
+
249
+ // Last point highlight
250
+ const last = points[points.length - 1];
251
+ ctx.beginPath();
252
+ ctx.arc(last.x, last.y, 5, 0, Math.PI * 2);
253
+ ctx.fillStyle = '#a78bfa';
254
+ ctx.fill();
255
+ ctx.strokeStyle = '#030712';
256
+ ctx.lineWidth = 2;
257
+ ctx.stroke();
258
+ }
259
+
260
+ function renderEvents() {
261
+ const container = document.getElementById('event-timeline');
262
+ const countEl = document.getElementById('event-count');
263
+
264
+ let filtered = allEvents;
265
+ if (currentFilter === 'positive') filtered = allEvents.filter(e => (e.delta || 0) > 0);
266
+ else if (currentFilter === 'negative') filtered = allEvents.filter(e => (e.delta || 0) < 0);
267
+
268
+ countEl.textContent = `(${filtered.length})`;
269
+
270
+ if (filtered.length === 0) {
271
+ container.innerHTML = '<div class="text-xs text-slate-600 text-center py-8">No events match filter</div>';
272
+ return;
273
+ }
274
+
275
+ container.innerHTML = filtered.slice(0, 50).map((e, idx) => {
276
+ const delta = e.delta || 0;
277
+ const isPositive = delta >= 0;
278
+ const deltaStr = (isPositive ? '+' : '') + delta.toFixed(2);
279
+ const badgeClass = isPositive ? 'delta-badge-positive' : 'delta-badge-negative';
280
+ const event = e.event || e.description || 'Trust event';
281
+ const context = e.context || '';
282
+ const when = e.created_at ? relativeTime(e.created_at) : '';
283
+ const score = e.score != null ? e.score.toFixed(1) : '';
284
+ const delay = Math.min(idx * 0.05, 1);
285
+
286
+ return `<div class="event-card flex items-start gap-3 bg-slate-900/50 border border-slate-800/50 rounded-xl p-4" style="animation-delay:${delay}s">
287
+ <div class="flex-shrink-0 mt-0.5">
288
+ <span class="inline-flex items-center justify-center w-8 h-8 rounded-lg border text-xs font-mono font-bold ${badgeClass}">
289
+ ${deltaStr}
290
+ </span>
291
+ </div>
292
+ <div class="flex-1 min-w-0">
293
+ <div class="flex items-center gap-2 mb-1">
294
+ <span class="text-sm text-slate-200 font-medium">${escapeHtml(event)}</span>
295
+ ${score ? `<span class="text-[10px] font-mono text-slate-500 ml-auto flex-shrink-0">${score}</span>` : ''}
296
+ </div>
297
+ ${context ? `<p class="text-xs text-slate-500 leading-relaxed">${escapeHtml(context)}</p>` : ''}
298
+ <span class="text-[10px] text-slate-600 font-mono mt-1 inline-block">${escapeHtml(when)}</span>
299
+ </div>
300
+ </div>`;
301
+ }).join('');
302
+ }
303
+
304
+ async function loadTrust() {
305
+ const [trustData, eventsData] = await Promise.all([
306
+ fetchJSON('/api/trust'),
307
+ fetchJSON('/api/trust/events?limit=50')
308
+ ]);
309
+
310
+ if (trustData) {
311
+ const score = trustData.current_score != null ? trustData.current_score : 0;
312
+ updateGauge(score);
313
+
314
+ const history = trustData.history || [];
315
+ drawChart(history);
316
+
317
+ // Weekly stats
318
+ const now = new Date();
319
+ const weekAgo = new Date(now - 7 * 86400000);
320
+ const weekEvents = history.filter(h => new Date(h.created_at) >= weekAgo);
321
+ const weekScores = weekEvents.map(h => h.score).filter(s => s != null);
322
+ const weekDeltas = weekEvents.map(h => h.delta).filter(d => d != null);
323
+
324
+ if (weekScores.length > 0) {
325
+ const avg = weekScores.reduce((a, b) => a + b, 0) / weekScores.length;
326
+ document.getElementById('stat-avg').textContent = avg.toFixed(1);
327
+ document.getElementById('stat-avg-bar').style.width = avg + '%';
328
+ }
329
+
330
+ if (weekDeltas.length > 0) {
331
+ const maxGain = Math.max(...weekDeltas);
332
+ const maxDrop = Math.min(...weekDeltas);
333
+ document.getElementById('stat-gain').textContent = maxGain > 0 ? '+' + maxGain.toFixed(2) : '0';
334
+ document.getElementById('stat-drop').textContent = maxDrop < 0 ? maxDrop.toFixed(2) : '0';
335
+ }
336
+
337
+ document.getElementById('stat-events').textContent = formatNumber(history.length);
338
+ }
339
+
340
+ if (eventsData) {
341
+ allEvents = eventsData.events || eventsData || [];
342
+ renderEvents();
343
+ }
344
+ }
345
+
346
+ // Handle resize for chart
347
+ let resizeTimer;
348
+ window.addEventListener('resize', () => {
349
+ clearTimeout(resizeTimer);
350
+ resizeTimer = setTimeout(() => {
351
+ // Re-fetch to redraw chart
352
+ loadTrust();
353
+ }, 250);
354
+ });
355
+
356
+ // Init
357
+ loadTrust();
358
+ setInterval(loadTrust, REFRESH_MS);
359
+ </script>
360
+ {% endblock %}
@@ -0,0 +1,259 @@
1
+ """NEXO DB — Modular SQLite database layer.
2
+
3
+ This package replaces the monolithic db.py. All public functions are
4
+ re-exported here for full backwards compatibility:
5
+ from db import get_db, create_learning, ...
6
+
7
+ Important:
8
+ `importlib.reload(db)` must also refresh the concrete submodules. The test
9
+ suite and several runtime repair flows rely on switching database paths or
10
+ runtime roots mid-process. If the package only re-exported functions from
11
+ already-imported submodules, those callables would keep pointing at stale
12
+ module state (especially old `db._core` connection globals).
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import importlib
18
+ import sys
19
+
20
+
21
+ def _load_submodule(name: str):
22
+ """Import or reload a db submodule and expose it on the package."""
23
+ module = sys.modules.get(name)
24
+ if module is None:
25
+ return importlib.import_module(name)
26
+ return importlib.reload(module)
27
+
28
+
29
+ def _module(name: str):
30
+ module = sys.modules.get(name)
31
+ if module is None:
32
+ module = importlib.import_module(name)
33
+ return module
34
+
35
+
36
+ _core = _load_submodule("db._core")
37
+ _fts = _load_submodule("db._fts")
38
+ _schema = _load_submodule("db._schema")
39
+ _sessions = _load_submodule("db._sessions")
40
+ _reminders = _load_submodule("db._reminders")
41
+ _learnings = _load_submodule("db._learnings")
42
+ _credentials = _load_submodule("db._credentials")
43
+ _tasks = _load_submodule("db._tasks")
44
+ _entities = _load_submodule("db._entities")
45
+ _episodic = _load_submodule("db._episodic")
46
+ _evolution = _load_submodule("db._evolution")
47
+ _cron_runs = _load_submodule("db._cron_runs")
48
+ _protocol = _load_submodule("db._protocol")
49
+ _workflow = _load_submodule("db._workflow")
50
+ _watchers = _load_submodule("db._watchers")
51
+ _personal_scripts = _load_submodule("db._personal_scripts")
52
+ _skills = _load_submodule("db._skills")
53
+ _hot_context = _load_submodule("db._hot_context")
54
+ _drive = _load_submodule("db._drive")
55
+ _outcomes = _load_submodule("db._outcomes")
56
+ _goal_profiles = _load_submodule("db._goal_profiles")
57
+
58
+ # Core: connection, constants, init, utils
59
+ from db._core import (
60
+ DB_PATH, SESSION_STALE_SECONDS, MESSAGE_TTL_SECONDS, QUESTION_TTL_SECONDS,
61
+ get_db, close_db, _get_raw_conn, _SerializedConnection,
62
+ _shared_conn, _write_lock,
63
+ init_db, _gen_id, now_epoch, local_time_str, _multi_word_like,
64
+ )
65
+
66
+ # FTS5 search
67
+ from db._fts import (
68
+ fts_add_dir, fts_remove_dir, fts_list_dirs,
69
+ rebuild_fts_index, fts_search, fts_upsert,
70
+ )
71
+
72
+ # Schema migrations
73
+ from db._schema import (
74
+ run_migrations, get_schema_version,
75
+ )
76
+
77
+ # Sessions, file tracking, messages, questions
78
+ from db._sessions import (
79
+ register_session, update_session, complete_session,
80
+ get_active_sessions, clean_stale_sessions, search_sessions,
81
+ track_files, untrack_files, get_all_tracked_files,
82
+ send_message, get_inbox,
83
+ ask_question, answer_question, get_pending_questions, check_answer,
84
+ )
85
+
86
+ # Reminders and followups
87
+ from db._reminders import (
88
+ create_reminder, update_reminder, complete_reminder, delete_reminder,
89
+ restore_reminder, add_reminder_note, get_reminders, get_reminder, get_reminder_history,
90
+ create_followup, update_followup, complete_followup, delete_followup,
91
+ restore_followup, add_followup_note, get_followups, get_followup, get_followup_history,
92
+ find_similar_followups,
93
+ compute_followup_impact, score_followup, score_active_followups,
94
+ add_item_history, get_item_history, validate_item_read_token,
95
+ )
96
+
97
+ # Learnings
98
+ from db._learnings import (
99
+ create_learning, update_learning, supersede_learning, delete_learning,
100
+ search_learnings, list_learnings,
101
+ extract_keywords, find_similar_learnings,
102
+ )
103
+
104
+ # Credentials
105
+ from db._credentials import (
106
+ create_credential, update_credential, delete_credential,
107
+ get_credential, list_credentials,
108
+ )
109
+
110
+ # Task history
111
+ from db._tasks import (
112
+ log_task, list_task_history, set_task_frequency,
113
+ get_overdue_tasks, get_task_frequencies,
114
+ )
115
+
116
+ # Entities, preferences, agents
117
+ from db._entities import (
118
+ create_entity, search_entities, list_entities, update_entity, delete_entity,
119
+ set_preference, get_preference, list_preferences, delete_preference,
120
+ create_agent, get_agent, list_agents, update_agent, delete_agent,
121
+ )
122
+
123
+ # Episodic memory
124
+ from db._episodic import (
125
+ cleanup_old_changes, log_change, search_changes, update_change_commit, auto_resolve_followups,
126
+ cleanup_old_decisions, log_decision, update_decision_outcome,
127
+ get_memory_review_queue, find_decisions_by_context_ref, search_decisions,
128
+ cleanup_old_diaries, write_session_diary,
129
+ diary_archive_search, diary_archive_read, diary_archive_stats,
130
+ check_session_has_diary,
131
+ upsert_diary_draft, get_diary_draft, delete_diary_draft,
132
+ save_checkpoint, read_checkpoint, increment_compaction_count,
133
+ get_orphan_sessions, read_session_diary,
134
+ recall,
135
+ )
136
+
137
+ # Evolution
138
+ from db._evolution import (
139
+ insert_evolution_metric, get_latest_metrics,
140
+ insert_evolution_log, get_evolution_history, update_evolution_log_status,
141
+ )
142
+
143
+ # Cron execution history
144
+ from db._cron_runs import (
145
+ cron_run_start, cron_run_end, cron_runs_recent, cron_runs_summary,
146
+ )
147
+
148
+ # Protocol discipline runtime
149
+ from db._protocol import (
150
+ VALID_IMPACT_LEVELS,
151
+ VALID_TASK_TYPES,
152
+ VALID_CLOSE_OUTCOMES,
153
+ create_protocol_task, get_protocol_task, close_protocol_task,
154
+ create_protocol_debt, resolve_protocol_debts, list_protocol_debts,
155
+ protocol_compliance_summary,
156
+ create_cortex_evaluation, get_cortex_evaluation, list_cortex_evaluations,
157
+ cortex_evaluation_summary,
158
+ latest_cortex_evaluation_for_task, task_has_cortex_evaluation,
159
+ override_cortex_evaluation, validate_close_outcome, validate_impact_level, validate_task_type,
160
+ )
161
+
162
+ # Durable workflow runtime
163
+ from db._workflow import (
164
+ create_workflow_run, get_workflow_run, list_workflow_runs,
165
+ list_workflow_steps, record_workflow_transition,
166
+ get_workflow_replay, get_workflow_resume_state,
167
+ create_workflow_goal, get_workflow_goal, list_workflow_goals,
168
+ update_workflow_goal,
169
+ )
170
+
171
+ # State watchers
172
+ from db._watchers import (
173
+ create_state_watcher, get_state_watcher, list_state_watchers,
174
+ update_state_watcher, update_state_watcher_result,
175
+ )
176
+
177
+ # Personal scripts registry
178
+ from db._personal_scripts import (
179
+ upsert_personal_script, list_personal_scripts, get_personal_script,
180
+ delete_missing_personal_scripts, register_personal_script_schedule,
181
+ delete_missing_personal_schedules, list_personal_script_schedules,
182
+ get_personal_script_schedule, delete_personal_script_schedule,
183
+ delete_personal_script,
184
+ record_personal_script_run, sync_personal_scripts_registry,
185
+ get_personal_script_health_report,
186
+ )
187
+
188
+ # Skills
189
+ from db._skills import (
190
+ create_skill, get_skill, list_skills, search_skills,
191
+ update_skill, delete_skill,
192
+ record_usage as record_skill_usage,
193
+ match_skills, merge_skills, get_skill_stats, decay_unused_skills,
194
+ get_featured_skills, get_skill_execution_spec, resolve_skill_paths,
195
+ validate_skill_params, render_command_template, sync_skill_directories,
196
+ import_skill_from_directory, approve_skill, collect_scriptable_skill_candidates,
197
+ collect_skill_improvement_candidates, materialize_personal_skill_definition,
198
+ get_skill_outcome_evidence, list_skill_outcome_reviews,
199
+ get_skill_health_report,
200
+ )
201
+
202
+ # Drive / Curiosity signals
203
+ from db._drive import (
204
+ create_drive_signal, reinforce_drive_signal, get_drive_signals,
205
+ get_drive_signal, update_drive_signal_status, decay_drive_signals,
206
+ find_similar_drive_signal, drive_signal_stats,
207
+ )
208
+
209
+ # Hot context / recent continuity
210
+ from db._hot_context import (
211
+ DEFAULT_CONTEXT_TTL_HOURS,
212
+ derive_context_key, clamp_ttl_hours,
213
+ cleanup_expired_hot_context,
214
+ remember_hot_context, record_recent_event, capture_context_event,
215
+ get_hot_context, search_hot_context, search_recent_events,
216
+ build_pre_action_context, format_pre_action_context_bundle,
217
+ resolve_hot_context,
218
+ )
219
+
220
+ # Outcomes
221
+ from db._outcomes import (
222
+ VALID_METRIC_SOURCES as OUTCOME_METRIC_SOURCES,
223
+ VALID_TARGET_OPERATORS as OUTCOME_TARGET_OPERATORS,
224
+ create_outcome, get_outcome, list_outcomes,
225
+ cancel_outcome, evaluate_outcome, pending_outcomes_due,
226
+ find_pending_outcomes_by_action, set_linked_outcomes_met,
227
+ list_outcome_pattern_candidates, capture_outcome_pattern,
228
+ )
229
+
230
+ # Goal Engine v1
231
+ from db._goal_profiles import (
232
+ DEFAULT_GOAL_PROFILES,
233
+ ensure_default_goal_profiles, get_goal_profile, list_goal_profiles,
234
+ upsert_goal_profile, resolve_goal_profile,
235
+ )
236
+
237
+
238
+ def get_db():
239
+ return _module("db._core").get_db()
240
+
241
+
242
+ def close_db():
243
+ return _module("db._core").close_db()
244
+
245
+
246
+ def init_db():
247
+ return _module("db._core").init_db()
248
+
249
+
250
+ def now_epoch():
251
+ return _module("db._core").now_epoch()
252
+
253
+
254
+ def run_migrations():
255
+ return _module("db._schema").run_migrations()
256
+
257
+
258
+ def get_schema_version():
259
+ return _module("db._schema").get_schema_version()