nexo-brain 2.6.1 → 2.6.3

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "2.5.1",
3
+ "version": "2.6.3",
4
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
5
  "author": {
6
6
  "name": "NEXO Brain",
package/README.md CHANGED
@@ -280,7 +280,7 @@ NEXO Brain doesn't just respond — it runs 14 autonomous processes in the backg
280
280
  | **postmortem** | 23:30 daily | Session consolidation, extract patterns from day's events |
281
281
  | **catchup** | On boot | Runs any missed scheduled processes (Mac was off/asleep) |
282
282
  | **tcc-approve** | On boot (macOS) | Auto-approve macOS permissions for Claude Code updates |
283
- | **prevent-sleep** | Always (daemon) | Keeps machine awake for nocturnal processes (caffeinate/systemd-inhibit) |
283
+ | **prevent-sleep** | Optional opt-in daemon | Keeps machine awake for nocturnal processes when `power_policy=always_on` (caffeinate/systemd-inhibit) |
284
284
  | **evolution** | Weekly (Sun) | Self-improvement proposals — NEXO suggests and applies enhancements |
285
285
  | **followup-hygiene** | Weekly (Sun) | Normalizes statuses, flags stale followups, cleans orphans |
286
286
  | **learning-housekeep** | 03:15 daily | Dedup learnings, adjust weights by usage, process overdue reviews, reconcile decision outcomes |
@@ -289,7 +289,7 @@ NEXO Brain doesn't just respond — it runs 14 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 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.
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. `prevent-sleep` is opt-in via the persisted power policy (`always_on` / `disabled` / `unset`). Personal scripts (your own automations) are tracked separately in the Personal Scripts Registry, can declare their own recovery policy inline, and are never touched by the core sync. If your Mac was asleep during a scheduled process, the catch-up system can now recover both core crons and managed personal schedules according to their recovery contract.
293
293
 
294
294
  ## Deep Sleep v2 — Overnight Learning (v2.1.0)
295
295
 
package/bin/nexo-brain.js CHANGED
@@ -226,9 +226,11 @@ function registerAllCoreHooks(settings, hooksDir, nexoHome) {
226
226
  // We need to search and update in both formats.
227
227
  let found = false;
228
228
 
229
- for (const entry of settings.hooks[hook.event]) {
229
+ for (let idx = 0; idx < settings.hooks[hook.event].length; idx++) {
230
+ const entry = settings.hooks[hook.event][idx];
230
231
  if (entry.hooks && Array.isArray(entry.hooks)) {
231
232
  // Nested format: {matcher, hooks: [...]}
233
+ if (!entry.matcher) entry.matcher = "*";
232
234
  const subIdx = entry.hooks.findIndex(
233
235
  (h) => h.command && h.command.includes(hook.key)
234
236
  );
@@ -240,19 +242,25 @@ function registerAllCoreHooks(settings, hooksDir, nexoHome) {
240
242
  break;
241
243
  }
242
244
  } else if (entry.command && entry.command.includes(hook.key)) {
243
- // Flat format: {type:"command", command:"..."}
244
- if (entry.command !== command) entry.command = command;
245
- if (hook.timeout && !entry.timeout) entry.timeout = hook.timeout;
245
+ // Legacy flat format: migrate to nested matcher+hooks.
246
+ const migrated = { type: "command", command };
247
+ if (hook.timeout) migrated.timeout = hook.timeout;
248
+ settings.hooks[hook.event][idx] = {
249
+ matcher: "*",
250
+ hooks: [migrated],
251
+ };
246
252
  found = true;
247
253
  break;
248
254
  }
249
255
  }
250
256
 
251
257
  if (!found) {
252
- // Hook missing add it in flat format (Claude Code accepts both)
253
- const newEntry = { type: "command", command };
254
- if (hook.timeout) newEntry.timeout = hook.timeout;
255
- settings.hooks[hook.event].push(newEntry);
258
+ const newHook = { type: "command", command };
259
+ if (hook.timeout) newHook.timeout = hook.timeout;
260
+ settings.hooks[hook.event].push({
261
+ matcher: "*",
262
+ hooks: [newHook],
263
+ });
256
264
  }
257
265
  }
258
266
  }
@@ -286,6 +294,8 @@ function getDefaultSchedule(timezone) {
286
294
  return {
287
295
  timezone: timezone || "UTC",
288
296
  auto_update: true,
297
+ power_policy: "unset",
298
+ power_policy_version: 1,
289
299
  processes: {
290
300
  "cognitive-decay": { hour: 3, minute: 0 },
291
301
  "postmortem": { hour: 23, minute: 30 },
@@ -298,6 +308,33 @@ function getDefaultSchedule(timezone) {
298
308
  };
299
309
  }
300
310
 
311
+ async function maybeConfigurePowerPolicy(schedule, useDefaults) {
312
+ const current = String((schedule && schedule.power_policy) || "unset").toLowerCase();
313
+ if (current && current !== "unset") {
314
+ return schedule;
315
+ }
316
+ if (useDefaults || !process.stdin.isTTY || !process.stdout.isTTY) {
317
+ schedule.power_policy = "unset";
318
+ schedule.power_policy_version = 1;
319
+ return schedule;
320
+ }
321
+
322
+ console.log("");
323
+ log("Optional power policy:");
324
+ log("If enabled, NEXO will try to keep the machine awake for background work.");
325
+ const answer = (await ask(" Keep this machine awake for background work? [y/N/later]: ")).trim().toLowerCase();
326
+ if (answer === "y" || answer === "yes") {
327
+ schedule.power_policy = "always_on";
328
+ } else if (answer === "later" || answer === "l") {
329
+ schedule.power_policy = "unset";
330
+ } else {
331
+ schedule.power_policy = "disabled";
332
+ }
333
+ schedule.power_policy_version = 1;
334
+ fs.writeFileSync(path.join(NEXO_HOME, "config", "schedule.json"), JSON.stringify(schedule, null, 2));
335
+ return schedule;
336
+ }
337
+
301
338
  /**
302
339
  * Resolve the venv python path for an existing NEXO_HOME installation.
303
340
  */
@@ -370,6 +407,9 @@ function installAllProcesses(platform, pythonPath, nexoHome, schedule, launchAge
370
407
  const sPath = scriptPath(proc);
371
408
  const interp = interpreterPath(proc);
372
409
  const s = getSchedule(proc);
410
+ if (proc.name === "prevent-sleep" && (schedule.power_policy || "unset") !== "always_on") {
411
+ continue;
412
+ }
373
413
 
374
414
  let scheduleBlock = "";
375
415
  if (proc.type === "keepAlive") {
@@ -477,6 +517,7 @@ function installAllProcesses(platform, pythonPath, nexoHome, schedule, launchAge
477
517
  const sPath = scriptPath(proc);
478
518
  const interp = interpreterPath(proc);
479
519
  const s = getSchedule(proc);
520
+ if (proc.name === "prevent-sleep" && (schedule.power_policy || "unset") !== "always_on") continue;
480
521
 
481
522
  const serviceType = proc.type === "keepAlive" ? "simple" : "oneshot";
482
523
  const restartPolicy = proc.type === "keepAlive" ? "Restart=always\nRestartSec=5" : "";
@@ -795,7 +836,8 @@ async function main() {
795
836
  log(" All 8 core hooks registered in Claude Code settings.");
796
837
 
797
838
  // Regenerate all core LaunchAgents / systemd timers
798
- const migSchedule = loadOrCreateSchedule(NEXO_HOME);
839
+ let migSchedule = loadOrCreateSchedule(NEXO_HOME);
840
+ migSchedule = await maybeConfigurePowerPolicy(migSchedule, useDefaults);
799
841
  const migPython = findVenvPython(NEXO_HOME) || "python3";
800
842
  let migOptionals = {};
801
843
  try {
@@ -2067,7 +2109,8 @@ ${doScan ? `- Stack: ${Object.keys(profileData.code.languages || {}).slice(0, 5)
2067
2109
 
2068
2110
  // Step 7: Create schedule.json (only on fresh install) and install core processes
2069
2111
  log("Setting up automated processes...");
2070
- const schedule = loadOrCreateSchedule(NEXO_HOME);
2112
+ let schedule = loadOrCreateSchedule(NEXO_HOME);
2113
+ schedule = await maybeConfigurePowerPolicy(schedule, useDefaults);
2071
2114
  const enabledOptionals = { dashboard: doDashboard };
2072
2115
  if (isEphemeralInstall(NEXO_HOME)) {
2073
2116
  log("Ephemeral HOME/NEXO_HOME detected — skipping LaunchAgents installation.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "2.6.1",
3
+ "version": "2.6.3",
4
4
  "mcpName": "io.github.wazionapps/nexo",
5
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": {