nexo-brain 2.5.0 → 2.6.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 +33 -0
- package/.mcp.json +12 -0
- package/README.md +48 -23
- package/bin/nexo-brain.js +65 -33
- package/hooks/hooks.json +14 -0
- package/package.json +15 -3
- package/src/auto_update.py +79 -2
- package/src/cli.py +490 -11
- package/src/cron_recovery.py +283 -0
- package/src/crons/manifest.json +79 -21
- package/src/crons/sync.py +132 -27
- package/src/db/__init__.py +11 -0
- package/src/db/_personal_scripts.py +548 -0
- package/src/db/_schema.py +44 -1
- package/src/doctor/providers/runtime.py +272 -75
- package/src/evolution_cycle.py +90 -7
- package/src/nexo.db +0 -0
- package/src/plugins/evolution.py +9 -2
- package/src/plugins/personal_scripts.py +117 -0
- package/src/plugins/schedule.py +116 -27
- package/src/script_registry.py +877 -28
- package/src/scripts/nexo-catchup.py +74 -109
- package/src/scripts/nexo-evolution-run.py +178 -67
- package/src/scripts/nexo-watchdog.sh +242 -54
- package/src/tools_learnings.py +8 -0
- package/templates/launchagents/com.nexo.catchup.plist +7 -6
- package/templates/script-template.py +3 -0
- package/templates/script-template.sh +13 -0
- package/src/scripts/nexo-day-orchestrator.sh +0 -139
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nexo-brain",
|
|
3
|
+
"version": "2.5.1",
|
|
4
|
+
"description": "Cognitive co-operator — persistent memory, learning, autonomous processes, Deep Sleep overnight analysis, unified diagnostics, executable skills, day orchestrator, and web dashboard for AI agents.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "NEXO Brain",
|
|
7
|
+
"email": "info@nexo-brain.com",
|
|
8
|
+
"url": "https://nexo-brain.com"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://nexo-brain.com",
|
|
11
|
+
"repository": "https://github.com/wazionapps/nexo",
|
|
12
|
+
"license": "AGPL-3.0",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"memory",
|
|
15
|
+
"cognitive",
|
|
16
|
+
"autonomous",
|
|
17
|
+
"deep-sleep",
|
|
18
|
+
"learning",
|
|
19
|
+
"mcp",
|
|
20
|
+
"work-continuity",
|
|
21
|
+
"skills",
|
|
22
|
+
"doctor",
|
|
23
|
+
"dashboard"
|
|
24
|
+
],
|
|
25
|
+
"mcpServers": "./.mcp.json",
|
|
26
|
+
"hooks": "./hooks/hooks.json",
|
|
27
|
+
"userConfig": {
|
|
28
|
+
"operator_name": {
|
|
29
|
+
"description": "What should your AI co-operator call itself? (default: NEXO)",
|
|
30
|
+
"sensitive": false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
package/.mcp.json
ADDED
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://github.com/wazionapps/nexo/stargazers)
|
|
7
7
|
[](https://www.gnu.org/licenses/agpl-3.0)
|
|
8
8
|
|
|
9
|
-
> The first AI memory system with architectural inhibitory control — the agent reasons about whether to act before acting. Cognitive Cortex, Context Continuity via auto-compaction hooks, Smart Startup, Context Packets, Auto-Prime, 30 Core Rules as DNA, auto-update, auto-diary,
|
|
9
|
+
> The first AI memory system with architectural inhibitory control — the agent reasons about whether to act before acting. Cognitive Cortex, Context Continuity via auto-compaction hooks, Smart Startup, Context Packets, Auto-Prime, 30 Core Rules as DNA, auto-update, auto-diary, CLAUDE.md version tracker, personal scripts registry, and Claude Code plugin structure. Battle-tested from 6 months of production use, validated via multi-AI debate.
|
|
10
10
|
|
|
11
11
|
**NEXO Brain transforms any MCP-compatible AI agent from a stateless assistant into a cognitive partner that remembers, learns, forgets, adapts, and builds a relationship with you over time.**
|
|
12
12
|
|
|
@@ -198,7 +198,7 @@ This means long sessions (8+ hours) feel like one continuous conversation instea
|
|
|
198
198
|
|
|
199
199
|
## Cognitive Features
|
|
200
200
|
|
|
201
|
-
NEXO Brain provides **
|
|
201
|
+
NEXO Brain provides **144+ MCP tools** across 20+ categories. These features implement cognitive science concepts that go beyond basic memory:
|
|
202
202
|
|
|
203
203
|
### Input Pipeline
|
|
204
204
|
|
|
@@ -269,7 +269,7 @@ Full results in [`benchmarks/locomo/results/`](benchmarks/locomo/results/).
|
|
|
269
269
|
|
|
270
270
|
## Nervous System (v2.0.0)
|
|
271
271
|
|
|
272
|
-
NEXO Brain doesn't just respond — it runs
|
|
272
|
+
NEXO Brain doesn't just respond — it runs 14 autonomous processes in the background, like a biological nervous system. They handle maintenance, health monitoring, and self-improvement without any user interaction:
|
|
273
273
|
|
|
274
274
|
| Script | Schedule | What It Does |
|
|
275
275
|
|--------|----------|-------------|
|
|
@@ -289,7 +289,7 @@ NEXO Brain doesn't just respond — it runs 15 autonomous processes in the backg
|
|
|
289
289
|
| **watchdog** | Every 30 min | Monitors services, LaunchAgents, and infrastructure health |
|
|
290
290
|
| **auto-close-sessions** | Every 5 min | Cleans stale sessions |
|
|
291
291
|
|
|
292
|
-
Core processes are defined in `src/crons/manifest.json` and auto-synced to your system by `nexo_update`. On macOS they run via LaunchAgents; on Linux via systemd user timers. `tcc-approve`, `prevent-sleep`, and `backup` are platform/personal helpers — not in the manifest but listed above for completeness. Personal
|
|
292
|
+
Core processes are defined in `src/crons/manifest.json` and auto-synced to your system by `nexo_update`. On macOS they run via LaunchAgents; on Linux via systemd user timers. `tcc-approve`, `prevent-sleep`, and `backup` are platform/personal helpers — not in the manifest but listed above for completeness. Personal scripts (your own automations) are tracked separately in the Personal Scripts Registry and never touched by the core sync. If your Mac was asleep during a scheduled process, the catch-up script re-runs everything in order when it wakes.
|
|
293
293
|
|
|
294
294
|
## Deep Sleep v2 — Overnight Learning (v2.1.0)
|
|
295
295
|
|
|
@@ -390,7 +390,7 @@ This opens `localhost:6174` in your browser. Add `--port 8080` to change the por
|
|
|
390
390
|
|
|
391
391
|
## Full Orchestration System
|
|
392
392
|
|
|
393
|
-
Memory alone doesn't make a co-operator. What makes the difference is the **behavioral loop** — the automated discipline that ensures every session starts informed, runs with guardrails, and ends with self-reflection.
|
|
393
|
+
Memory alone doesn't make a co-operator. What makes the difference is the **behavioral loop** — the automated discipline that ensures every session starts informed, runs with guardrails, and ends with self-reflection. The hooks and lifecycle below are built into the core product. The optional Day Orchestrator (autonomous headless cycles) was removed from core in v2.6.0 — power users can set it up as a personal script.
|
|
394
394
|
|
|
395
395
|
### Automated Hooks
|
|
396
396
|
|
|
@@ -501,7 +501,7 @@ The installer handles everything:
|
|
|
501
501
|
- Node.js project detected
|
|
502
502
|
Configuring MCP server...
|
|
503
503
|
Setting up nervous system...
|
|
504
|
-
|
|
504
|
+
14 autonomous processes configured.
|
|
505
505
|
Dashboard configured at localhost:6174.
|
|
506
506
|
Caffeinate enabled.
|
|
507
507
|
Generating operator instructions...
|
|
@@ -532,23 +532,31 @@ That's it. No need to run `claude` manually. Your operator will greet you immedi
|
|
|
532
532
|
| Component | What | Where |
|
|
533
533
|
|-----------|------|-------|
|
|
534
534
|
| Cognitive engine | Python: fastembed, numpy, vector search | pip packages |
|
|
535
|
-
| MCP server |
|
|
535
|
+
| MCP server | 144+ tools for memory, cognition, learning, guard | NEXO_HOME/ |
|
|
536
536
|
| Plugins | Guard, episodic memory, cognitive memory, entities, preferences, update, etc. | Code: src/plugins/, Personal: NEXO_HOME/plugins/ |
|
|
537
537
|
| Hooks (7) | SessionStart, Stop, PostToolUse, PreCompact, PostCompact | NEXO_HOME/hooks/ |
|
|
538
|
-
| Nervous system |
|
|
539
|
-
| Dashboard | Web UI at localhost:6174 (
|
|
538
|
+
| Nervous system | 14 autonomous processes (decay, sleep, audit, evolution, watchdog, dashboard, etc.) | NEXO_HOME/scripts/ |
|
|
539
|
+
| Dashboard | Web UI at localhost:6174 (23 modules, dark theme) — opt-in, always-on | NEXO_HOME/dashboard/ |
|
|
540
|
+
| Runtime CLI | `nexo` command: chat, scripts, doctor, skills, update | NEXO_HOME/bin/ |
|
|
541
|
+
| Doctor | Unified diagnostics: boot/runtime/deep tiers, `--fix` mode | src/doctor/ |
|
|
542
|
+
| Skills v2 | Executable skills with guide/execute/hybrid modes, approval levels | NEXO_HOME/skills/ |
|
|
543
|
+
| Personal Scripts Registry | User-defined scripts tracked in SQLite with scheduling, reconciliation, and 9 MCP tools | NEXO_HOME/scripts/ |
|
|
540
544
|
| CLAUDE.md | Complete operator instructions (Codex, hooks, guard, trust, memory) | ~/.claude/CLAUDE.md |
|
|
541
545
|
| Schedule config | schedule.json with customizable process times and timezone | NEXO_HOME/config/ |
|
|
542
546
|
| Auto-update | Non-blocking startup check (5s max), opt-out via schedule.json | Built into server startup |
|
|
543
547
|
| CLAUDE.md tracker | Version-tracked core sections with safe updates preserving customizations | Built into auto-update |
|
|
544
548
|
| Auto-diary | 3-layer system: PostToolUse every 10 calls, PreCompact emergency, heartbeat DIARY_OVERDUE | Built into hooks |
|
|
545
|
-
| Claude Code config | MCP server + 7 hooks +
|
|
549
|
+
| Claude Code config | MCP server + 7 hooks + core processes registered | ~/.claude/settings.json |
|
|
546
550
|
|
|
547
551
|
### Runtime CLI
|
|
548
552
|
|
|
549
553
|
After installation or auto-update, NEXO adds `NEXO_HOME/bin` to your shell `PATH`. Open a new terminal and the `nexo` command provides operational tools:
|
|
550
554
|
|
|
551
555
|
```bash
|
|
556
|
+
# Claude Code
|
|
557
|
+
nexo chat # Launch Claude Code in the current directory
|
|
558
|
+
nexo chat ~/claude # Launch Claude Code in a specific directory
|
|
559
|
+
|
|
552
560
|
# Personal Scripts
|
|
553
561
|
nexo scripts list # List your personal scripts
|
|
554
562
|
nexo scripts run my-script # Run a script with injected NEXO env
|
|
@@ -570,7 +578,7 @@ nexo doctor --tier runtime --json # Machine-readable health report
|
|
|
570
578
|
nexo doctor --fix # Apply deterministic repairs
|
|
571
579
|
```
|
|
572
580
|
|
|
573
|
-
Personal scripts live in `NEXO_HOME/scripts/` with inline metadata. See `docs/writing-scripts.md` for details.
|
|
581
|
+
Personal scripts live in `NEXO_HOME/scripts/` with inline metadata and are tracked in a first-class registry (SQLite). 9 MCP tools manage the full lifecycle: `nexo_personal_scripts_list`, `nexo_personal_script_create`, `nexo_personal_script_remove`, `nexo_personal_scripts_reconcile`, `nexo_personal_scripts_sync`, `nexo_personal_scripts_classify`, `nexo_personal_script_schedules`, `nexo_personal_script_unschedule`, `nexo_personal_scripts_ensure_schedules`. See `docs/writing-scripts.md` for details.
|
|
574
582
|
|
|
575
583
|
Skills v2 combine procedural guides with optional executable scripts. Personal skills live in `NEXO_HOME/skills/`, packaged core skills live in `NEXO_CODE/skills/` during development and `NEXO_HOME/skills-core/` in installed environments, and staged runtime copies live in `NEXO_HOME/skills-runtime/`. Execution is fully autonomous: Deep Sleep can evolve mature guide skills into executable drafts automatically, and runtime execution no longer waits for manual approval. See `docs/skills-v2.md` for the full model.
|
|
576
584
|
|
|
@@ -580,7 +588,7 @@ The Doctor system reads existing health artifacts (immune, watchdog, self-audit)
|
|
|
580
588
|
|
|
581
589
|
- **macOS or Linux** (Windows via [WSL](https://learn.microsoft.com/en-us/windows/wsl/install))
|
|
582
590
|
- **Node.js 18+** (for the installer)
|
|
583
|
-
- **Claude Opus (latest version) strongly recommended.** NEXO Brain provides
|
|
591
|
+
- **Claude Opus (latest version) strongly recommended.** NEXO Brain provides 144+ MCP tools across 20+ categories. This cognitive load requires a top-tier model with large context window. Smaller models (Haiku, Sonnet) may struggle with tool selection and produce inconsistent results. Opus handles all 144+ tools without hesitation.
|
|
584
592
|
- Python 3, Homebrew, and Claude Code are installed automatically if missing.
|
|
585
593
|
|
|
586
594
|
## Architecture
|
|
@@ -599,7 +607,7 @@ NEXO Brain separates **code** (immutable, in the repo or npm package) from **dat
|
|
|
599
607
|
|
|
600
608
|
The plugin loader scans `src/plugins/` first (base), then `NEXO_HOME/plugins/` (personal override by filename). This dual-directory approach lets you extend NEXO without forking the repo.
|
|
601
609
|
|
|
602
|
-
###
|
|
610
|
+
### 144+ MCP Tools across 20+ Categories
|
|
603
611
|
|
|
604
612
|
| Category | Count | Tools | Purpose |
|
|
605
613
|
|----------|-------|-------|---------|
|
|
@@ -624,6 +632,7 @@ The plugin loader scans `src/plugins/` first (base), then `NEXO_HOME/plugins/` (
|
|
|
624
632
|
| Adaptive & Somatic | 4 | adaptive_weights, adaptive_override, somatic_check, somatic_stats | Learned signal weights + pain memory per file |
|
|
625
633
|
| Knowledge Graph | 4 | kg_query, kg_path, kg_neighbors, kg_stats | Bi-temporal entity-relationship graph |
|
|
626
634
|
| Context Continuity | 2 | checkpoint_save, checkpoint_read | Auto-compaction session preservation |
|
|
635
|
+
| Personal Scripts | 9 | personal_scripts_list, personal_script_create, personal_script_remove, personal_scripts_reconcile, personal_scripts_sync, personal_scripts_classify, personal_script_schedules, personal_script_unschedule, personal_scripts_ensure_schedules | First-class script registry with lifecycle, scheduling, and reconciliation |
|
|
627
636
|
| Update | 1 | update | Pull latest code, backup, migrate, verify (with rollback) |
|
|
628
637
|
|
|
629
638
|
### Plugin System
|
|
@@ -682,7 +691,7 @@ NEXO Brain is designed as an MCP server. Claude Code is the primary supported cl
|
|
|
682
691
|
npx nexo-brain
|
|
683
692
|
```
|
|
684
693
|
|
|
685
|
-
All
|
|
694
|
+
All 144+ tools are available immediately after installation. The installer configures Claude Code's `~/.claude/settings.json` automatically.
|
|
686
695
|
|
|
687
696
|
### OpenClaw
|
|
688
697
|
|
|
@@ -778,17 +787,33 @@ If NEXO Brain is useful to you, consider:
|
|
|
778
787
|
|
|
779
788
|
[](https://star-history.com/#wazionapps/nexo&Date)
|
|
780
789
|
|
|
781
|
-
## Known Issues
|
|
790
|
+
## Known Issues
|
|
782
791
|
|
|
783
|
-
| Priority | Issue |
|
|
784
|
-
|
|
785
|
-
|
|
|
786
|
-
|
|
|
787
|
-
| P0 | Dashboard has no authentication (localhost only) | v2.1.0 |
|
|
788
|
-
| P0 | Database migrations are fail-open (errors logged but not blocking) | v2.1.0 |
|
|
792
|
+
| Priority | Issue | Status |
|
|
793
|
+
|----------|-------|--------|
|
|
794
|
+
| P1 | Credentials stored in plaintext SQLite — protect with filesystem permissions | Mitigated: secret redaction in tool logs (v2.4.0) |
|
|
795
|
+
| P2 | Dashboard has no authentication (localhost only) | By design — bind to localhost, no remote access |
|
|
789
796
|
|
|
790
797
|
## Changelog
|
|
791
798
|
|
|
799
|
+
### v2.6.0 — Personal Scripts Registry, Plugin Marketplace, Orchestrator Decoupled (2026-04-03)
|
|
800
|
+
- **Personal Scripts Registry**: First-class script tracking in SQLite with 9 MCP tools for full lifecycle management — create, list, remove, schedule, unschedule, reconcile, sync, classify.
|
|
801
|
+
- **Orchestrator removed from core**: Day Orchestrator decoupled from the product. Power users keep it as a personal script. Reduces complexity for standard installs.
|
|
802
|
+
- **Claude Code plugin structure**: `plugin.json` + entry point for Anthropic marketplace submission.
|
|
803
|
+
- **`nexo chat`**: Official CLI command to launch Claude Code with NEXO as operator.
|
|
804
|
+
- **Managed Evolution hardening**: Can now modify core behavior modules with rollback followups. Fixed false-positive watchdog tamper detection.
|
|
805
|
+
- **Cron reliability**: Hardened recovery, TCC diagnostics, keepalive sync, disabled optional cron respect.
|
|
806
|
+
- **Duplicate learning prevention**: Exact-title guard before insert.
|
|
807
|
+
|
|
808
|
+
### v2.5.0 — Runtime CLI, Doctor, Skills v2, Day Orchestrator (2026-04-03)
|
|
809
|
+
- **Runtime CLI** (`nexo`): New operational CLI separate from installer. `nexo scripts list/run/doctor/call` for personal scripts, `nexo doctor` for diagnostics, `nexo skills apply` for executable skills, `nexo update` for one-step sync.
|
|
810
|
+
- **Unified Doctor**: Modular diagnostic system with boot/runtime/deep tiers. Report-only by default, deterministic `--fix` mode. MCP tool `nexo_doctor`. LaunchAgent schedule drift detection and reconciliation.
|
|
811
|
+
- **Skills v2**: Executable skills with guide/execute/hybrid modes. Security levels (read-only/local/remote) with explicit approval. Core vs personal vs community directories. Deep Sleep auto-evolution integration.
|
|
812
|
+
- **Day Orchestrator**: Autonomous NEXO cycles every 15 min (8:00-23:00). *(Removed from core in v2.6.0 — available as personal script.)*
|
|
813
|
+
- **Dashboard always-on**: Web UI at localhost:6174 as persistent LaunchAgent. 23 modules, Jinja2 templating, dark theme. Opt-in.
|
|
814
|
+
- **Personal Scripts Framework**: Auto-discovery in NEXO_HOME/scripts/, inline metadata, runtime detection, forbidden-pattern validation. *(Promoted to first-class registry in v2.6.0.)*
|
|
815
|
+
- Configurable operator name (UserContext singleton), watchdog normalized to 30 min, LaunchAgent drift fix.
|
|
816
|
+
|
|
792
817
|
### v2.4.0 — Skills, Cron Scheduler, Security, Full Audit (2026-04-03)
|
|
793
818
|
- **Skill Auto-Creation**: Deep Sleep extracts reusable procedures from sessions. Content stored as markdown with steps and gotchas. Trust pipeline with autonomous quality control.
|
|
794
819
|
- **Cron Scheduler**: execution tracking (`cron_runs` table), `nexo_schedule_status` and `nexo_schedule_add` MCP tools, universal cron wrapper for all processes.
|
|
@@ -811,9 +836,9 @@ If NEXO Brain is useful to you, consider:
|
|
|
811
836
|
- **Auto-diary**: 3-layer system — PostToolUse every 10 calls, PreCompact emergency save, heartbeat DIARY_OVERDUE signal.
|
|
812
837
|
- **CLAUDE.md version tracker**: Section markers enable safe core updates without losing user customizations.
|
|
813
838
|
- **schedule.json**: Customizable process schedules with timezone support and `auto_update` flag.
|
|
814
|
-
- **
|
|
839
|
+
- **14 autonomous processes**: Added auto-close-sessions, synthesis, backup, tcc-approve, prevent-sleep (cross-platform).
|
|
815
840
|
- **7 hooks**: SessionStart (timestamp + briefing), Stop, PostToolUse (capture + inbox), PreCompact, PostCompact.
|
|
816
|
-
- **
|
|
841
|
+
- **144+ MCP tools**: Added `nexo_update` tool for manual updates with rollback.
|
|
817
842
|
- **Lambda fix**: Decay values were 24x too aggressive (STM: 7h to 7d, LTM: 2.4d to 60d).
|
|
818
843
|
- **Guard scoping**: Was returning 35+ irrelevant blocking rules; now scoped to area and gated to high/critical.
|
|
819
844
|
- **12 rounds of external audit**: ~60 findings resolved.
|
package/bin/nexo-brain.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
const { execSync, spawnSync } = require("child_process");
|
|
18
|
+
const crypto = require("crypto");
|
|
18
19
|
const fs = require("fs");
|
|
19
20
|
const path = require("path");
|
|
20
21
|
const readline = require("readline");
|
|
@@ -59,13 +60,67 @@ function log(msg) {
|
|
|
59
60
|
console.log(` ${msg}`);
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
function syncWatchdogHashRegistry(nexoHome) {
|
|
64
|
+
try {
|
|
65
|
+
const watchdogPath = path.join(nexoHome, "scripts", "nexo-watchdog.sh");
|
|
66
|
+
if (!fs.existsSync(watchdogPath)) return;
|
|
67
|
+
|
|
68
|
+
const registryPath = path.join(nexoHome, "scripts", ".watchdog-hashes");
|
|
69
|
+
const entries = new Map();
|
|
70
|
+
if (fs.existsSync(registryPath)) {
|
|
71
|
+
for (const line of fs.readFileSync(registryPath, "utf8").split(/\r?\n/)) {
|
|
72
|
+
if (!line.includes("|")) continue;
|
|
73
|
+
const [filePath, expectedHash] = line.split("|");
|
|
74
|
+
if (filePath) entries.set(filePath, expectedHash || "");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const digest = crypto.createHash("sha256").update(fs.readFileSync(watchdogPath)).digest("hex");
|
|
79
|
+
entries.set(watchdogPath, digest);
|
|
80
|
+
const body = Array.from(entries.entries())
|
|
81
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
82
|
+
.map(([filePath, hash]) => `${filePath}|${hash}`)
|
|
83
|
+
.join("\n");
|
|
84
|
+
fs.writeFileSync(registryPath, `${body}\n`);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
log(`WARN: could not sync watchdog hash registry: ${err.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isProtectedMacPath(candidate) {
|
|
91
|
+
if (process.platform !== "darwin" || !candidate) return false;
|
|
92
|
+
const homeDir = require("os").homedir();
|
|
93
|
+
const expanded = candidate.replace(/^~/, homeDir);
|
|
94
|
+
const resolved = path.resolve(expanded);
|
|
95
|
+
const protectedRoots = [
|
|
96
|
+
path.join(homeDir, "Documents"),
|
|
97
|
+
path.join(homeDir, "Desktop"),
|
|
98
|
+
path.join(homeDir, "Downloads"),
|
|
99
|
+
path.join(homeDir, "Library", "Mobile Documents"),
|
|
100
|
+
];
|
|
101
|
+
return protectedRoots.some((root) => resolved === root || resolved.startsWith(`${root}${path.sep}`));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function logMacPermissionsNotice(nexoHome, pythonPath = "") {
|
|
105
|
+
if (!isProtectedMacPath(nexoHome)) return;
|
|
106
|
+
log("macOS protected-folder warning:");
|
|
107
|
+
log(` NEXO_HOME is inside a protected folder: ${nexoHome}`);
|
|
108
|
+
log(" Background jobs may fail with 'Operation not permitted'.");
|
|
109
|
+
log(" Recommended: move NEXO_HOME outside Documents/Desktop/Downloads/iCloud Drive.");
|
|
110
|
+
log(" If you keep it there, grant Full Disk Access to /bin/bash and your Python runtime.");
|
|
111
|
+
if (pythonPath) {
|
|
112
|
+
log(` Python runtime: ${pythonPath}`);
|
|
113
|
+
}
|
|
114
|
+
log(" System Settings → Privacy & Security → Full Disk Access");
|
|
115
|
+
}
|
|
116
|
+
|
|
62
117
|
// ══════════════════════════════════════════════════════════════════════════════
|
|
63
118
|
// CORE PROCESS & HOOK DEFINITIONS
|
|
64
|
-
// All
|
|
119
|
+
// All core nightly/periodic processes and all 8 core hooks that make NEXO functional.
|
|
65
120
|
// ══════════════════════════════════════════════════════════════════════════════
|
|
66
121
|
|
|
67
122
|
/**
|
|
68
|
-
* Complete definition of all
|
|
123
|
+
* Complete definition of all core NEXO automated processes.
|
|
69
124
|
* Each entry specifies the script, its interpreter ("python" or "bash"),
|
|
70
125
|
* the schedule type, and default schedule values.
|
|
71
126
|
*/
|
|
@@ -95,8 +150,6 @@ const ALL_PROCESSES = [
|
|
|
95
150
|
type: "keepAlive", purpose: "Keep machine awake for nocturnal processes" },
|
|
96
151
|
{ name: "dashboard", script: "nexo-dashboard.sh", interpreter: "bash", scriptDir: "scripts",
|
|
97
152
|
type: "keepAlive", optional: "dashboard", purpose: "Web dashboard at localhost:6174" },
|
|
98
|
-
{ name: "day-orchestrator", script: "nexo-day-orchestrator.sh", interpreter: "bash", scriptDir: "scripts",
|
|
99
|
-
type: "keepAlive", optional: "orchestrator", purpose: "Autonomous NEXO cycles every 15 min (8:00-23:00)" },
|
|
100
153
|
// --- Daily (times from schedule.json) ---
|
|
101
154
|
{ name: "cognitive-decay", script: "nexo-cognitive-decay.py", interpreter: "python", scriptDir: "scripts",
|
|
102
155
|
type: "daily", defaultHour: 3, defaultMinute: 0, purpose: "Memory decay" },
|
|
@@ -741,7 +794,7 @@ async function main() {
|
|
|
741
794
|
fs.writeFileSync(CLAUDE_SETTINGS, JSON.stringify(settings, null, 2));
|
|
742
795
|
log(" All 8 core hooks registered in Claude Code settings.");
|
|
743
796
|
|
|
744
|
-
// Regenerate
|
|
797
|
+
// Regenerate all core LaunchAgents / systemd timers
|
|
745
798
|
const migSchedule = loadOrCreateSchedule(NEXO_HOME);
|
|
746
799
|
const migPython = findVenvPython(NEXO_HOME) || "python3";
|
|
747
800
|
let migOptionals = {};
|
|
@@ -865,6 +918,8 @@ async function main() {
|
|
|
865
918
|
});
|
|
866
919
|
}
|
|
867
920
|
|
|
921
|
+
logMacPermissionsNotice(NEXO_HOME, syncPython);
|
|
922
|
+
|
|
868
923
|
log(`Already at v${currentVersion}. No migration needed.`);
|
|
869
924
|
rl.close();
|
|
870
925
|
return;
|
|
@@ -909,6 +964,7 @@ async function main() {
|
|
|
909
964
|
}
|
|
910
965
|
const pyVersion = run(`${python} --version`);
|
|
911
966
|
log(`Found ${pyVersion} at ${python}`);
|
|
967
|
+
logMacPermissionsNotice(NEXO_HOME, python);
|
|
912
968
|
|
|
913
969
|
// Find or install Claude Code
|
|
914
970
|
let claudeInstalled = run("which claude");
|
|
@@ -972,9 +1028,6 @@ async function main() {
|
|
|
972
1028
|
dashboardQ: " Enable web dashboard at localhost:6174?\n (Always-on UI to explore memory, sessions, learnings, and system health)\n 1. Yes\n 2. No\n > ",
|
|
973
1029
|
dashYes: "Dashboard enabled.",
|
|
974
1030
|
dashNo: "Dashboard disabled. You can start it manually: nexo dashboard",
|
|
975
|
-
orchestratorQ: " Enable autonomous mode? (I'll work on my own every 15 min: check followups, emails, infra, and report by email)\n 1. Yes\n 2. No\n > ",
|
|
976
|
-
orchYes: "Autonomous mode enabled. I'll be working for you 8:00-23:00.",
|
|
977
|
-
orchNo: "Autonomous mode disabled. I'll only work when you open a session.",
|
|
978
1031
|
autoInstallQ: " Can I install tools automatically if I need them? (brew, pip, npm)\n 1. Yes, install whatever you need\n 2. Ask me before installing anything\n > ",
|
|
979
1032
|
autoInstallYes: "Auto-install enabled.",
|
|
980
1033
|
autoInstallNo: "I'll ask before installing.",
|
|
@@ -1007,9 +1060,6 @@ async function main() {
|
|
|
1007
1060
|
dashboardQ: " ¿Activar el dashboard web en localhost:6174?\n (UI siempre activa para explorar memoria, sesiones, learnings y salud del sistema)\n 1. Sí\n 2. No\n > ",
|
|
1008
1061
|
dashYes: "Dashboard activado.",
|
|
1009
1062
|
dashNo: "Dashboard desactivado. Puedes iniciarlo manualmente: nexo dashboard",
|
|
1010
|
-
orchestratorQ: " ¿Activar modo autónomo? (Trabajo solo cada 15 min: reviso followups, emails, infra, y te informo por email)\n 1. Sí\n 2. No\n > ",
|
|
1011
|
-
orchYes: "Modo autónomo activado. Estaré trabajando para ti de 8:00 a 23:00.",
|
|
1012
|
-
orchNo: "Modo autónomo desactivado. Solo trabajo cuando abras sesión.",
|
|
1013
1063
|
autoInstallQ: " ¿Puedo instalar herramientas automáticamente si las necesito? (brew, pip, npm)\n 1. Sí, instala lo que necesites\n 2. Pregúntame antes de instalar algo\n > ",
|
|
1014
1064
|
autoInstallYes: "Auto-instalación activada.",
|
|
1015
1065
|
autoInstallNo: "Te preguntaré antes.",
|
|
@@ -1042,9 +1092,6 @@ async function main() {
|
|
|
1042
1092
|
dashboardQ: " Activer le dashboard web sur localhost:6174 ?\n (UI toujours active pour explorer mémoire, sessions et santé du système)\n 1. Oui\n 2. Non\n > ",
|
|
1043
1093
|
dashYes: "Dashboard activé.",
|
|
1044
1094
|
dashNo: "Dashboard désactivé. Démarrage manuel : nexo dashboard",
|
|
1045
|
-
orchestratorQ: " Activer le mode autonome ? (Je travaille seul toutes les 15 min : followups, emails, infra, rapport par email)\n 1. Oui\n 2. Non\n > ",
|
|
1046
|
-
orchYes: "Mode autonome activé. Je travaille pour vous de 8h à 23h.",
|
|
1047
|
-
orchNo: "Mode autonome désactivé. Je travaille uniquement en session.",
|
|
1048
1095
|
autoInstallQ: " Puis-je installer des outils automatiquement ? (brew, pip, npm)\n 1. Oui\n 2. Demande-moi avant\n > ",
|
|
1049
1096
|
autoInstallYes: "Auto-installation activée.",
|
|
1050
1097
|
autoInstallNo: "Je demanderai avant.",
|
|
@@ -1077,9 +1124,6 @@ async function main() {
|
|
|
1077
1124
|
dashboardQ: " Web-Dashboard auf localhost:6174 aktivieren?\n (Immer aktive UI für Speicher, Sitzungen und Systemgesundheit)\n 1. Ja\n 2. Nein\n > ",
|
|
1078
1125
|
dashYes: "Dashboard aktiviert.",
|
|
1079
1126
|
dashNo: "Dashboard deaktiviert. Manuell starten: nexo dashboard",
|
|
1080
|
-
orchestratorQ: " Autonomen Modus aktivieren? (Ich arbeite alle 15 Min selbstständig: Followups, E-Mails, Infra, Bericht per E-Mail)\n 1. Ja\n 2. Nein\n > ",
|
|
1081
|
-
orchYes: "Autonomer Modus aktiviert. Ich arbeite für dich von 8:00 bis 23:00.",
|
|
1082
|
-
orchNo: "Autonomer Modus deaktiviert. Ich arbeite nur in Sitzungen.",
|
|
1083
1127
|
autoInstallQ: " Darf ich Tools automatisch installieren? (brew, pip, npm)\n 1. Ja\n 2. Frag mich vorher\n > ",
|
|
1084
1128
|
autoInstallYes: "Auto-Installation aktiviert.",
|
|
1085
1129
|
autoInstallNo: "Frage vorher.",
|
|
@@ -1112,9 +1156,6 @@ async function main() {
|
|
|
1112
1156
|
dashboardQ: " Attivare la dashboard web su localhost:6174?\n (UI sempre attiva per esplorare memoria, sessioni e salute del sistema)\n 1. Sì\n 2. No\n > ",
|
|
1113
1157
|
dashYes: "Dashboard attivata.",
|
|
1114
1158
|
dashNo: "Dashboard disattivata. Avvio manuale: nexo dashboard",
|
|
1115
|
-
orchestratorQ: " Attivare la modalità autonoma? (Lavoro da solo ogni 15 min: followup, email, infra, report via email)\n 1. Sì\n 2. No\n > ",
|
|
1116
|
-
orchYes: "Modalità autonoma attivata. Lavoro per te dalle 8:00 alle 23:00.",
|
|
1117
|
-
orchNo: "Modalità autonoma disattivata. Lavoro solo nelle sessioni.",
|
|
1118
1159
|
autoInstallQ: " Posso installare strumenti automaticamente? (brew, pip, npm)\n 1. Sì\n 2. Chiedimi prima\n > ",
|
|
1119
1160
|
autoInstallYes: "Auto-installazione attivata.",
|
|
1120
1161
|
autoInstallNo: "Chiederò prima.",
|
|
@@ -1147,9 +1188,6 @@ async function main() {
|
|
|
1147
1188
|
dashboardQ: " Ativar dashboard web em localhost:6174?\n (UI sempre ativa para explorar memória, sessões e saúde do sistema)\n 1. Sim\n 2. Não\n > ",
|
|
1148
1189
|
dashYes: "Dashboard ativado.",
|
|
1149
1190
|
dashNo: "Dashboard desativado. Iniciar manualmente: nexo dashboard",
|
|
1150
|
-
orchestratorQ: " Ativar modo autônomo? (Trabalho sozinho a cada 15 min: followups, emails, infra, relatório por email)\n 1. Sim\n 2. Não\n > ",
|
|
1151
|
-
orchYes: "Modo autônomo ativado. Trabalho para você das 8:00 às 23:00.",
|
|
1152
|
-
orchNo: "Modo autônomo desativado. Trabalho apenas nas sessões.",
|
|
1153
1191
|
autoInstallQ: " Posso instalar ferramentas automaticamente? (brew, pip, npm)\n 1. Sim\n 2. Pergunta antes\n > ",
|
|
1154
1192
|
autoInstallYes: "Auto-instalação ativada.",
|
|
1155
1193
|
autoInstallNo: "Perguntarei antes.",
|
|
@@ -1269,7 +1307,6 @@ async function main() {
|
|
|
1269
1307
|
let doScan = false;
|
|
1270
1308
|
let doCaffeinate = false;
|
|
1271
1309
|
let doDashboard = false;
|
|
1272
|
-
let doOrchestrator = false;
|
|
1273
1310
|
let autoInstall = "ask";
|
|
1274
1311
|
if (!useDefaults) {
|
|
1275
1312
|
const scanAnswer = await ask(t.scanQ);
|
|
@@ -1290,12 +1327,6 @@ async function main() {
|
|
|
1290
1327
|
log(doDashboard ? `✓ ${t.dashYes}` : t.dashNo);
|
|
1291
1328
|
console.log("");
|
|
1292
1329
|
|
|
1293
|
-
// Step 6c: Day Orchestrator — autonomous NEXO cycles
|
|
1294
|
-
const orchAnswer = await ask(t.orchestratorQ);
|
|
1295
|
-
doOrchestrator = orchAnswer.trim() === "1" || orchAnswer.trim().toLowerCase().startsWith("y") || orchAnswer.trim().toLowerCase().startsWith("s");
|
|
1296
|
-
log(doOrchestrator ? `✓ ${t.orchYes}` : t.orchNo);
|
|
1297
|
-
console.log("");
|
|
1298
|
-
|
|
1299
1330
|
// Step 7: Auto-install permission (P11)
|
|
1300
1331
|
const autoInstallAnswer = await ask(t.autoInstallQ);
|
|
1301
1332
|
autoInstall = (autoInstallAnswer.trim() === "1" || autoInstallAnswer.trim().toLowerCase().startsWith("y") || autoInstallAnswer.trim().toLowerCase().startsWith("s")) ? "auto" : "ask";
|
|
@@ -1370,7 +1401,7 @@ async function main() {
|
|
|
1370
1401
|
objective: "Improve operational excellence and reduce repeated errors",
|
|
1371
1402
|
focus_areas: ["error_prevention", "proactivity", "memory_quality"],
|
|
1372
1403
|
evolution_enabled: true,
|
|
1373
|
-
evolution_mode: "
|
|
1404
|
+
evolution_mode: "auto",
|
|
1374
1405
|
dimensions: {
|
|
1375
1406
|
episodic_memory: { current: 0, target: 90 },
|
|
1376
1407
|
autonomy: { current: 0, target: 80 },
|
|
@@ -1502,6 +1533,7 @@ async function main() {
|
|
|
1502
1533
|
fs.readdirSync(scriptsDest).filter(f => f.endsWith(".sh")).forEach(f => {
|
|
1503
1534
|
fs.chmodSync(path.join(scriptsDest, f), "755");
|
|
1504
1535
|
});
|
|
1536
|
+
syncWatchdogHashRegistry(NEXO_HOME);
|
|
1505
1537
|
}
|
|
1506
1538
|
|
|
1507
1539
|
// Core skills are shipped separately from personal skills.
|
|
@@ -2033,10 +2065,10 @@ ${doScan ? `- Stack: ${Object.keys(profileData.code.languages || {}).slice(0, 5)
|
|
|
2033
2065
|
fs.writeFileSync(CLAUDE_SETTINGS, JSON.stringify(settings, null, 2));
|
|
2034
2066
|
log("MCP server + 8 core hooks configured in Claude Code settings.");
|
|
2035
2067
|
|
|
2036
|
-
// Step 7: Create schedule.json (only on fresh install) and install
|
|
2068
|
+
// Step 7: Create schedule.json (only on fresh install) and install core processes
|
|
2037
2069
|
log("Setting up automated processes...");
|
|
2038
2070
|
const schedule = loadOrCreateSchedule(NEXO_HOME);
|
|
2039
|
-
const enabledOptionals = { dashboard: doDashboard
|
|
2071
|
+
const enabledOptionals = { dashboard: doDashboard };
|
|
2040
2072
|
if (isEphemeralInstall(NEXO_HOME)) {
|
|
2041
2073
|
log("Ephemeral HOME/NEXO_HOME detected — skipping LaunchAgents installation.");
|
|
2042
2074
|
} else {
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "diff -q \"${CLAUDE_PLUGIN_ROOT}/src/requirements.txt\" \"${CLAUDE_PLUGIN_DATA}/requirements.txt\" >/dev/null 2>&1 || (python3 -m venv \"${CLAUDE_PLUGIN_DATA}/.venv\" 2>/dev/null; cp \"${CLAUDE_PLUGIN_ROOT}/src/requirements.txt\" \"${CLAUDE_PLUGIN_DATA}/requirements.txt\"; \"${CLAUDE_PLUGIN_DATA}/.venv/bin/pip\" install --quiet -r \"${CLAUDE_PLUGIN_DATA}/requirements.txt\") || rm -f \"${CLAUDE_PLUGIN_DATA}/requirements.txt\""
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"mcpName": "io.github.wazionapps/nexo",
|
|
5
|
-
"description": "NEXO — Cognitive co-operator for Claude Code. Memory, emotional intelligence, overnight learning (Deep Sleep), cron management, trust scoring, and adaptive calibration.",
|
|
5
|
+
"description": "NEXO — Cognitive co-operator for Claude Code. Memory, emotional intelligence, overnight learning (Deep Sleep), personal scripts registry, cron management, trust scoring, managed evolution, and adaptive calibration.",
|
|
6
6
|
"bin": {
|
|
7
7
|
"nexo-brain": "./bin/nexo-brain.js",
|
|
8
8
|
"nexo": "./bin/nexo.js"
|
|
@@ -39,7 +39,16 @@
|
|
|
39
39
|
"cron-manifest",
|
|
40
40
|
"session-tone",
|
|
41
41
|
"mood-tracking",
|
|
42
|
-
"productivity-analysis"
|
|
42
|
+
"productivity-analysis",
|
|
43
|
+
"runtime-cli",
|
|
44
|
+
"doctor",
|
|
45
|
+
"executable-skills",
|
|
46
|
+
"work-continuity",
|
|
47
|
+
"personal-scripts",
|
|
48
|
+
"script-registry",
|
|
49
|
+
"claude-code-plugin",
|
|
50
|
+
"managed-evolution",
|
|
51
|
+
"nexo-chat"
|
|
43
52
|
],
|
|
44
53
|
"author": "NEXO Brain <info@nexo-brain.com>",
|
|
45
54
|
"license": "AGPL-3.0",
|
|
@@ -63,6 +72,9 @@
|
|
|
63
72
|
"!src/**/*.pyc",
|
|
64
73
|
"!src/**/*.pyo",
|
|
65
74
|
"templates/",
|
|
75
|
+
".claude-plugin/",
|
|
76
|
+
".mcp.json",
|
|
77
|
+
"hooks/hooks.json",
|
|
66
78
|
"README.md",
|
|
67
79
|
"LICENSE"
|
|
68
80
|
]
|
package/src/auto_update.py
CHANGED
|
@@ -8,6 +8,7 @@ This is separate from plugins/update.py which handles MANUAL updates with rollba
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import json
|
|
11
|
+
import hashlib
|
|
11
12
|
import os
|
|
12
13
|
import re
|
|
13
14
|
import subprocess
|
|
@@ -56,6 +57,53 @@ def _write_last_check(data: dict):
|
|
|
56
57
|
_log(f"Failed to write last-check file: {e}")
|
|
57
58
|
|
|
58
59
|
|
|
60
|
+
def _sync_watchdog_hash_registry():
|
|
61
|
+
"""Keep the immutable-hash registry aligned with the installed watchdog script."""
|
|
62
|
+
try:
|
|
63
|
+
watchdog_file = NEXO_HOME / "scripts" / "nexo-watchdog.sh"
|
|
64
|
+
if not watchdog_file.exists():
|
|
65
|
+
return
|
|
66
|
+
registry_file = NEXO_HOME / "scripts" / ".watchdog-hashes"
|
|
67
|
+
entries: dict[str, str] = {}
|
|
68
|
+
if registry_file.exists():
|
|
69
|
+
for line in registry_file.read_text().splitlines():
|
|
70
|
+
if "|" not in line:
|
|
71
|
+
continue
|
|
72
|
+
filepath, expected = line.split("|", 1)
|
|
73
|
+
if filepath:
|
|
74
|
+
entries[filepath] = expected
|
|
75
|
+
actual_hash = hashlib.sha256(watchdog_file.read_bytes()).hexdigest()
|
|
76
|
+
entries[str(watchdog_file)] = actual_hash
|
|
77
|
+
registry_file.write_text(
|
|
78
|
+
"\n".join(f"{filepath}|{digest}" for filepath, digest in sorted(entries.items())) + "\n"
|
|
79
|
+
)
|
|
80
|
+
except Exception as e:
|
|
81
|
+
_log(f"watchdog hash registry sync error: {e}")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _warn_protected_runtime_location():
|
|
85
|
+
"""Log a targeted macOS TCC warning for risky NEXO_HOME locations."""
|
|
86
|
+
if sys.platform != "darwin":
|
|
87
|
+
return
|
|
88
|
+
try:
|
|
89
|
+
home = Path.home()
|
|
90
|
+
resolved = NEXO_HOME.resolve(strict=False)
|
|
91
|
+
protected_roots = (
|
|
92
|
+
home / "Documents",
|
|
93
|
+
home / "Desktop",
|
|
94
|
+
home / "Downloads",
|
|
95
|
+
home / "Library" / "Mobile Documents",
|
|
96
|
+
)
|
|
97
|
+
if any(resolved == root or root in resolved.parents for root in protected_roots):
|
|
98
|
+
_log(
|
|
99
|
+
"NEXO_HOME is inside a macOS protected folder. Background jobs may need Full Disk Access "
|
|
100
|
+
"for /bin/bash and the NEXO Python runtime, or NEXO_HOME should be moved outside "
|
|
101
|
+
"Documents/Desktop/Downloads/iCloud Drive."
|
|
102
|
+
)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
_log(f"protected runtime warning skipped: {e}")
|
|
105
|
+
|
|
106
|
+
|
|
59
107
|
def _is_git_repo() -> bool:
|
|
60
108
|
"""Check if REPO_DIR is inside a git repository."""
|
|
61
109
|
try:
|
|
@@ -205,6 +253,24 @@ def _refresh_installed_manifest():
|
|
|
205
253
|
_log(f"Manifest refresh warning: {e}")
|
|
206
254
|
|
|
207
255
|
|
|
256
|
+
def _cleanup_retired_runtime_files():
|
|
257
|
+
"""Remove retired core files that should not survive updates."""
|
|
258
|
+
retired = [
|
|
259
|
+
NEXO_HOME / "scripts" / "nexo-day-orchestrator.sh",
|
|
260
|
+
]
|
|
261
|
+
for target in retired:
|
|
262
|
+
try:
|
|
263
|
+
if target.exists():
|
|
264
|
+
if target.is_dir():
|
|
265
|
+
import shutil
|
|
266
|
+
shutil.rmtree(target)
|
|
267
|
+
else:
|
|
268
|
+
target.unlink()
|
|
269
|
+
_log(f"Removed retired runtime file: {target.name}")
|
|
270
|
+
except Exception as e:
|
|
271
|
+
_log(f"Retired runtime cleanup warning ({target.name}): {e}")
|
|
272
|
+
|
|
273
|
+
|
|
208
274
|
def _sync_crons():
|
|
209
275
|
"""Sync cron definitions with manifest after a git pull."""
|
|
210
276
|
try:
|
|
@@ -219,6 +285,7 @@ def _sync_crons():
|
|
|
219
285
|
_log(f"Cron sync failed (exit {result.returncode}): {result.stderr or result.stdout}")
|
|
220
286
|
return # Don't refresh manifest if timers weren't actually updated
|
|
221
287
|
_log("Synced cron definitions with manifest")
|
|
288
|
+
_cleanup_retired_runtime_files()
|
|
222
289
|
# Refresh the installed manifest only after successful sync
|
|
223
290
|
_refresh_installed_manifest()
|
|
224
291
|
except Exception as e:
|
|
@@ -811,13 +878,14 @@ def auto_update_check() -> dict:
|
|
|
811
878
|
# Backfill evolution-objective.json for existing installs
|
|
812
879
|
try:
|
|
813
880
|
evo_obj_path = NEXO_HOME / "brain" / "evolution-objective.json"
|
|
881
|
+
from evolution_cycle import normalize_objective
|
|
814
882
|
if not evo_obj_path.exists():
|
|
815
883
|
(NEXO_HOME / "brain").mkdir(parents=True, exist_ok=True)
|
|
816
884
|
default_objective = {
|
|
817
885
|
"objective": "Improve operational excellence and reduce repeated errors",
|
|
818
886
|
"focus_areas": ["error_prevention", "proactivity", "memory_quality"],
|
|
819
887
|
"evolution_enabled": True,
|
|
820
|
-
"evolution_mode": "
|
|
888
|
+
"evolution_mode": "auto",
|
|
821
889
|
"dimensions": {
|
|
822
890
|
"episodic_memory": {"current": 0, "target": 90},
|
|
823
891
|
"autonomy": {"current": 0, "target": 80},
|
|
@@ -831,6 +899,12 @@ def auto_update_check() -> dict:
|
|
|
831
899
|
}
|
|
832
900
|
evo_obj_path.write_text(json.dumps(default_objective, indent=2))
|
|
833
901
|
_log("Backfilled evolution-objective.json for existing install")
|
|
902
|
+
else:
|
|
903
|
+
raw_objective = json.loads(evo_obj_path.read_text())
|
|
904
|
+
normalized = normalize_objective(raw_objective)
|
|
905
|
+
if normalized != raw_objective:
|
|
906
|
+
evo_obj_path.write_text(json.dumps(normalized, indent=2, ensure_ascii=False))
|
|
907
|
+
_log("Normalized legacy evolution-objective.json")
|
|
834
908
|
except Exception as e:
|
|
835
909
|
_log(f"evolution-objective.json backfill error: {e}")
|
|
836
910
|
|
|
@@ -853,9 +927,12 @@ def auto_update_check() -> dict:
|
|
|
853
927
|
except Exception as e:
|
|
854
928
|
_log(f"scripts backfill error: {e}")
|
|
855
929
|
|
|
930
|
+
_sync_watchdog_hash_registry()
|
|
931
|
+
_warn_protected_runtime_location()
|
|
932
|
+
|
|
856
933
|
# Backfill runtime CLI modules for existing installs
|
|
857
934
|
try:
|
|
858
|
-
for fname in ("cli.py", "script_registry.py", "skills_runtime.py"):
|
|
935
|
+
for fname in ("cli.py", "script_registry.py", "skills_runtime.py", "cron_recovery.py"):
|
|
859
936
|
src_file = SRC_DIR / fname
|
|
860
937
|
dest_file = NEXO_HOME / fname
|
|
861
938
|
if src_file.is_file() and (not dest_file.exists() or src_file.stat().st_mtime > dest_file.stat().st_mtime):
|