nexo-brain 5.0.0 → 5.0.1

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": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "Local cognitive runtime for Claude Code \u2014 persistent memory, overnight learning, doctor diagnostics, personal scripts, recovery-aware jobs, startup preflight, and optional dashboard/power helper.",
5
5
  "author": {
6
6
  "name": "NEXO Brain",
package/README.md CHANGED
@@ -87,6 +87,12 @@ Versions `3.1.7` through `3.2.0` close the recent-memory gap:
87
87
  - when even that misses, NEXO now exposes raw transcript fallback tools for Claude Code and Codex session stores
88
88
  - NEXO can now inspect itself through a live system catalog derived from canonical sources instead of relying only on stale docs or operator memory
89
89
 
90
+ Version `5.0.1` hardens the live 5.0 upgrade path:
91
+
92
+ - managed Claude Code hooks are now cleaned up when an older release left obsolete core-managed entries behind
93
+ - upgrades no longer preserve the stale `heartbeat-guard.sh` path that could create warning storms and fake "hung" symptoms after `nexo update`
94
+ - the corrected path has been revalidated on a real install with `nexo clients sync`, Codex/Claude Code headless runtime access, email-monitor recovery, and a full `nexo update`
95
+
90
96
  Version `5.0.0` closes the loop between memory, decisions, outcomes, and reusable behavior:
91
97
 
92
98
  - goal profiles are now explicit and auditable instead of living as hidden heuristics
@@ -702,7 +708,7 @@ The installer handles everything and syncs the same `nexo` MCP brain into Claude
702
708
  - Node.js project detected
703
709
  Configuring MCP server...
704
710
  Setting up nervous system...
705
- 13 core recovery-aware jobs configured.
711
+ 15 core recovery-aware jobs configured.
706
712
  Dashboard configured at localhost:6174.
707
713
  Caffeinate enabled.
708
714
  Generating operator instructions...
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "mcpName": "io.github.wazionapps/nexo",
5
5
  "description": "NEXO Brain — Shared brain for AI agents. Persistent memory, semantic RAG, natural forgetting, metacognitive guard, trust scoring, 150+ MCP tools. Works with Claude Code, Codex, Claude Desktop & any MCP client. 100% local, free.",
6
6
  "homepage": "https://nexo-brain.com",
@@ -386,6 +386,32 @@ CORE_HOOK_SPECS = [
386
386
  },
387
387
  ]
388
388
 
389
+ LEGACY_CORE_HOOK_IDENTITIES_BY_EVENT = {
390
+ "PostToolUse": {
391
+ "heartbeat-guard.sh",
392
+ },
393
+ }
394
+
395
+
396
+ def _current_core_hook_identities_by_event() -> dict[str, set[str]]:
397
+ identities: dict[str, set[str]] = {}
398
+ for spec in CORE_HOOK_SPECS:
399
+ identities.setdefault(spec["event"], set()).add(spec["identity"])
400
+ return identities
401
+
402
+
403
+ CURRENT_CORE_HOOK_IDENTITIES_BY_EVENT = _current_core_hook_identities_by_event()
404
+
405
+
406
+ def _managed_core_hook_identities_by_event() -> dict[str, set[str]]:
407
+ managed = {event: set(identities) for event, identities in CURRENT_CORE_HOOK_IDENTITIES_BY_EVENT.items()}
408
+ for event, identities in LEGACY_CORE_HOOK_IDENTITIES_BY_EVENT.items():
409
+ managed.setdefault(event, set()).update(identities)
410
+ return managed
411
+
412
+
413
+ MANAGED_CORE_HOOK_IDENTITIES_BY_EVENT = _managed_core_hook_identities_by_event()
414
+
389
415
 
390
416
  def _resolve_hook_source_dir(runtime_root: Path) -> Path:
391
417
  direct = runtime_root / "hooks"
@@ -450,6 +476,25 @@ def _merge_core_hooks(existing_hooks, *, runtime_root: Path, nexo_home: Path) ->
450
476
  hooks_dir = _resolve_hook_source_dir(runtime_root)
451
477
  managed_count = 0
452
478
 
479
+ for event, managed_identities in MANAGED_CORE_HOOK_IDENTITIES_BY_EVENT.items():
480
+ sections = _normalize_hook_sections(hooks_payload.get(event))
481
+ desired_identities = CURRENT_CORE_HOOK_IDENTITIES_BY_EVENT.get(event, set())
482
+ cleaned_sections: list[dict] = []
483
+ for section in sections:
484
+ cleaned_hooks = []
485
+ for hook in section["hooks"]:
486
+ identity = _hook_identity(hook.get("command", ""))
487
+ if identity in managed_identities and identity not in desired_identities:
488
+ continue
489
+ cleaned_hooks.append(hook)
490
+ cleaned_sections.append(
491
+ {
492
+ "matcher": section.get("matcher", "*") or "*",
493
+ "hooks": cleaned_hooks,
494
+ }
495
+ )
496
+ hooks_payload[event] = cleaned_sections
497
+
453
498
  for spec in CORE_HOOK_SPECS:
454
499
  event = spec["event"]
455
500
  sections = _normalize_hook_sections(hooks_payload.get(event))