nexo-brain 7.20.14 → 7.20.15
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/README.md +3 -1
- package/package.json +1 -1
- package/src/auto_update.py +18 -1
- package/src/plugins/update.py +40 -5
package/README.md
CHANGED
|
@@ -18,7 +18,9 @@
|
|
|
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.20.
|
|
21
|
+
Version `7.20.15` is the current packaged-runtime line. Patch release over v7.20.14 — Brain update/recovery paths now fail closed when the DB guard is missing or stale, and backup validation rejects any replacement that loses Local Memory tables.
|
|
22
|
+
|
|
23
|
+
Previously in `7.20.14`: patch release over v7.20.13 — Brain protects Local Memory during update/recovery paths, rotates runtime backup families to the latest 5 entries, keeps first-indexing status stable, and exposes bounded indexing speed profiles for Desktop.
|
|
22
24
|
|
|
23
25
|
Previously in `7.20.13`: patch release over v7.20.12 — Brain recovery now pauses all known DB writers before restoring `nexo.db`, and Doctor can repair the zero-byte/locked database state that made Desktop Local Memory show zero files.
|
|
24
26
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.20.
|
|
3
|
+
"version": "7.20.15",
|
|
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
|
@@ -82,6 +82,23 @@ TEMPLATE_FILE = _RESOLVED_REPO_DIR / "templates" / "CLAUDE.md.template"
|
|
|
82
82
|
CHECK_COOLDOWN_SECONDS = 3600 # 1 hour
|
|
83
83
|
GIT_TIMEOUT_SECONDS = 4 # stay well under the 5s total budget
|
|
84
84
|
CRITICAL_BACKUP_TABLES = ("learnings", "session_diary", "guard_checks", "protocol_debt")
|
|
85
|
+
LOCAL_CONTEXT_BACKUP_TABLES = (
|
|
86
|
+
"local_index_roots",
|
|
87
|
+
"local_index_exclusions",
|
|
88
|
+
"local_index_jobs",
|
|
89
|
+
"local_index_checkpoints",
|
|
90
|
+
"local_index_state",
|
|
91
|
+
"local_index_errors",
|
|
92
|
+
"local_assets",
|
|
93
|
+
"local_asset_versions",
|
|
94
|
+
"local_chunks",
|
|
95
|
+
"local_entities",
|
|
96
|
+
"local_relations",
|
|
97
|
+
"local_embeddings",
|
|
98
|
+
"local_context_queries",
|
|
99
|
+
"local_index_dirs",
|
|
100
|
+
)
|
|
101
|
+
PROTECTED_BACKUP_TABLES = CRITICAL_BACKUP_TABLES + LOCAL_CONTEXT_BACKUP_TABLES
|
|
85
102
|
CLASSIFIER_INSTALL_TIMEOUT_SECONDS = 1800
|
|
86
103
|
CLASSIFIER_INSTALL_JOIN_SECONDS = 1500
|
|
87
104
|
CLASSIFIER_INSTALL_LOG = paths.logs_dir() / "classifier-install.log"
|
|
@@ -321,7 +338,7 @@ def _validate_db_backup(source_db: Path, backup_db: Path) -> dict:
|
|
|
321
338
|
report["errors"].append(f"backup db missing: {backup_db}")
|
|
322
339
|
return report
|
|
323
340
|
|
|
324
|
-
for table in
|
|
341
|
+
for table in PROTECTED_BACKUP_TABLES:
|
|
325
342
|
source_count = _critical_table_count(source_db, table)
|
|
326
343
|
backup_count = _critical_table_count(backup_db, table)
|
|
327
344
|
report["source_counts"][table] = source_count
|
package/src/plugins/update.py
CHANGED
|
@@ -32,10 +32,9 @@ except ModuleNotFoundError as exc:
|
|
|
32
32
|
def is_duplicate_artifact_name(_path) -> bool:
|
|
33
33
|
return False
|
|
34
34
|
|
|
35
|
-
# db_guard
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# the fixed module on its own.
|
|
35
|
+
# db_guard must be available before any DB-touching update runs. Desktop syncs
|
|
36
|
+
# this file plus db_guard.py as a pair; if a stale runtime imports update.py
|
|
37
|
+
# without a current guard, we abort instead of risking a local-memory wipe.
|
|
39
38
|
try:
|
|
40
39
|
from db_guard import (
|
|
41
40
|
CRITICAL_TABLES,
|
|
@@ -52,8 +51,10 @@ try:
|
|
|
52
51
|
validate_backup_matches_source,
|
|
53
52
|
)
|
|
54
53
|
_DB_GUARD_AVAILABLE = True
|
|
55
|
-
|
|
54
|
+
_DB_GUARD_IMPORT_ERROR = ""
|
|
55
|
+
except Exception as exc: # pragma: no cover - exercised only during stale installs
|
|
56
56
|
_DB_GUARD_AVAILABLE = False
|
|
57
|
+
_DB_GUARD_IMPORT_ERROR = str(exc)
|
|
57
58
|
CRITICAL_TABLES = ()
|
|
58
59
|
PROTECTED_TABLES = ()
|
|
59
60
|
HOURLY_BACKUP_MAX_AGE = 48 * 3600
|
|
@@ -90,6 +91,13 @@ except Exception: # pragma: no cover - exercised only during mid-upgrade instal
|
|
|
90
91
|
def validate_backup_matches_source(*_args, **_kwargs): # type: ignore[misc]
|
|
91
92
|
return True, None
|
|
92
93
|
|
|
94
|
+
REQUIRED_LOCAL_MEMORY_TABLES: tuple[str, ...] = (
|
|
95
|
+
"local_assets",
|
|
96
|
+
"local_chunks",
|
|
97
|
+
"local_embeddings",
|
|
98
|
+
"local_index_dirs",
|
|
99
|
+
)
|
|
100
|
+
|
|
93
101
|
# Code root is the parent of plugins/:
|
|
94
102
|
# - source checkout: <repo>/src
|
|
95
103
|
# - packaged runtime: <NEXO_HOME>
|
|
@@ -383,6 +391,30 @@ def _check_dirty() -> str | None:
|
|
|
383
391
|
return None
|
|
384
392
|
|
|
385
393
|
|
|
394
|
+
def _db_guard_integrity_error() -> str | None:
|
|
395
|
+
"""Return why DB protection is unsafe, or None when current guard is usable."""
|
|
396
|
+
if os.environ.get("NEXO_SKIP_WIPE_GUARD") == "1":
|
|
397
|
+
return None
|
|
398
|
+
primary_db = DATA_DIR / "nexo.db"
|
|
399
|
+
if not primary_db.is_file():
|
|
400
|
+
return None
|
|
401
|
+
if not _DB_GUARD_AVAILABLE:
|
|
402
|
+
return (
|
|
403
|
+
"DB protection module is not available; refusing to update while local memory may exist.\n"
|
|
404
|
+
f" import error: {_DB_GUARD_IMPORT_ERROR or 'unknown'}\n"
|
|
405
|
+
"Restart Desktop so it can sync the bundled Brain guard, then retry the update."
|
|
406
|
+
)
|
|
407
|
+
protected = set(PROTECTED_TABLES)
|
|
408
|
+
missing = [table for table in REQUIRED_LOCAL_MEMORY_TABLES if table not in protected]
|
|
409
|
+
if missing:
|
|
410
|
+
return (
|
|
411
|
+
"DB protection module is stale; refusing to update because local memory tables are not protected.\n"
|
|
412
|
+
f" missing tables: {', '.join(missing)}\n"
|
|
413
|
+
"Restart Desktop so it can sync the bundled Brain guard, then retry the update."
|
|
414
|
+
)
|
|
415
|
+
return None
|
|
416
|
+
|
|
417
|
+
|
|
386
418
|
def _preflight_wipe_check() -> str | None:
|
|
387
419
|
"""Abort the update early if the primary DB already looks wiped.
|
|
388
420
|
|
|
@@ -399,6 +431,9 @@ def _preflight_wipe_check() -> str | None:
|
|
|
399
431
|
"""
|
|
400
432
|
if os.environ.get("NEXO_SKIP_WIPE_GUARD") == "1":
|
|
401
433
|
return None
|
|
434
|
+
guard_err = _db_guard_integrity_error()
|
|
435
|
+
if guard_err:
|
|
436
|
+
return guard_err
|
|
402
437
|
primary_db = DATA_DIR / "nexo.db"
|
|
403
438
|
if not primary_db.is_file():
|
|
404
439
|
return None # Nothing to protect; fresh install path.
|