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 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.14` is the current packaged-runtime line. 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.
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.14",
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",
@@ -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 CRITICAL_BACKUP_TABLES:
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
@@ -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 landed in v5.5.5. When plugins/update.py is imported from a runtime
36
- # that still ships the v5.5.4 tree (e.g. mid-upgrade), the import will fail
37
- # we fall back to no-op guards so the update can still complete and bring in
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
- except Exception: # pragma: no cover - exercised only during mid-upgrade installs
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.