nexo-brain 7.20.15 → 7.20.17

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,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.20.15` is the current packaged-runtime line. Patch release over v7.20.14Brain 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.
21
+ Version `7.20.17` is the current packaged-runtime line. Patch release over v7.20.16validated DB backups now tolerate tiny live-write growth from the Local Memory indexer while still rejecting real protected-table loss.
22
+
23
+ Previously in `7.20.16`: patch release over v7.20.15 — packaged updates keep the `local_context` runtime shim importable and rollback code-tree snapshots safely when compatibility directories are symlinks.
24
+
25
+ Previously in `7.20.15`: 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
26
 
23
27
  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.
24
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "7.20.15",
3
+ "version": "7.20.17",
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",
@@ -2440,6 +2440,7 @@ def _f06_legacy_shim_map() -> list[tuple[str, Path]]:
2440
2440
  ("plugins", core_root / "plugins"),
2441
2441
  ("hooks", core_root / "hooks"),
2442
2442
  ("rules", core_root / "rules"),
2443
+ ("local_context", core_root / "local_context"),
2443
2444
  ("data", NEXO_HOME / "runtime" / "data"),
2444
2445
  ("logs", NEXO_HOME / "runtime" / "logs"),
2445
2446
  ("operations", NEXO_HOME / "runtime" / "operations"),
@@ -2508,6 +2509,7 @@ def _f06_live_legacy_paths() -> list[Path]:
2508
2509
  "plugins",
2509
2510
  "hooks",
2510
2511
  "rules",
2512
+ "local_context",
2511
2513
  "db",
2512
2514
  "dashboard",
2513
2515
  "skills-core",
@@ -2593,6 +2595,7 @@ def _promote_packaged_runtime_code_to_core() -> None:
2593
2595
  ("cognitive", core_root / "cognitive"),
2594
2596
  ("doctor", core_root / "doctor"),
2595
2597
  ("dashboard", core_root / "dashboard"),
2598
+ ("local_context", core_root / "local_context"),
2596
2599
  ("skills-core", core_root / "skills"),
2597
2600
  ]
2598
2601
 
@@ -4325,6 +4328,7 @@ def _backup_runtime_tree(dest: Path = NEXO_HOME) -> str:
4325
4328
  "db",
4326
4329
  "cognitive",
4327
4330
  "dashboard",
4331
+ "local_context",
4328
4332
  "rules",
4329
4333
  "crons",
4330
4334
  "scripts",
package/src/db_guard.py CHANGED
@@ -455,13 +455,28 @@ def validate_backup_matches_source(
455
455
  if s is not None and d is None:
456
456
  discrepancies.append(f"{table}: source={s} backup=missing")
457
457
  continue
458
- if s is not None and d is not None and d < s:
458
+ if s is not None and d is not None and d < s and not _backup_drift_is_safe(s, d):
459
459
  discrepancies.append(f"{table}: source={s} backup={d}")
460
460
  if discrepancies:
461
461
  return False, "; ".join(discrepancies)
462
462
  return True, None
463
463
 
464
464
 
465
+ def _backup_drift_is_safe(source_count: int, backup_count: int) -> bool:
466
+ """Allow tiny live-write drift while still rejecting real backup data loss.
467
+
468
+ `sqlite3.backup()` creates a consistent snapshot, but NEXO's background
469
+ memory service can add rows immediately after the snapshot. Comparing the
470
+ backup with the live DB after that growth must not abort an update. Small
471
+ tables stay exact because a 1-row loss there can be meaningful.
472
+ """
473
+ if backup_count <= 0 or source_count < 1000:
474
+ return False
475
+ drift = source_count - backup_count
476
+ allowed = max(25, int(source_count * 0.005))
477
+ return 0 < drift <= allowed
478
+
479
+
465
480
  def _quote_identifier(identifier: str) -> str:
466
481
  if identifier not in PROTECTED_TABLES:
467
482
  raise ValueError(f"refusing unsafe table identifier: {identifier!r}")
@@ -908,6 +908,7 @@ def _backup_code_tree() -> tuple[str | None, str | None]:
908
908
  "db",
909
909
  "cognitive",
910
910
  "dashboard",
911
+ "local_context",
911
912
  "rules",
912
913
  "crons",
913
914
  "scripts",
@@ -952,7 +953,9 @@ def _restore_code_tree(backup_dir: str) -> str | None:
952
953
  for item in bdir.iterdir():
953
954
  dest = NEXO_HOME / item.name
954
955
  if item.is_dir():
955
- if dest.is_dir():
956
+ if dest.is_symlink():
957
+ dest.unlink()
958
+ elif dest.is_dir():
956
959
  shutil.rmtree(dest)
957
960
  shutil.copytree(item, dest)
958
961
  elif item.is_file():