nexo-brain 7.30.25 → 7.30.27
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 +5 -1
- package/package.json +1 -1
- package/src/auto_update.py +127 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.30.
|
|
3
|
+
"version": "7.30.27",
|
|
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,7 +18,11 @@
|
|
|
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 `7.30.
|
|
21
|
+
Version `7.30.27` is the current packaged-runtime line. Patch release over v7.30.26 - post-update repair now recovers core scripts archived by older F0.6 shim reconciliation and refreshes `core/current` from `core`, so same-version snapshots cannot keep stale watchdog code.
|
|
22
|
+
|
|
23
|
+
Previously in `7.30.26`: patch release over v7.30.25 - `nexo update` now copies packaged core scripts through the F0.6 `scripts -> core/scripts` shim, so watchdog fixes reach the active LaunchAgent path.
|
|
24
|
+
|
|
25
|
+
Previously in `7.30.25`: patch release over v7.30.24 - Desktop-facing maintenance diagnostics now self-heal or stay informational when the runtime is healthy, and watchdog uses the canonical cognitive DB layout.
|
|
22
26
|
|
|
23
27
|
Previously in `7.30.24`: patch release over v7.30.23 - client sync now stages and verifies managed MCP providers before writing Brain-owned default MCP entries into Claude, Codex, or Desktop configs.
|
|
24
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.30.
|
|
3
|
+
"version": "7.30.27",
|
|
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
|
@@ -4843,6 +4843,14 @@ def _copy_runtime_from_source(src_dir: Path, repo_dir: Path, dest: Path = NEXO_H
|
|
|
4843
4843
|
script_conflicts: list[dict[str, str]] = []
|
|
4844
4844
|
installed_script_classes = _installed_scripts_classification(dest)
|
|
4845
4845
|
|
|
4846
|
+
def _preserve_f06_legacy_shims() -> bool:
|
|
4847
|
+
if dest != NEXO_HOME:
|
|
4848
|
+
return False
|
|
4849
|
+
try:
|
|
4850
|
+
return (dest / ".structure-version").read_text(encoding="utf-8").strip() == "F0.6"
|
|
4851
|
+
except Exception:
|
|
4852
|
+
return False
|
|
4853
|
+
|
|
4846
4854
|
for dirname in ("bin", "skills", "skills-core", "skills-runtime", "templates"):
|
|
4847
4855
|
(dest / dirname).mkdir(parents=True, exist_ok=True)
|
|
4848
4856
|
|
|
@@ -4886,7 +4894,10 @@ def _copy_runtime_from_source(src_dir: Path, repo_dir: Path, dest: Path = NEXO_H
|
|
|
4886
4894
|
scripts_dest = dest / "scripts"
|
|
4887
4895
|
if scripts_src.is_dir():
|
|
4888
4896
|
if scripts_dest.is_symlink():
|
|
4889
|
-
|
|
4897
|
+
if _preserve_f06_legacy_shims():
|
|
4898
|
+
scripts_dest = scripts_dest.resolve(strict=False)
|
|
4899
|
+
else:
|
|
4900
|
+
_remove_runtime_copy_target(scripts_dest)
|
|
4890
4901
|
scripts_dest.mkdir(parents=True, exist_ok=True)
|
|
4891
4902
|
for item in scripts_src.iterdir():
|
|
4892
4903
|
if item.name == "__pycache__" or item.name.startswith(".") or is_duplicate_artifact_name(item):
|
|
@@ -4897,7 +4908,7 @@ def _copy_runtime_from_source(src_dir: Path, repo_dir: Path, dest: Path = NEXO_H
|
|
|
4897
4908
|
shutil.copytree(str(item), str(dst), ignore=_runtime_copy_ignore())
|
|
4898
4909
|
elif item.is_file():
|
|
4899
4910
|
existing_class = installed_script_classes.get(item.name, "")
|
|
4900
|
-
if dst.exists() and existing_class
|
|
4911
|
+
if dst.exists() and existing_class == "personal":
|
|
4901
4912
|
script_conflicts.append(
|
|
4902
4913
|
{
|
|
4903
4914
|
"name": item.name,
|
|
@@ -5579,12 +5590,126 @@ def _maybe_promote_adaptive_weights_empirically(dest: Path) -> tuple[bool, str |
|
|
|
5579
5590
|
return True, None
|
|
5580
5591
|
|
|
5581
5592
|
|
|
5593
|
+
def _promote_f06_core_scripts_from_latest_legacy_conflict(dest: Path) -> tuple[bool, str | None]:
|
|
5594
|
+
"""Recover core script updates archived by the pre-fix F0.6 shim path.
|
|
5595
|
+
|
|
5596
|
+
Older updaters copied packaged scripts into ``NEXO_HOME/scripts`` after
|
|
5597
|
+
breaking the F0.6 compatibility symlink. The subsequent shim reconciliation
|
|
5598
|
+
saw those fresh scripts as legacy conflicts and archived them under
|
|
5599
|
+
``runtime/backups/legacy-shim-conflicts-*`` instead of replacing
|
|
5600
|
+
``core/scripts``. This hook runs from the freshly-copied release tree, so it
|
|
5601
|
+
can repair the first upgrade that introduces the updater fix.
|
|
5602
|
+
"""
|
|
5603
|
+
try:
|
|
5604
|
+
marker = (dest / ".structure-version").read_text(encoding="utf-8").strip()
|
|
5605
|
+
except Exception:
|
|
5606
|
+
marker = ""
|
|
5607
|
+
if marker != "F0.6":
|
|
5608
|
+
return False, None
|
|
5609
|
+
|
|
5610
|
+
core_scripts = dest / "core" / "scripts"
|
|
5611
|
+
backups_dir = dest / "runtime" / "backups"
|
|
5612
|
+
if not core_scripts.is_dir() or not backups_dir.is_dir():
|
|
5613
|
+
return False, None
|
|
5614
|
+
|
|
5615
|
+
try:
|
|
5616
|
+
candidates = sorted(
|
|
5617
|
+
[p for p in backups_dir.glob("legacy-shim-conflicts-*") if (p / "scripts").is_dir()],
|
|
5618
|
+
key=lambda p: p.stat().st_mtime,
|
|
5619
|
+
reverse=True,
|
|
5620
|
+
)
|
|
5621
|
+
except Exception:
|
|
5622
|
+
return False, "f06-core-scripts-promote-warning:scan"
|
|
5623
|
+
|
|
5624
|
+
if not candidates:
|
|
5625
|
+
return False, None
|
|
5626
|
+
|
|
5627
|
+
def _should_skip(path: Path) -> bool:
|
|
5628
|
+
return (
|
|
5629
|
+
path.name == "__pycache__"
|
|
5630
|
+
or path.name.startswith(".")
|
|
5631
|
+
or is_duplicate_artifact_name(path)
|
|
5632
|
+
)
|
|
5633
|
+
|
|
5634
|
+
def _source_is_not_older(src: Path, dst: Path) -> bool:
|
|
5635
|
+
if not dst.exists() and not dst.is_symlink():
|
|
5636
|
+
return True
|
|
5637
|
+
try:
|
|
5638
|
+
return src.stat().st_mtime >= dst.stat().st_mtime
|
|
5639
|
+
except Exception:
|
|
5640
|
+
return False
|
|
5641
|
+
|
|
5642
|
+
copied = 0
|
|
5643
|
+
|
|
5644
|
+
def _copy_entry(src: Path, dst: Path) -> None:
|
|
5645
|
+
nonlocal copied
|
|
5646
|
+
if _should_skip(src):
|
|
5647
|
+
return
|
|
5648
|
+
if src.is_dir():
|
|
5649
|
+
dst.mkdir(parents=True, exist_ok=True)
|
|
5650
|
+
for child in src.iterdir():
|
|
5651
|
+
_copy_entry(child, dst / child.name)
|
|
5652
|
+
return
|
|
5653
|
+
if not src.is_file() or not _source_is_not_older(src, dst):
|
|
5654
|
+
return
|
|
5655
|
+
try:
|
|
5656
|
+
if dst.exists() and dst.is_file() and src.read_bytes() == dst.read_bytes():
|
|
5657
|
+
return
|
|
5658
|
+
except Exception:
|
|
5659
|
+
pass
|
|
5660
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
5661
|
+
shutil.copy2(str(src), str(dst))
|
|
5662
|
+
if src.suffix == ".sh":
|
|
5663
|
+
dst.chmod(0o755)
|
|
5664
|
+
copied += 1
|
|
5665
|
+
|
|
5666
|
+
for backup_root in candidates[:3]:
|
|
5667
|
+
scripts_backup = backup_root / "scripts"
|
|
5668
|
+
try:
|
|
5669
|
+
for item in scripts_backup.iterdir():
|
|
5670
|
+
_copy_entry(item, core_scripts / item.name)
|
|
5671
|
+
except Exception as exc:
|
|
5672
|
+
return False, f"f06-core-scripts-promote-warning:{exc.__class__.__name__}"
|
|
5673
|
+
if copied:
|
|
5674
|
+
return True, f"f06-core-scripts-promoted:{copied}"
|
|
5675
|
+
|
|
5676
|
+
return False, None
|
|
5677
|
+
|
|
5678
|
+
|
|
5679
|
+
def _refresh_active_versioned_runtime_snapshot(dest: Path) -> tuple[bool, str | None]:
|
|
5680
|
+
"""Rebuild ``core/current`` from ``core`` after an update.
|
|
5681
|
+
|
|
5682
|
+
Version equality is not enough evidence that the active snapshot contains
|
|
5683
|
+
the same bytes as ``core``: a same-version repair can update ``core/scripts``
|
|
5684
|
+
after ``core/current`` was already activated. Refreshing the snapshot keeps
|
|
5685
|
+
Desktop/CLI clients that resolve through ``core/current`` on the repaired
|
|
5686
|
+
runtime tree.
|
|
5687
|
+
"""
|
|
5688
|
+
core_root = dest / "core"
|
|
5689
|
+
if not core_root.is_dir() or not (core_root / "cli.py").is_file():
|
|
5690
|
+
return False, None
|
|
5691
|
+
try:
|
|
5692
|
+
from runtime_versioning import activate_versioned_runtime_snapshot, read_version_for_path
|
|
5693
|
+
|
|
5694
|
+
version = str(read_version_for_path(core_root) or "").strip()
|
|
5695
|
+
if not version:
|
|
5696
|
+
return False, "runtime-snapshot-refresh-skipped:missing-version"
|
|
5697
|
+
result = activate_versioned_runtime_snapshot(source_root=core_root, version=version)
|
|
5698
|
+
except Exception as exc:
|
|
5699
|
+
return False, f"runtime-snapshot-refresh-warning:{exc.__class__.__name__}"
|
|
5700
|
+
if not result.get("ok"):
|
|
5701
|
+
return False, f"runtime-snapshot-refresh-warning:{result.get('error', 'unknown')}"
|
|
5702
|
+
return True, f"runtime-snapshot-refreshed:{version}"
|
|
5703
|
+
|
|
5704
|
+
|
|
5582
5705
|
# Whitelist of post-install hooks to invoke from the fresh tree. Each entry
|
|
5583
5706
|
# is the function name inside ``auto_update.py`` of the freshly-copied
|
|
5584
5707
|
# code. The subprocess resolves them on the NEW module and calls
|
|
5585
5708
|
# ``fn(dest)`` returning ``(bool, str | None)``. New hooks added in
|
|
5586
5709
|
# future releases only need an entry here — no extra wiring.
|
|
5587
5710
|
_POST_INSTALL_FRESH_HOOKS = (
|
|
5711
|
+
("f06-core-scripts-promoted", "_promote_f06_core_scripts_from_latest_legacy_conflict"),
|
|
5712
|
+
("runtime-snapshot-refreshed", "_refresh_active_versioned_runtime_snapshot"),
|
|
5588
5713
|
("guardian-hard-persisted", "_persist_guardian_hard_defaults"),
|
|
5589
5714
|
("adaptive-weights-promoted", "_maybe_promote_adaptive_weights_empirically"),
|
|
5590
5715
|
)
|