nexo-brain 5.3.2 → 5.3.4
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 +1 -1
- package/src/auto_update.py +13 -0
- package/src/cli.py +66 -0
- package/src/doctor/providers/runtime.py +1 -1
- package/src/script_registry.py +4 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.4",
|
|
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
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
|
|
19
19
|
[Watch the overview video](https://nexo-brain.com/watch/) · [Watch on YouTube](https://www.youtube.com/watch?v=i2lkGhKyVqI) · [Open the infographic](https://nexo-brain.com/assets/nexo-brain-infographic-v5.png)
|
|
20
20
|
|
|
21
|
+
Version `5.3.4` closes the last packaged-runtime core/personal leak from the hook migration work: legacy hook aliases stay out of the personal bucket, `nexo update` removes those retired aliases when the canonical hooks already exist, and both `nexo` and `nexo chat` now show latest vs installed version at a glance.
|
|
22
|
+
|
|
21
23
|
Start here:
|
|
22
24
|
- [5-minute quickstart](docs/quickstart-5-minutes.md)
|
|
23
25
|
- [Workflow quickstart](docs/workflows-quickstart.md)
|
|
@@ -87,7 +89,7 @@ Versions `3.1.7` through `3.2.0` close the recent-memory gap:
|
|
|
87
89
|
- when even that misses, NEXO now exposes raw transcript fallback tools for Claude Code and Codex session stores
|
|
88
90
|
- 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
91
|
|
|
90
|
-
Version `5.3.
|
|
92
|
+
Version `5.3.3` closes the remaining packaged-runtime doctor mismatch: the built-in hourly backup helper is now inventoried as a core LaunchAgent, so clean installs no longer get a false unknown-LaunchAgent warning. Version `5.3.2` already hardened the runtime boundary by persisting which runtime scripts/hooks are core product artifacts, keeping `nexo scripts` from mixing those into the personal bucket, and migrating the legacy Claude Code heartbeat wrappers into managed core hooks.
|
|
91
93
|
|
|
92
94
|
Version `5.3.1` normalizes packaged npm installs so they behave like packaged npm installs: `nexo update` now keeps the runtime anchored to `~/.nexo`, refreshes packaged bootstrap/client artifacts after upgrade, avoids repo-only release-artifact drift in installed runtimes, and keeps personal scripts on the canonical packaged path.
|
|
93
95
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.4",
|
|
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",
|
package/src/auto_update.py
CHANGED
|
@@ -364,6 +364,12 @@ def _cleanup_retired_runtime_files():
|
|
|
364
364
|
NEXO_HOME / "scripts" / "heartbeat-user-msg.sh",
|
|
365
365
|
NEXO_HOME / "hooks" / "heartbeat-guard.sh",
|
|
366
366
|
]
|
|
367
|
+
conditional_retired = [
|
|
368
|
+
(NEXO_HOME / "scripts" / "nexo-postcompact.sh", NEXO_HOME / "hooks" / "post-compact.sh"),
|
|
369
|
+
(NEXO_HOME / "scripts" / "nexo-memory-precompact.sh", NEXO_HOME / "hooks" / "pre-compact.sh"),
|
|
370
|
+
(NEXO_HOME / "scripts" / "nexo-memory-stop.sh", NEXO_HOME / "hooks" / "session-stop.sh"),
|
|
371
|
+
(NEXO_HOME / "scripts" / "nexo-session-briefing.sh", NEXO_HOME / "hooks" / "session-start.sh"),
|
|
372
|
+
]
|
|
367
373
|
for target in retired:
|
|
368
374
|
try:
|
|
369
375
|
if target.exists():
|
|
@@ -375,6 +381,13 @@ def _cleanup_retired_runtime_files():
|
|
|
375
381
|
_log(f"Removed retired runtime file: {target.name}")
|
|
376
382
|
except Exception as e:
|
|
377
383
|
_log(f"Retired runtime cleanup warning ({target.name}): {e}")
|
|
384
|
+
for target, canonical in conditional_retired:
|
|
385
|
+
try:
|
|
386
|
+
if target.exists() and canonical.exists():
|
|
387
|
+
target.unlink()
|
|
388
|
+
_log(f"Removed retired runtime alias: {target.name} (canonical: {canonical.name})")
|
|
389
|
+
except Exception as e:
|
|
390
|
+
_log(f"Retired runtime alias cleanup warning ({target.name}): {e}")
|
|
378
391
|
|
|
379
392
|
|
|
380
393
|
def _sync_crons():
|
package/src/cli.py
CHANGED
|
@@ -38,6 +38,7 @@ import os
|
|
|
38
38
|
import shutil
|
|
39
39
|
import subprocess
|
|
40
40
|
import sys
|
|
41
|
+
import time
|
|
41
42
|
from pathlib import Path
|
|
42
43
|
|
|
43
44
|
from runtime_home import export_resolved_nexo_home
|
|
@@ -49,6 +50,8 @@ TERMINAL_CLIENT_LABELS = {
|
|
|
49
50
|
"codex": "Codex",
|
|
50
51
|
}
|
|
51
52
|
TERMINAL_CLIENT_ORDER = ("claude_code", "codex")
|
|
53
|
+
VERSION_STATUS_CACHE = NEXO_HOME / "config" / "cli-version-status.json"
|
|
54
|
+
LATEST_NPM_PACKAGE = "nexo-brain"
|
|
52
55
|
|
|
53
56
|
|
|
54
57
|
def _get_version() -> str:
|
|
@@ -67,6 +70,67 @@ def _get_version() -> str:
|
|
|
67
70
|
continue
|
|
68
71
|
return "?"
|
|
69
72
|
|
|
73
|
+
|
|
74
|
+
def _load_latest_version_cache(max_age_seconds: int = 6 * 3600) -> str | None:
|
|
75
|
+
try:
|
|
76
|
+
payload = json.loads(VERSION_STATUS_CACHE.read_text())
|
|
77
|
+
except Exception:
|
|
78
|
+
return None
|
|
79
|
+
version = str(payload.get("latest", "")).strip()
|
|
80
|
+
checked_at = float(payload.get("checked_at", 0) or 0)
|
|
81
|
+
if not version:
|
|
82
|
+
return None
|
|
83
|
+
if checked_at and (time.time() - checked_at) > max_age_seconds:
|
|
84
|
+
return None
|
|
85
|
+
return version
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _save_latest_version_cache(version: str) -> None:
|
|
89
|
+
VERSION_STATUS_CACHE.parent.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
VERSION_STATUS_CACHE.write_text(json.dumps({
|
|
91
|
+
"latest": version,
|
|
92
|
+
"checked_at": time.time(),
|
|
93
|
+
}))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _fetch_latest_version(timeout_seconds: int = 2) -> str | None:
|
|
97
|
+
try:
|
|
98
|
+
result = subprocess.run(
|
|
99
|
+
["npm", "view", LATEST_NPM_PACKAGE, "version"],
|
|
100
|
+
capture_output=True,
|
|
101
|
+
text=True,
|
|
102
|
+
timeout=timeout_seconds,
|
|
103
|
+
)
|
|
104
|
+
except Exception:
|
|
105
|
+
return None
|
|
106
|
+
if result.returncode != 0:
|
|
107
|
+
return None
|
|
108
|
+
latest = result.stdout.strip()
|
|
109
|
+
if not latest:
|
|
110
|
+
return None
|
|
111
|
+
try:
|
|
112
|
+
_save_latest_version_cache(latest)
|
|
113
|
+
except Exception:
|
|
114
|
+
pass
|
|
115
|
+
return latest
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _should_refresh_latest_version() -> bool:
|
|
119
|
+
try:
|
|
120
|
+
return sys.stdout.isatty() or sys.stderr.isatty()
|
|
121
|
+
except Exception:
|
|
122
|
+
return False
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _version_status_line() -> str:
|
|
126
|
+
installed = _get_version()
|
|
127
|
+
latest = _load_latest_version_cache()
|
|
128
|
+
if latest is None and _should_refresh_latest_version():
|
|
129
|
+
latest = _fetch_latest_version()
|
|
130
|
+
if latest:
|
|
131
|
+
return f"NEXO Latest: v{latest} | Installed: v{installed}"
|
|
132
|
+
return f"NEXO Installed: v{installed}"
|
|
133
|
+
|
|
70
134
|
# Ensure src/ is on path for imports
|
|
71
135
|
if str(NEXO_CODE) not in sys.path:
|
|
72
136
|
sys.path.insert(0, str(NEXO_CODE))
|
|
@@ -1011,6 +1075,7 @@ def _prompt_for_terminal_client(
|
|
|
1011
1075
|
def _chat(args):
|
|
1012
1076
|
target = args.path or "."
|
|
1013
1077
|
selected_client = getattr(args, "client", None)
|
|
1078
|
+
print(f"[NEXO] {_version_status_line()}", file=sys.stderr)
|
|
1014
1079
|
|
|
1015
1080
|
try:
|
|
1016
1081
|
from auto_update import startup_preflight
|
|
@@ -1499,6 +1564,7 @@ def _uninstall(args):
|
|
|
1499
1564
|
def _print_help():
|
|
1500
1565
|
v = _get_version()
|
|
1501
1566
|
print(f"""NEXO Runtime CLI v{v}
|
|
1567
|
+
{_version_status_line()}
|
|
1502
1568
|
|
|
1503
1569
|
Commands:
|
|
1504
1570
|
nexo chat [path] [--client claude_code|codex] Launch a NEXO terminal client
|
|
@@ -42,7 +42,7 @@ IMMUNE_FRESHNESS = 3600 # 1 hour (runs every 30 min)
|
|
|
42
42
|
WATCHDOG_FRESHNESS = 3600 # 1 hour (runs every 30 min)
|
|
43
43
|
DEFAULT_CRON_THRESHOLD = 7200 # Fallback when manifest data is unavailable
|
|
44
44
|
LIVE_PROTOCOL_SESSION_FRESHNESS = 1800 # 30 minutes
|
|
45
|
-
AUXILIARY_CORE_LAUNCHAGENT_IDS = {"dashboard", "prevent-sleep", "tcc-approve"}
|
|
45
|
+
AUXILIARY_CORE_LAUNCHAGENT_IDS = {"backup", "dashboard", "prevent-sleep", "tcc-approve"}
|
|
46
46
|
SPECIAL_LAUNCHAGENT_IDS = {"prevent-sleep", "tcc-approve"}
|
|
47
47
|
SPECIAL_ENV_NORMALIZE_IDS = SPECIAL_LAUNCHAGENT_IDS
|
|
48
48
|
OPTIONALS_FILE = NEXO_HOME / "config" / "optionals.json"
|
package/src/script_registry.py
CHANGED
|
@@ -50,6 +50,10 @@ _LEGACY_CORE_RUNTIME_FILES = {
|
|
|
50
50
|
"heartbeat-enforcement.py",
|
|
51
51
|
"heartbeat-posttool.sh",
|
|
52
52
|
"heartbeat-user-msg.sh",
|
|
53
|
+
"nexo-memory-precompact.sh",
|
|
54
|
+
"nexo-memory-stop.sh",
|
|
55
|
+
"nexo-postcompact.sh",
|
|
56
|
+
"nexo-session-briefing.sh",
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
# Forbidden patterns — direct DB access from personal scripts
|