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
|
@@ -43,7 +43,27 @@ RUNTIME_PREFLIGHT_SUMMARY = LOG_DIR / "runtime-preflight-summary.json"
|
|
|
43
43
|
WATCHDOG_SMOKE_SUMMARY = LOG_DIR / "watchdog-smoke-summary.json"
|
|
44
44
|
RESTORE_LOG = LOG_DIR / "snapshot-restores.log"
|
|
45
45
|
CORTEX_LOG_DIR = NEXO_HOME / "brain" / "logs"
|
|
46
|
-
|
|
46
|
+
def _resolve_claude_cli() -> Path:
|
|
47
|
+
"""Find claude CLI: saved path > PATH > common locations."""
|
|
48
|
+
import shutil as _shutil
|
|
49
|
+
saved = NEXO_HOME / "config" / "claude-cli-path"
|
|
50
|
+
if saved.exists():
|
|
51
|
+
p = Path(saved.read_text().strip())
|
|
52
|
+
if p.exists():
|
|
53
|
+
return p
|
|
54
|
+
found = _shutil.which("claude")
|
|
55
|
+
if found:
|
|
56
|
+
return Path(found)
|
|
57
|
+
for candidate in [
|
|
58
|
+
Path.home() / ".local" / "bin" / "claude",
|
|
59
|
+
Path.home() / ".npm-global" / "bin" / "claude",
|
|
60
|
+
Path("/usr/local/bin/claude"),
|
|
61
|
+
]:
|
|
62
|
+
if candidate.exists():
|
|
63
|
+
return candidate
|
|
64
|
+
return Path.home() / ".local" / "bin" / "claude"
|
|
65
|
+
|
|
66
|
+
CLAUDE_CLI = _resolve_claude_cli()
|
|
47
67
|
|
|
48
68
|
findings = []
|
|
49
69
|
|
|
@@ -64,7 +64,27 @@ IMMUTABLE_FILES = {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
# ── Claude CLI path ──────────────────────────────────────────────────────
|
|
67
|
-
|
|
67
|
+
def _resolve_claude_cli() -> Path:
|
|
68
|
+
"""Find claude CLI: saved path > PATH > common locations."""
|
|
69
|
+
import shutil as _shutil
|
|
70
|
+
saved = NEXO_HOME / "config" / "claude-cli-path"
|
|
71
|
+
if saved.exists():
|
|
72
|
+
p = Path(saved.read_text().strip())
|
|
73
|
+
if p.exists():
|
|
74
|
+
return p
|
|
75
|
+
found = _shutil.which("claude")
|
|
76
|
+
if found:
|
|
77
|
+
return Path(found)
|
|
78
|
+
for candidate in [
|
|
79
|
+
Path.home() / ".local" / "bin" / "claude",
|
|
80
|
+
Path.home() / ".npm-global" / "bin" / "claude",
|
|
81
|
+
Path("/usr/local/bin/claude"),
|
|
82
|
+
]:
|
|
83
|
+
if candidate.exists():
|
|
84
|
+
return candidate
|
|
85
|
+
return Path.home() / ".local" / "bin" / "claude"
|
|
86
|
+
|
|
87
|
+
CLAUDE_CLI = _resolve_claude_cli()
|
|
68
88
|
|
|
69
89
|
# ── Logging ──────────────────────────────────────────────────────────────
|
|
70
90
|
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
@@ -42,7 +42,27 @@ MEMORY_DIR = NEXO_HOME / "memory"
|
|
|
42
42
|
MEMORY_INDEX = MEMORY_DIR / "MEMORY.md"
|
|
43
43
|
HISTORY_FILE = NEXO_HOME / "coordination" / "postmortem-history.json"
|
|
44
44
|
CONSOLIDATION_LOG = NEXO_HOME / "logs" / "postmortem-consolidation.log"
|
|
45
|
-
|
|
45
|
+
def _resolve_claude_cli() -> Path:
|
|
46
|
+
"""Find claude CLI: saved path > PATH > common locations."""
|
|
47
|
+
import shutil as _shutil
|
|
48
|
+
saved = NEXO_HOME / "config" / "claude-cli-path"
|
|
49
|
+
if saved.exists():
|
|
50
|
+
p = Path(saved.read_text().strip())
|
|
51
|
+
if p.exists():
|
|
52
|
+
return p
|
|
53
|
+
found = _shutil.which("claude")
|
|
54
|
+
if found:
|
|
55
|
+
return Path(found)
|
|
56
|
+
for candidate in [
|
|
57
|
+
HOME / ".local" / "bin" / "claude",
|
|
58
|
+
HOME / ".npm-global" / "bin" / "claude",
|
|
59
|
+
Path("/usr/local/bin/claude"),
|
|
60
|
+
]:
|
|
61
|
+
if candidate.exists():
|
|
62
|
+
return candidate
|
|
63
|
+
return HOME / ".local" / "bin" / "claude"
|
|
64
|
+
|
|
65
|
+
CLAUDE_CLI = _resolve_claude_cli()
|
|
46
66
|
SESSION_BUFFER = NEXO_HOME / "brain" / "session_buffer.jsonl"
|
|
47
67
|
|
|
48
68
|
TODAY = date.today()
|
|
@@ -379,6 +399,7 @@ def main():
|
|
|
379
399
|
return
|
|
380
400
|
|
|
381
401
|
log("=== NEXO Post-Mortem Consolidator v2 starting ===")
|
|
402
|
+
had_errors = False
|
|
382
403
|
|
|
383
404
|
# Stage 1: Collect data
|
|
384
405
|
data = collect_data()
|
|
@@ -392,27 +413,31 @@ def main():
|
|
|
392
413
|
if not success:
|
|
393
414
|
log("Stage 2 failed (CLI unavailable or error). "
|
|
394
415
|
"Skipping intelligent consolidation. Stage 3 (sensory + force) will still run.")
|
|
416
|
+
had_errors = True
|
|
395
417
|
|
|
396
418
|
# Stage 3: Sensory Register (mechanical, kept from v1)
|
|
397
419
|
try:
|
|
398
420
|
process_sensory_register()
|
|
399
421
|
except Exception as e:
|
|
400
422
|
log(f"Sensory register failed: {e}")
|
|
423
|
+
had_errors = True
|
|
401
424
|
|
|
402
425
|
# Stage 3b: Force analysis (mechanical, kept from v1)
|
|
403
426
|
try:
|
|
404
427
|
analyze_force_events()
|
|
405
428
|
except Exception as e:
|
|
406
429
|
log(f"Force analysis failed: {e}")
|
|
430
|
+
had_errors = True
|
|
407
431
|
|
|
408
|
-
# Register successful run
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
432
|
+
# Register successful run only if no stages failed
|
|
433
|
+
if not had_errors:
|
|
434
|
+
try:
|
|
435
|
+
state_file = NEXO_HOME / "operations" / ".catchup-state.json"
|
|
436
|
+
state = json.loads(state_file.read_text()) if state_file.exists() else {}
|
|
437
|
+
state["postmortem"] = datetime.now().isoformat()
|
|
438
|
+
state_file.write_text(json.dumps(state, indent=2))
|
|
439
|
+
except Exception:
|
|
440
|
+
pass
|
|
416
441
|
|
|
417
442
|
mark_done()
|
|
418
443
|
log("=== Consolidation v2 complete ===")
|
|
@@ -49,7 +49,26 @@ SLEEP_LOG = COORD_DIR / "sleep-log.json"
|
|
|
49
49
|
MEMORY_MD = NEXO_HOME / "memory" / "MEMORY.md"
|
|
50
50
|
NEXO_DB = NEXO_HOME / "data" / "nexo.db"
|
|
51
51
|
CLAUDE_MEM_DB = Path.home() / ".claude-mem" / "claude-mem.db"
|
|
52
|
-
|
|
52
|
+
def _resolve_claude_cli() -> Path:
|
|
53
|
+
"""Find claude CLI: saved path > PATH > common locations."""
|
|
54
|
+
saved = NEXO_HOME / "config" / "claude-cli-path"
|
|
55
|
+
if saved.exists():
|
|
56
|
+
p = Path(saved.read_text().strip())
|
|
57
|
+
if p.exists():
|
|
58
|
+
return p
|
|
59
|
+
found = shutil.which("claude")
|
|
60
|
+
if found:
|
|
61
|
+
return Path(found)
|
|
62
|
+
for candidate in [
|
|
63
|
+
Path.home() / ".local" / "bin" / "claude",
|
|
64
|
+
Path.home() / ".npm-global" / "bin" / "claude",
|
|
65
|
+
Path("/usr/local/bin/claude"),
|
|
66
|
+
]:
|
|
67
|
+
if candidate.exists():
|
|
68
|
+
return candidate
|
|
69
|
+
return Path.home() / ".local" / "bin" / "claude"
|
|
70
|
+
|
|
71
|
+
CLAUDE_CLI = _resolve_claude_cli()
|
|
53
72
|
|
|
54
73
|
LAST_RUN_FILE = COORD_DIR / "sleep-last-run"
|
|
55
74
|
LOCK_FILE = COORD_DIR / "sleep.lock"
|
|
@@ -534,6 +553,7 @@ def main():
|
|
|
534
553
|
|
|
535
554
|
run_log = {"date": str(TODAY), "started": TIMESTAMP,
|
|
536
555
|
"stage_a": None, "stage_b": None, "completed": None}
|
|
556
|
+
sleep_had_errors = False
|
|
537
557
|
|
|
538
558
|
# Stage A: Housekeeping (mechanical)
|
|
539
559
|
if start_phase == "stage_a":
|
|
@@ -555,7 +575,8 @@ def main():
|
|
|
555
575
|
|
|
556
576
|
if "error" in dream_result:
|
|
557
577
|
log(f"Stage B: Dreaming failed ({dream_result['error']}). "
|
|
558
|
-
"Stage A cleanup completed successfully.
|
|
578
|
+
"Stage A cleanup completed successfully. Not marking catchup to allow retry.")
|
|
579
|
+
sleep_had_errors = True
|
|
559
580
|
else:
|
|
560
581
|
# Stage B2: Execute actions from CLI output
|
|
561
582
|
actions_file = COORD_DIR / "sleep-actions.json"
|
|
@@ -575,14 +596,15 @@ def main():
|
|
|
575
596
|
append_sleep_log(run_log)
|
|
576
597
|
log(f"NEXO Sleep v2 complete at {run_log['completed']}")
|
|
577
598
|
|
|
578
|
-
# Register for catch-up
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
599
|
+
# Register for catch-up only if all stages succeeded
|
|
600
|
+
if not sleep_had_errors:
|
|
601
|
+
try:
|
|
602
|
+
state_file = NEXO_HOME / "operations" / ".catchup-state.json"
|
|
603
|
+
st = json.loads(state_file.read_text()) if state_file.exists() else {}
|
|
604
|
+
st["sleep"] = datetime.now().isoformat()
|
|
605
|
+
state_file.write_text(json.dumps(st, indent=2))
|
|
606
|
+
except Exception:
|
|
607
|
+
pass
|
|
586
608
|
|
|
587
609
|
finally:
|
|
588
610
|
try:
|
|
@@ -26,7 +26,27 @@ NEXO_DB = NEXO_HOME / "data" / "nexo.db"
|
|
|
26
26
|
OUTPUT_FILE = COORD_DIR / "daily-synthesis.md"
|
|
27
27
|
LAST_RUN_FILE = COORD_DIR / "synthesis-last-run"
|
|
28
28
|
LOCK_FILE = COORD_DIR / "synthesis.lock"
|
|
29
|
-
|
|
29
|
+
def _resolve_claude_cli() -> Path:
|
|
30
|
+
"""Find claude CLI: saved path > PATH > common locations."""
|
|
31
|
+
import shutil as _shutil
|
|
32
|
+
saved = NEXO_HOME / "config" / "claude-cli-path"
|
|
33
|
+
if saved.exists():
|
|
34
|
+
p = Path(saved.read_text().strip())
|
|
35
|
+
if p.exists():
|
|
36
|
+
return p
|
|
37
|
+
found = _shutil.which("claude")
|
|
38
|
+
if found:
|
|
39
|
+
return Path(found)
|
|
40
|
+
for candidate in [
|
|
41
|
+
HOME / ".local" / "bin" / "claude",
|
|
42
|
+
HOME / ".npm-global" / "bin" / "claude",
|
|
43
|
+
Path("/usr/local/bin/claude"),
|
|
44
|
+
]:
|
|
45
|
+
if candidate.exists():
|
|
46
|
+
return candidate
|
|
47
|
+
return HOME / ".local" / "bin" / "claude"
|
|
48
|
+
|
|
49
|
+
CLAUDE_CLI = _resolve_claude_cli()
|
|
30
50
|
|
|
31
51
|
TODAY = date.today()
|
|
32
52
|
TODAY_STR = TODAY.isoformat()
|
|
@@ -109,17 +129,17 @@ def collect_data() -> dict:
|
|
|
109
129
|
(TODAY_STR,)
|
|
110
130
|
)
|
|
111
131
|
|
|
112
|
-
# Overdue reminders
|
|
132
|
+
# Overdue reminders (schema: description, date, status uppercase)
|
|
113
133
|
data["overdue_reminders"] = safe_query(
|
|
114
|
-
"SELECT id,
|
|
115
|
-
"WHERE status='PENDING' AND
|
|
134
|
+
"SELECT id, description, date FROM reminders "
|
|
135
|
+
"WHERE status='PENDING' AND date <= ? ORDER BY date",
|
|
116
136
|
(TODAY_STR,)
|
|
117
137
|
)
|
|
118
138
|
|
|
119
|
-
# Pending followups
|
|
139
|
+
# Pending followups (schema: description, date, status uppercase)
|
|
120
140
|
data["pending_followups"] = safe_query(
|
|
121
|
-
"SELECT id,
|
|
122
|
-
"WHERE status='
|
|
141
|
+
"SELECT id, description, date FROM followups "
|
|
142
|
+
"WHERE status='PENDING' ORDER BY date"
|
|
123
143
|
)
|
|
124
144
|
|
|
125
145
|
# Guard stats
|
|
@@ -240,13 +260,13 @@ def fallback_synthesis(data: dict):
|
|
|
240
260
|
if data.get("overdue_reminders"):
|
|
241
261
|
lines.append("## Overdue Reminders")
|
|
242
262
|
for r in data["overdue_reminders"][:10]:
|
|
243
|
-
lines.append(f"- #{r.get('id', '?')} {r.get('
|
|
263
|
+
lines.append(f"- #{r.get('id', '?')} {r.get('description', '')} (due {r.get('date', '?')})")
|
|
244
264
|
lines.append("")
|
|
245
265
|
|
|
246
266
|
if data.get("pending_followups"):
|
|
247
267
|
lines.append("## Pending Followups")
|
|
248
268
|
for f in data["pending_followups"][:10]:
|
|
249
|
-
lines.append(f"- #{f.get('id', '?')} {f.get('
|
|
269
|
+
lines.append(f"- #{f.get('id', '?')} {f.get('description', '')} (due {f.get('date', '?')})")
|
|
250
270
|
lines.append("")
|
|
251
271
|
|
|
252
272
|
OUTPUT_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -34,13 +34,20 @@ read_version() {
|
|
|
34
34
|
python3 -c "import json; print(json.load(open('$PACKAGE_JSON')).get('version','unknown'))" 2>/dev/null || echo "unknown"
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
# ---
|
|
38
|
-
|
|
37
|
+
# --- Check if this is a git repo ---
|
|
38
|
+
if [ ! -d "$REPO_DIR/.git" ] && [ ! -f "$REPO_DIR/.git" ]; then
|
|
39
|
+
err "ABORTED: Not a git repository at $REPO_DIR"
|
|
40
|
+
err "For packaged installs, use: npm update -g nexo-brain"
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# --- Step 1: Check for uncommitted changes in entire worktree ---
|
|
45
|
+
log "Checking for uncommitted changes..."
|
|
39
46
|
cd "$REPO_DIR"
|
|
40
47
|
|
|
41
|
-
if [ -n "$(git status --porcelain
|
|
42
|
-
err "ABORTED: Uncommitted changes in
|
|
43
|
-
git status --short
|
|
48
|
+
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
|
|
49
|
+
err "ABORTED: Uncommitted changes in worktree"
|
|
50
|
+
git status --short
|
|
44
51
|
exit 1
|
|
45
52
|
fi
|
|
46
53
|
log "Working tree clean."
|
|
@@ -48,6 +55,11 @@ log "Working tree clean."
|
|
|
48
55
|
# Record current state
|
|
49
56
|
OLD_VERSION="$(read_version)"
|
|
50
57
|
OLD_COMMIT="$(git rev-parse HEAD)"
|
|
58
|
+
REQ_FILE="$SRC_DIR/requirements.txt"
|
|
59
|
+
OLD_REQ_HASH=""
|
|
60
|
+
if [ -f "$REQ_FILE" ]; then
|
|
61
|
+
OLD_REQ_HASH="$(shasum -a 256 "$REQ_FILE" | cut -d' ' -f1)"
|
|
62
|
+
fi
|
|
51
63
|
log "Current: v${OLD_VERSION} (${OLD_COMMIT:0:8})"
|
|
52
64
|
|
|
53
65
|
# --- Step 2: Backup databases ---
|
|
@@ -94,6 +106,54 @@ fi
|
|
|
94
106
|
NEW_VERSION="$(read_version)"
|
|
95
107
|
log "New version: v${NEW_VERSION}"
|
|
96
108
|
|
|
109
|
+
# --- Step 4b: Reinstall Python dependencies if requirements.txt changed ---
|
|
110
|
+
NEW_REQ_HASH=""
|
|
111
|
+
if [ -f "$REQ_FILE" ]; then
|
|
112
|
+
NEW_REQ_HASH="$(shasum -a 256 "$REQ_FILE" | cut -d' ' -f1)"
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
DEPS_CHANGED=false
|
|
116
|
+
if [ "$OLD_REQ_HASH" != "$NEW_REQ_HASH" ]; then
|
|
117
|
+
DEPS_CHANGED=true
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
reinstall_pip_deps() {
|
|
121
|
+
local VENV_PIP="$NEXO_HOME/.venv/bin/pip"
|
|
122
|
+
if [ -f "$REQ_FILE" ]; then
|
|
123
|
+
if [ -x "$VENV_PIP" ]; then
|
|
124
|
+
"$VENV_PIP" install --quiet -r "$REQ_FILE" || return 1
|
|
125
|
+
else
|
|
126
|
+
python3 -m pip install --quiet -r "$REQ_FILE" --break-system-packages 2>/dev/null || return 1
|
|
127
|
+
fi
|
|
128
|
+
fi
|
|
129
|
+
return 0
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if [ "$DEPS_CHANGED" = true ] || [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
|
|
133
|
+
log "Reinstalling Python dependencies..."
|
|
134
|
+
if ! reinstall_pip_deps; then
|
|
135
|
+
err "pip install failed! Rolling back..."
|
|
136
|
+
git reset --hard "$OLD_COMMIT"
|
|
137
|
+
reinstall_pip_deps || warn "pip rollback also had issues"
|
|
138
|
+
if [ -d "$BACKUP_DIR" ]; then
|
|
139
|
+
for db in "$BACKUP_DIR"/*.db; do
|
|
140
|
+
[ -f "$db" ] || continue
|
|
141
|
+
BASENAME="$(basename "$db")"
|
|
142
|
+
for candidate in "$NEXO_HOME/data/$BASENAME" "$NEXO_HOME/$BASENAME" "$SRC_DIR/$BASENAME"; do
|
|
143
|
+
if [ -f "$candidate" ]; then
|
|
144
|
+
cp "$db" "$candidate"
|
|
145
|
+
warn " Restored: $BASENAME"
|
|
146
|
+
break
|
|
147
|
+
fi
|
|
148
|
+
done
|
|
149
|
+
done
|
|
150
|
+
fi
|
|
151
|
+
err "Rolled back to ${OLD_COMMIT:0:8}. Databases restored."
|
|
152
|
+
exit 1
|
|
153
|
+
fi
|
|
154
|
+
log "Python dependencies updated."
|
|
155
|
+
fi
|
|
156
|
+
|
|
97
157
|
# --- Step 5: Run migrations if version changed ---
|
|
98
158
|
if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
|
|
99
159
|
log "Version changed: ${OLD_VERSION} -> ${NEW_VERSION}"
|
|
@@ -101,6 +161,8 @@ if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
|
|
|
101
161
|
if ! (cd "$SRC_DIR" && python3 -c "import db; db.init_db()" 2>&1); then
|
|
102
162
|
err "Migration failed! Rolling back..."
|
|
103
163
|
git reset --hard "$OLD_COMMIT"
|
|
164
|
+
# Reinstall pip deps from restored old requirements.txt
|
|
165
|
+
reinstall_pip_deps || warn "pip rollback also had issues"
|
|
104
166
|
# Restore DB backups
|
|
105
167
|
if [ -d "$BACKUP_DIR" ]; then
|
|
106
168
|
for db in "$BACKUP_DIR"/*.db; do
|
|
@@ -115,7 +177,7 @@ if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
|
|
|
115
177
|
done
|
|
116
178
|
done
|
|
117
179
|
fi
|
|
118
|
-
err "Rolled back to ${OLD_COMMIT:0:8}. Databases restored."
|
|
180
|
+
err "Rolled back to ${OLD_COMMIT:0:8}. Databases and deps restored."
|
|
119
181
|
exit 1
|
|
120
182
|
fi
|
|
121
183
|
log "Migrations applied."
|
|
@@ -128,6 +190,8 @@ log "Verifying server.py import..."
|
|
|
128
190
|
if ! (cd "$SRC_DIR" && python3 -c "import server" 2>&1); then
|
|
129
191
|
err "Import verification failed! Rolling back..."
|
|
130
192
|
git reset --hard "$OLD_COMMIT"
|
|
193
|
+
# Reinstall pip deps from restored old requirements.txt
|
|
194
|
+
reinstall_pip_deps || warn "pip rollback also had issues"
|
|
131
195
|
if [ -d "$BACKUP_DIR" ]; then
|
|
132
196
|
for db in "$BACKUP_DIR"/*.db; do
|
|
133
197
|
[ -f "$db" ] || continue
|
|
@@ -141,10 +205,48 @@ if ! (cd "$SRC_DIR" && python3 -c "import server" 2>&1); then
|
|
|
141
205
|
done
|
|
142
206
|
done
|
|
143
207
|
fi
|
|
144
|
-
err "Rolled back to ${OLD_COMMIT:0:8}. Databases restored."
|
|
208
|
+
err "Rolled back to ${OLD_COMMIT:0:8}. Databases and deps restored."
|
|
145
209
|
exit 1
|
|
146
210
|
fi
|
|
147
211
|
|
|
212
|
+
# --- Step 7: Sync hooks to NEXO_HOME ---
|
|
213
|
+
HOOKS_SRC="$SRC_DIR/hooks"
|
|
214
|
+
HOOKS_DEST="$NEXO_HOME/hooks"
|
|
215
|
+
if [ -d "$HOOKS_SRC" ]; then
|
|
216
|
+
mkdir -p "$HOOKS_DEST"
|
|
217
|
+
SYNCED=0
|
|
218
|
+
for hook in "$HOOKS_SRC"/*.sh; do
|
|
219
|
+
[ -f "$hook" ] || continue
|
|
220
|
+
cp "$hook" "$HOOKS_DEST/$(basename "$hook")"
|
|
221
|
+
chmod 755 "$HOOKS_DEST/$(basename "$hook")"
|
|
222
|
+
SYNCED=$((SYNCED + 1))
|
|
223
|
+
done
|
|
224
|
+
if [ "$SYNCED" -gt 0 ]; then
|
|
225
|
+
log "Synced $SYNCED hook(s) to $HOOKS_DEST"
|
|
226
|
+
fi
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
# --- Step 8: Sync cron definitions with manifest ---
|
|
230
|
+
CRON_SYNC="$SRC_DIR/crons/sync.py"
|
|
231
|
+
CRON_SYNC_OK=false
|
|
232
|
+
if [ -f "$CRON_SYNC" ]; then
|
|
233
|
+
log "Syncing cron definitions..."
|
|
234
|
+
if NEXO_HOME="$NEXO_HOME" NEXO_CODE="$SRC_DIR" python3 "$CRON_SYNC" 2>&1; then
|
|
235
|
+
log "Cron definitions synced."
|
|
236
|
+
CRON_SYNC_OK=true
|
|
237
|
+
else
|
|
238
|
+
warn "Cron sync failed (non-fatal). Installed manifest NOT refreshed to avoid divergence."
|
|
239
|
+
fi
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
# --- Step 8b: Refresh installed manifest for catchup/watchdog (only if sync succeeded) ---
|
|
243
|
+
if $CRON_SYNC_OK && [ -d "$SRC_DIR/crons" ]; then
|
|
244
|
+
mkdir -p "$NEXO_HOME/crons"
|
|
245
|
+
cp -f "$SRC_DIR/crons/"*.json "$NEXO_HOME/crons/" 2>/dev/null
|
|
246
|
+
cp -f "$SRC_DIR/crons/"*.py "$NEXO_HOME/crons/" 2>/dev/null
|
|
247
|
+
log "Refreshed installed crons manifest."
|
|
248
|
+
fi
|
|
249
|
+
|
|
148
250
|
# --- Done ---
|
|
149
251
|
echo ""
|
|
150
252
|
log "========================================="
|