nexo-brain 7.25.5 → 7.25.6

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "7.25.5",
3
+ "version": "7.25.6",
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,7 @@
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.25.5` is the current packaged-runtime line. Patch release over v7.25.4 - explicit user-included Local Memory roots can override default system/noisy-tree skips, while Brain LocalIndex operator messages stay in English and Desktop continues to enforce the Core-pinned Python 3.12 runtime.
21
+ Version `7.25.6` is the current packaged-runtime line. Patch release over v7.25.5 - existing Local Memory sidecar databases repair legacy root/exclusion columns before source-dependent indexes are created, and core background crons prefer the NEXO-managed Python runtime.
22
22
 
23
23
  Previously in `7.25.4`: patch release over v7.25.3 - Local Memory starts from safe user-content and email roots, adds configurable included/excluded file types, and cleans legacy whole-disk index state with backup or archive-rebuild safety.
24
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "7.25.5",
3
+ "version": "7.25.6",
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/crons/sync.py CHANGED
@@ -150,6 +150,41 @@ RETIRED_CORE_FILES = (
150
150
  )
151
151
 
152
152
 
153
+ def _resolve_core_python_bin() -> str:
154
+ """Prefer the NEXO-managed Python for core cron execution."""
155
+ candidates = [
156
+ os.environ.get("NEXO_RUNTIME_PYTHON", ""),
157
+ os.environ.get("NEXO_PYTHON", ""),
158
+ str(RUNTIME_ROOT / ".venv" / "bin" / "python3"),
159
+ str(RUNTIME_ROOT / ".venv" / "bin" / "python"),
160
+ str(_runtime_code_dir() / ".venv" / "bin" / "python3"),
161
+ str(_runtime_code_dir() / ".venv" / "bin" / "python"),
162
+ ]
163
+ if platform.system() == "Darwin":
164
+ candidates.extend(
165
+ [
166
+ "/Library/Frameworks/Python.framework/Versions/3.12/bin/python3",
167
+ "/opt/homebrew/bin/python3.12",
168
+ "/usr/local/bin/python3.12",
169
+ "/opt/homebrew/bin/python3",
170
+ "/usr/local/bin/python3",
171
+ "/usr/bin/python3",
172
+ ]
173
+ )
174
+ else:
175
+ candidates.extend(["/usr/bin/python3", "/usr/local/bin/python3", "python3"])
176
+
177
+ for candidate in candidates:
178
+ if not candidate:
179
+ continue
180
+ expanded = Path(str(candidate)).expanduser()
181
+ if expanded.exists():
182
+ return str(expanded)
183
+ if os.sep not in str(candidate) and shutil.which(str(candidate)):
184
+ return str(candidate)
185
+ return "python3"
186
+
187
+
153
188
  def _runtime_scripts_dir() -> Path:
154
189
  new = RUNTIME_ROOT / "core" / "scripts"
155
190
  legacy = RUNTIME_ROOT / "scripts"
@@ -407,21 +442,10 @@ def build_plist(cron: dict) -> dict:
407
442
  if subdir_src.is_dir():
408
443
  _copy_into_runtime(subdir_src)
409
444
 
445
+ python_bin = _resolve_core_python_bin()
410
446
  if script_type == "shell":
411
447
  program_args = ["/bin/bash", wrapper_path, cron_id, "/bin/bash", script_path]
412
448
  else:
413
- # Find python3
414
- python_candidates = [
415
- "/opt/homebrew/bin/python3",
416
- "/usr/local/bin/python3",
417
- "/Library/Frameworks/Python.framework/Versions/3.12/bin/python3",
418
- "/usr/bin/python3",
419
- ]
420
- python_bin = "python3"
421
- for p in python_candidates:
422
- if Path(p).exists():
423
- python_bin = p
424
- break
425
449
  program_args = ["/bin/bash", wrapper_path, cron_id, python_bin, script_path]
426
450
 
427
451
  plist = {
@@ -436,6 +460,7 @@ def build_plist(cron: dict) -> dict:
436
460
  "NEXO_CODE": str(_runtime_code_dir()),
437
461
  "NEXO_SOURCE_CODE": str(SOURCE_ROOT),
438
462
  "NEXO_MANAGED_CORE_CRON": "1",
463
+ "NEXO_RUNTIME_PYTHON": python_bin,
439
464
  "PYTHONUNBUFFERED": "1",
440
465
  },
441
466
  }
@@ -505,6 +530,7 @@ def _linux_crontab_entry(cron: dict, exec_cmd: str, stdout_log: Path, stderr_log
505
530
  "HOME": Path.home(),
506
531
  "NEXO_HOME": NEXO_HOME,
507
532
  "NEXO_CODE": _runtime_code_dir(),
533
+ "NEXO_RUNTIME_PYTHON": _resolve_core_python_bin(),
508
534
  "PYTHONUNBUFFERED": "1",
509
535
  }.items()
510
536
  )
@@ -578,12 +604,13 @@ def _sync_wsl_windows_host_local_index_task(dry_run: bool = False) -> dict:
578
604
  log("WARNING: WSL_DISTRO_NAME missing; local-index host task not installed.")
579
605
  return {"ok": False, "skipped": True, "reason": "wsl_distro_missing"}
580
606
 
581
- python_bin = "/usr/bin/python3" if Path("/usr/bin/python3").exists() else "python3"
607
+ python_bin = _resolve_core_python_bin()
582
608
  script_path = _runtime_code_dir() / "scripts" / "nexo-local-index.py"
583
609
  command = (
584
610
  f"cd {shlex.quote(str(Path.home()))} && "
585
611
  f"NEXO_HOME={shlex.quote(str(NEXO_HOME))} "
586
612
  f"NEXO_CODE={shlex.quote(str(_runtime_code_dir()))} "
613
+ f"NEXO_RUNTIME_PYTHON={shlex.quote(python_bin)} "
587
614
  f"{shlex.quote(python_bin)} {shlex.quote(str(script_path))}"
588
615
  )
589
616
  wsl_args = " ".join(
@@ -835,11 +862,7 @@ def sync_linux(dry_run: bool = False):
835
862
 
836
863
  log(f"Manifest: {len(manifest_crons)} core crons")
837
864
 
838
- python_bin = "/usr/bin/python3"
839
- for p in ["/usr/bin/python3", "/usr/local/bin/python3"]:
840
- if Path(p).exists():
841
- python_bin = p
842
- break
865
+ python_bin = _resolve_core_python_bin()
843
866
 
844
867
  enable_units: list[str] = []
845
868
  crontab_entries: list[str] = []
@@ -878,6 +901,7 @@ Type={service_type}
878
901
  ExecStart={exec_cmd}
879
902
  Environment=NEXO_HOME={NEXO_HOME}
880
903
  Environment=NEXO_CODE={_runtime_code_dir()}
904
+ Environment=NEXO_RUNTIME_PYTHON={python_bin}
881
905
  Environment=HOME={Path.home()}
882
906
  StandardOutput=append:{stdout_log}
883
907
  StandardError=append:{stderr_log}
package/src/db/_schema.py CHANGED
@@ -1767,6 +1767,7 @@ def _m62_memory_observations_fts_trigger_fix(conn):
1767
1767
 
1768
1768
  def _m63_local_context_layer(conn):
1769
1769
  """Local Context Layer storage for on-device memory indexing."""
1770
+ _m63_repair_legacy_local_context_columns(conn)
1770
1771
  conn.executescript(
1771
1772
  """
1772
1773
  CREATE TABLE IF NOT EXISTS local_index_roots (
@@ -1995,6 +1996,35 @@ def _m63_local_context_layer(conn):
1995
1996
  )
1996
1997
 
1997
1998
 
1999
+ def _table_exists(conn, table: str) -> bool:
2000
+ row = conn.execute(
2001
+ "SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1",
2002
+ (table,),
2003
+ ).fetchone()
2004
+ return bool(row)
2005
+
2006
+
2007
+ def _m63_repair_legacy_local_context_columns(conn):
2008
+ """Add v2 columns before m63 creates indexes that reference them.
2009
+
2010
+ Existing sidecar DBs can already have m63-era tables without the v2
2011
+ columns. CREATE TABLE IF NOT EXISTS will not alter those tables, so index
2012
+ creation must be preceded by additive repairs.
2013
+ """
2014
+ if _table_exists(conn, "local_index_roots"):
2015
+ _migrate_add_column(conn, "local_index_roots", "source", "TEXT NOT NULL DEFAULT 'legacy'")
2016
+ _migrate_add_column(conn, "local_index_roots", "remote", "INTEGER NOT NULL DEFAULT 0")
2017
+ _migrate_add_column(conn, "local_index_roots", "seed_version", "INTEGER NOT NULL DEFAULT 1")
2018
+ if _table_exists(conn, "local_index_exclusions"):
2019
+ _migrate_add_column(conn, "local_index_exclusions", "source", "TEXT NOT NULL DEFAULT 'legacy'")
2020
+ _migrate_add_column(conn, "local_index_exclusions", "kind", "TEXT NOT NULL DEFAULT 'folder'")
2021
+ if _table_exists(conn, "local_index_file_type_rules"):
2022
+ _migrate_add_column(conn, "local_index_file_type_rules", "source", "TEXT NOT NULL DEFAULT 'legacy'")
2023
+ _migrate_add_column(conn, "local_index_file_type_rules", "priority", "INTEGER NOT NULL DEFAULT 0")
2024
+ _migrate_add_column(conn, "local_index_file_type_rules", "reason", "TEXT NOT NULL DEFAULT ''")
2025
+ _migrate_add_column(conn, "local_index_file_type_rules", "updated_at", "REAL NOT NULL DEFAULT 0")
2026
+
2027
+
1998
2028
  def _m64_local_context_live_dirs(conn):
1999
2029
  """Track known folders so local context can detect new/deleted/changed files quickly."""
2000
2030
  conn.executescript(