nexo-brain 6.5.0 → 7.0.0
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/.claude-plugin/plugin.json +1 -1
- package/README.md +3 -1
- package/package.json +2 -2
- package/src/auto_update.py +271 -30
- package/src/bootstrap_docs.py +2 -1
- package/src/calibration_migration.py +5 -2
- package/src/cli.py +7 -6
- package/src/cron_recovery.py +4 -3
- package/src/db/_skills.py +2 -1
- package/src/desktop_bridge.py +4 -2
- package/src/doctor/providers/boot.py +21 -7
- package/src/doctor/providers/deep.py +6 -5
- package/src/doctor/providers/runtime.py +17 -16
- package/src/evolution_cycle.py +7 -6
- package/src/health_check.py +4 -2
- package/src/paths.py +394 -0
- package/src/plugins/personal_plugins.py +2 -1
- package/src/plugins/recover.py +3 -2
- package/src/plugins/update.py +10 -9
- package/src/public_contribution.py +2 -1
- package/src/runtime_power.py +6 -5
- package/src/script_registry.py +50 -20
- package/src/scripts/nexo-backup.sh +2 -2
- package/src/scripts/nexo-cron-wrapper.sh +2 -2
- package/src/scripts/nexo-deep-sleep.sh +2 -2
- package/src/scripts/nexo-inbox-hook.sh +1 -1
- package/src/scripts/nexo-snapshot-restore.sh +1 -1
- package/src/scripts/nexo-tcc-approve.sh +2 -2
- package/src/scripts/nexo-watchdog.sh +14 -14
- package/src/system_catalog.py +3 -2
- package/src/tools_sessions.py +2 -1
- package/src/user_data_portability.py +9 -8
|
@@ -13,7 +13,8 @@ NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
|
|
|
13
13
|
|
|
14
14
|
def check_db_exists() -> DoctorCheck:
|
|
15
15
|
"""Check that the main database file exists and is readable."""
|
|
16
|
-
|
|
16
|
+
import paths
|
|
17
|
+
db_path = paths.db_path()
|
|
17
18
|
if db_path.is_file():
|
|
18
19
|
size_kb = db_path.stat().st_size / 1024
|
|
19
20
|
return DoctorCheck(
|
|
@@ -37,9 +38,21 @@ def check_db_exists() -> DoctorCheck:
|
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
def check_required_dirs() -> DoctorCheck:
|
|
40
|
-
"""Check that required NEXO_HOME directories exist.
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
"""Check that required NEXO_HOME directories exist (post-F0.6 layout
|
|
42
|
+
or pre-F0.6 fallback)."""
|
|
43
|
+
import paths
|
|
44
|
+
# Each required dir maps to one of the new locations OR the legacy fallback.
|
|
45
|
+
required_helpers = [
|
|
46
|
+
("data", paths.data_dir()),
|
|
47
|
+
("scripts", paths.core_scripts_dir()),
|
|
48
|
+
("plugins", paths.core_plugins_dir()),
|
|
49
|
+
("crons", paths.crons_dir()),
|
|
50
|
+
("hooks", paths.core_hooks_dir()),
|
|
51
|
+
("coordination", paths.coordination_dir()),
|
|
52
|
+
("operations", paths.operations_dir()),
|
|
53
|
+
("logs", paths.logs_dir()),
|
|
54
|
+
]
|
|
55
|
+
missing = [name for (name, path) in required_helpers if not path.is_dir()]
|
|
43
56
|
|
|
44
57
|
if not missing:
|
|
45
58
|
return DoctorCheck(
|
|
@@ -47,7 +60,7 @@ def check_required_dirs() -> DoctorCheck:
|
|
|
47
60
|
tier="boot",
|
|
48
61
|
status="healthy",
|
|
49
62
|
severity="info",
|
|
50
|
-
summary=f"All {len(
|
|
63
|
+
summary=f"All {len(required_helpers)} required directories present",
|
|
51
64
|
)
|
|
52
65
|
|
|
53
66
|
return DoctorCheck(
|
|
@@ -57,7 +70,7 @@ def check_required_dirs() -> DoctorCheck:
|
|
|
57
70
|
severity="warn" if len(missing) < 3 else "error",
|
|
58
71
|
summary=f"{len(missing)} required director{'y' if len(missing) == 1 else 'ies'} missing",
|
|
59
72
|
evidence=[f"Missing: {d}" for d in missing],
|
|
60
|
-
repair_plan=[f"mkdir -p {
|
|
73
|
+
repair_plan=[f"mkdir -p {dict(required_helpers)[d]}" for d in missing],
|
|
61
74
|
)
|
|
62
75
|
|
|
63
76
|
|
|
@@ -107,7 +120,8 @@ def check_disk_space() -> DoctorCheck:
|
|
|
107
120
|
|
|
108
121
|
def check_wrapper_scripts() -> DoctorCheck:
|
|
109
122
|
"""Check that cron wrapper script exists."""
|
|
110
|
-
|
|
123
|
+
import paths
|
|
124
|
+
wrapper = paths.core_scripts_dir() / "nexo-cron-wrapper.sh"
|
|
111
125
|
if wrapper.is_file():
|
|
112
126
|
return DoctorCheck(
|
|
113
127
|
id="boot.wrapper_scripts",
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
import datetime as dt
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
|
+
import paths
|
|
7
8
|
import time
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
|
|
@@ -67,7 +68,7 @@ def _self_audit_enabled() -> bool | None:
|
|
|
67
68
|
|
|
68
69
|
def check_self_audit_summary() -> DoctorCheck:
|
|
69
70
|
"""Check latest self-audit summary exists and is recent."""
|
|
70
|
-
summary_file =
|
|
71
|
+
summary_file = paths.logs_dir() / "self-audit-summary.json"
|
|
71
72
|
age = _file_age_seconds(summary_file)
|
|
72
73
|
|
|
73
74
|
if age is None:
|
|
@@ -154,7 +155,7 @@ def check_schema_version() -> DoctorCheck:
|
|
|
154
155
|
"""Check DB schema version is present and reasonable."""
|
|
155
156
|
try:
|
|
156
157
|
import sqlite3
|
|
157
|
-
db_path =
|
|
158
|
+
db_path = paths.db_path()
|
|
158
159
|
if not db_path.is_file():
|
|
159
160
|
return DoctorCheck(
|
|
160
161
|
id="deep.schema_version",
|
|
@@ -187,7 +188,7 @@ def check_schema_version() -> DoctorCheck:
|
|
|
187
188
|
|
|
188
189
|
def check_preflight_summary() -> DoctorCheck:
|
|
189
190
|
"""Check runtime preflight summary."""
|
|
190
|
-
summary_file =
|
|
191
|
+
summary_file = paths.logs_dir() / "runtime-preflight-summary.json"
|
|
191
192
|
age = _file_age_seconds(summary_file)
|
|
192
193
|
|
|
193
194
|
if age is None:
|
|
@@ -242,7 +243,7 @@ def check_preflight_summary() -> DoctorCheck:
|
|
|
242
243
|
|
|
243
244
|
def check_watchdog_smoke() -> DoctorCheck:
|
|
244
245
|
"""Check watchdog smoke summary."""
|
|
245
|
-
summary_file =
|
|
246
|
+
summary_file = paths.logs_dir() / "watchdog-smoke-summary.json"
|
|
246
247
|
age = _file_age_seconds(summary_file)
|
|
247
248
|
|
|
248
249
|
if age is None:
|
|
@@ -300,7 +301,7 @@ def check_learning_count() -> DoctorCheck:
|
|
|
300
301
|
"""Check learning count as a health proxy."""
|
|
301
302
|
try:
|
|
302
303
|
import sqlite3
|
|
303
|
-
db_path =
|
|
304
|
+
db_path = paths.db_path()
|
|
304
305
|
if not db_path.is_file():
|
|
305
306
|
return DoctorCheck(
|
|
306
307
|
id="deep.learning_count",
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
import datetime as dt
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
|
+
import paths
|
|
7
8
|
import platform
|
|
8
9
|
import plistlib
|
|
9
10
|
import re
|
|
@@ -423,7 +424,7 @@ def _extract_declared_file_targets(args: dict, cwd: str) -> set[str]:
|
|
|
423
424
|
|
|
424
425
|
|
|
425
426
|
def _load_active_conditioned_learnings() -> list[dict]:
|
|
426
|
-
db_path =
|
|
427
|
+
db_path = paths.db_path()
|
|
427
428
|
if not db_path.is_file():
|
|
428
429
|
return []
|
|
429
430
|
try:
|
|
@@ -622,7 +623,7 @@ def _recent_codex_conditioned_file_discipline_status(*, days: int = 7, max_files
|
|
|
622
623
|
|
|
623
624
|
|
|
624
625
|
def _open_protocol_debt_summary(*debt_types: str) -> dict:
|
|
625
|
-
db_path =
|
|
626
|
+
db_path = paths.db_path()
|
|
626
627
|
summary = {"available": False, "open_total": 0, "counts": {}}
|
|
627
628
|
if not db_path.is_file() or not debt_types:
|
|
628
629
|
return summary
|
|
@@ -661,7 +662,7 @@ def _client_assumption_regressions() -> list[str]:
|
|
|
661
662
|
if not src_root.is_dir():
|
|
662
663
|
return []
|
|
663
664
|
src_root = src_root.resolve()
|
|
664
|
-
backup_root = (
|
|
665
|
+
backup_root = (paths.backups_dir()).resolve()
|
|
665
666
|
contrib_root = (NEXO_HOME / "contrib").resolve()
|
|
666
667
|
allowed_relative_paths = {
|
|
667
668
|
Path("scripts") / "deep-sleep" / "collect.py",
|
|
@@ -715,7 +716,7 @@ def _load_json(path: Path) -> dict:
|
|
|
715
716
|
|
|
716
717
|
|
|
717
718
|
def _latest_cron_run_age_seconds(cron_id: str) -> float | None:
|
|
718
|
-
db_path =
|
|
719
|
+
db_path = paths.db_path()
|
|
719
720
|
if not db_path.is_file():
|
|
720
721
|
return None
|
|
721
722
|
try:
|
|
@@ -748,7 +749,7 @@ def _latest_cron_run_age_seconds(cron_id: str) -> float | None:
|
|
|
748
749
|
def _latest_periodic_summary(kind: str) -> dict | None:
|
|
749
750
|
pattern = f"*-{kind}-summary.json"
|
|
750
751
|
candidates: list[tuple[str, Path]] = []
|
|
751
|
-
for path in (
|
|
752
|
+
for path in (paths.operations_dir() / "deep-sleep").glob(pattern):
|
|
752
753
|
try:
|
|
753
754
|
payload = json.loads(path.read_text())
|
|
754
755
|
except Exception:
|
|
@@ -830,7 +831,7 @@ def _enabled_optionals() -> dict[str, bool]:
|
|
|
830
831
|
|
|
831
832
|
def _enabled_manifest_crons() -> list[dict]:
|
|
832
833
|
manifest_candidates = [
|
|
833
|
-
|
|
834
|
+
paths.crons_dir() / "manifest.json",
|
|
834
835
|
NEXO_CODE / "crons" / "manifest.json",
|
|
835
836
|
]
|
|
836
837
|
optionals = _enabled_optionals()
|
|
@@ -1049,7 +1050,7 @@ def _plist_runtime_paths(plist_data: dict) -> list[str]:
|
|
|
1049
1050
|
|
|
1050
1051
|
|
|
1051
1052
|
def _recent_permission_denial(cron_id: str, max_age_seconds: int = 7 * 86400) -> bool:
|
|
1052
|
-
stderr_path =
|
|
1053
|
+
stderr_path = paths.logs_dir() / f"{cron_id}-stderr.log"
|
|
1053
1054
|
age = _file_age_seconds(stderr_path)
|
|
1054
1055
|
if age is None or age > max_age_seconds:
|
|
1055
1056
|
return False
|
|
@@ -1135,7 +1136,7 @@ def _sync_launchagents_from_manifest() -> tuple[bool, list[str]]:
|
|
|
1135
1136
|
|
|
1136
1137
|
def check_immune_status() -> DoctorCheck:
|
|
1137
1138
|
"""Check immune system status freshness."""
|
|
1138
|
-
status_file =
|
|
1139
|
+
status_file = paths.coordination_dir() / "immune-status.json"
|
|
1139
1140
|
age = _file_age_seconds(status_file)
|
|
1140
1141
|
|
|
1141
1142
|
if age is None:
|
|
@@ -1230,7 +1231,7 @@ def check_immune_status() -> DoctorCheck:
|
|
|
1230
1231
|
|
|
1231
1232
|
def check_watchdog_status() -> DoctorCheck:
|
|
1232
1233
|
"""Check watchdog status freshness."""
|
|
1233
|
-
status_file =
|
|
1234
|
+
status_file = paths.operations_dir() / "watchdog-status.json"
|
|
1234
1235
|
age = _file_age_seconds(status_file)
|
|
1235
1236
|
|
|
1236
1237
|
if age is None:
|
|
@@ -1307,7 +1308,7 @@ def check_stale_sessions() -> DoctorCheck:
|
|
|
1307
1308
|
"""Check for stale sessions from DB."""
|
|
1308
1309
|
try:
|
|
1309
1310
|
import sqlite3
|
|
1310
|
-
db_path =
|
|
1311
|
+
db_path = paths.db_path()
|
|
1311
1312
|
if not db_path.is_file():
|
|
1312
1313
|
return DoctorCheck(
|
|
1313
1314
|
id="runtime.stale_sessions",
|
|
@@ -1358,7 +1359,7 @@ def check_cron_freshness() -> DoctorCheck:
|
|
|
1358
1359
|
"""Check cron_runs table for recent executions."""
|
|
1359
1360
|
try:
|
|
1360
1361
|
import sqlite3
|
|
1361
|
-
db_path =
|
|
1362
|
+
db_path = paths.db_path()
|
|
1362
1363
|
if not db_path.is_file():
|
|
1363
1364
|
return DoctorCheck(
|
|
1364
1365
|
id="runtime.cron_freshness",
|
|
@@ -2404,7 +2405,7 @@ def check_protocol_compliance() -> DoctorCheck:
|
|
|
2404
2405
|
try:
|
|
2405
2406
|
import sqlite3
|
|
2406
2407
|
|
|
2407
|
-
db_path =
|
|
2408
|
+
db_path = paths.db_path()
|
|
2408
2409
|
if db_path.is_file():
|
|
2409
2410
|
conn = sqlite3.connect(str(db_path), timeout=2)
|
|
2410
2411
|
try:
|
|
@@ -2775,7 +2776,7 @@ def check_release_artifact_sync() -> DoctorCheck:
|
|
|
2775
2776
|
|
|
2776
2777
|
|
|
2777
2778
|
def check_release_trace_hygiene() -> DoctorCheck:
|
|
2778
|
-
db_path =
|
|
2779
|
+
db_path = paths.db_path()
|
|
2779
2780
|
if not db_path.is_file():
|
|
2780
2781
|
return DoctorCheck(
|
|
2781
2782
|
id="runtime.release_trace_hygiene",
|
|
@@ -2906,8 +2907,8 @@ def check_release_trace_hygiene() -> DoctorCheck:
|
|
|
2906
2907
|
|
|
2907
2908
|
|
|
2908
2909
|
def check_state_watchers() -> DoctorCheck:
|
|
2909
|
-
db_path =
|
|
2910
|
-
summary_path =
|
|
2910
|
+
db_path = paths.db_path()
|
|
2911
|
+
summary_path = paths.operations_dir() / "state-watchers-status.json"
|
|
2911
2912
|
active_watchers = 0
|
|
2912
2913
|
if db_path.is_file():
|
|
2913
2914
|
try:
|
|
@@ -3007,7 +3008,7 @@ def check_state_watchers() -> DoctorCheck:
|
|
|
3007
3008
|
|
|
3008
3009
|
|
|
3009
3010
|
def check_automation_telemetry(days: int = 7) -> DoctorCheck:
|
|
3010
|
-
db_path =
|
|
3011
|
+
db_path = paths.db_path()
|
|
3011
3012
|
if not db_path.is_file():
|
|
3012
3013
|
return DoctorCheck(
|
|
3013
3014
|
id="runtime.automation_telemetry",
|
package/src/evolution_cycle.py
CHANGED
|
@@ -7,6 +7,7 @@ v1.1 (future): sandbox execution of auto-approved changes.
|
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
9
|
import os
|
|
10
|
+
import paths
|
|
10
11
|
import shutil
|
|
11
12
|
import subprocess
|
|
12
13
|
import sqlite3
|
|
@@ -16,17 +17,17 @@ from pathlib import Path
|
|
|
16
17
|
|
|
17
18
|
NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
|
|
18
19
|
NEXO_CODE = Path(os.environ.get("NEXO_CODE", str(NEXO_HOME)))
|
|
19
|
-
NEXO_DB =
|
|
20
|
+
NEXO_DB = paths.db_path()
|
|
20
21
|
SANDBOX_DIR = NEXO_HOME / "sandbox" / "workspace"
|
|
21
|
-
SNAPSHOTS_DIR =
|
|
22
|
-
RESTORE_LOG =
|
|
22
|
+
SNAPSHOTS_DIR = paths.snapshots_dir()
|
|
23
|
+
RESTORE_LOG = paths.logs_dir() / "snapshot-restores.log"
|
|
23
24
|
|
|
24
25
|
# Evolution config: brain/ (canonical) > cortex/ (legacy) > NEXO_CODE (dev)
|
|
25
26
|
def _resolve_evolution_file(name: str) -> Path:
|
|
26
|
-
for candidate in [
|
|
27
|
+
for candidate in [paths.brain_dir() / name, NEXO_HOME / "cortex" / name, NEXO_CODE / name]:
|
|
27
28
|
if candidate.exists():
|
|
28
29
|
return candidate
|
|
29
|
-
return
|
|
30
|
+
return paths.brain_dir() / name # default canonical path
|
|
30
31
|
|
|
31
32
|
OBJECTIVE_FILE = _resolve_evolution_file("evolution-objective.json")
|
|
32
33
|
PROMPT_FILE = _resolve_evolution_file("evolution-prompt.md")
|
|
@@ -233,7 +234,7 @@ def dry_run_restore_test() -> bool:
|
|
|
233
234
|
_nexo_code = Path(os.environ.get("NEXO_CODE", ""))
|
|
234
235
|
restore_script = None
|
|
235
236
|
for candidate in [_nexo_code / "scripts" / "nexo-snapshot-restore.sh",
|
|
236
|
-
|
|
237
|
+
paths.core_scripts_dir() / "nexo-snapshot-restore.sh"]:
|
|
237
238
|
if candidate.exists():
|
|
238
239
|
restore_script = candidate
|
|
239
240
|
break
|
package/src/health_check.py
CHANGED
|
@@ -47,7 +47,8 @@ def _check_runtime() -> dict:
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
def _check_database() -> dict:
|
|
50
|
-
|
|
50
|
+
import paths
|
|
51
|
+
db_path = paths.db_path()
|
|
51
52
|
out: dict[str, Any] = {"path": str(db_path), "exists": db_path.is_file()}
|
|
52
53
|
if not out["exists"]:
|
|
53
54
|
out["status"] = "error"
|
|
@@ -108,7 +109,8 @@ def _check_mcp() -> dict:
|
|
|
108
109
|
|
|
109
110
|
|
|
110
111
|
def _check_errors(hours: int = 24) -> dict:
|
|
111
|
-
|
|
112
|
+
import paths
|
|
113
|
+
ops_dir = paths.operations_dir()
|
|
112
114
|
out: dict[str, Any] = {"dir": str(ops_dir)}
|
|
113
115
|
if not ops_dir.is_dir():
|
|
114
116
|
out["recent_errors"] = 0
|