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