nexo-brain 7.18.0 → 7.18.1
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 +3 -1
- package/bin/nexo-brain.js +1 -1
- package/package.json +1 -1
- package/src/auto_update.py +1 -1
- package/src/local_context/api.py +47 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.18.
|
|
3
|
+
"version": "7.18.1",
|
|
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,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.18.
|
|
21
|
+
Version `7.18.1` is the current packaged-runtime line. Patch release over v7.18.0 - packaged Brain runtimes now include the `local_context` package, so Desktop Local Memory and `nexo local-context` do not get stuck behind `ModuleNotFoundError` or zero-file status; the local-index service also keeps detecting newly mounted volumes automatically.
|
|
22
|
+
|
|
23
|
+
Previously in `7.18.0`: minor release over v7.17.8 - Brain adds the Local Context Layer: a local-only background memory index with checkpoints, extraction, graph links, embeddings, MCP/CLI controls, and pre-action evidence for NEXO agents.
|
|
22
24
|
|
|
23
25
|
Previously in `7.17.8`: patch release over v7.17.7 - standalone `nexo chat` now surfaces macOS Full Disk Access guidance, and Brain clears stale permission state after a live access probe succeeds.
|
|
24
26
|
|
package/bin/nexo-brain.js
CHANGED
|
@@ -1123,7 +1123,7 @@ function getCoreRuntimeFlatFiles(srcDir = path.join(__dirname, "..", "src")) {
|
|
|
1123
1123
|
}
|
|
1124
1124
|
|
|
1125
1125
|
function getCoreRuntimePackages() {
|
|
1126
|
-
return ["db", "cognitive", "doctor"];
|
|
1126
|
+
return ["db", "cognitive", "doctor", "local_context"];
|
|
1127
1127
|
}
|
|
1128
1128
|
|
|
1129
1129
|
// Brain contracts — files the NEXO Brain publishes to consumers like
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.18.
|
|
3
|
+
"version": "7.18.1",
|
|
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
|
@@ -4350,7 +4350,7 @@ def _restore_runtime_tree(backup_dir: str, dest: Path = NEXO_HOME) -> None:
|
|
|
4350
4350
|
def _copy_runtime_from_source(src_dir: Path, repo_dir: Path, dest: Path = NEXO_HOME, progress_fn=None) -> dict:
|
|
4351
4351
|
import shutil
|
|
4352
4352
|
|
|
4353
|
-
packages = ["db", "cognitive", "doctor", "dashboard", "rules", "crons", "hooks", "presets"]
|
|
4353
|
+
packages = ["db", "cognitive", "doctor", "local_context", "dashboard", "rules", "crons", "hooks", "presets"]
|
|
4354
4354
|
flat_files = _runtime_flat_files(src_dir)
|
|
4355
4355
|
copied_packages = 0
|
|
4356
4356
|
copied_files = 0
|
package/src/local_context/api.py
CHANGED
|
@@ -66,20 +66,62 @@ def list_roots() -> list[dict]:
|
|
|
66
66
|
return [dict(row) for row in rows]
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
def _dedupe_roots(roots: list[str]) -> list[str]:
|
|
70
|
+
seen: set[str] = set()
|
|
71
|
+
result: list[str] = []
|
|
72
|
+
for root in roots:
|
|
73
|
+
normalized = norm_path(root)
|
|
74
|
+
if not normalized or normalized in seen:
|
|
75
|
+
continue
|
|
76
|
+
seen.add(normalized)
|
|
77
|
+
result.append(normalized)
|
|
78
|
+
return result
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _mounted_volume_roots() -> list[str]:
|
|
82
|
+
candidates: list[Path] = []
|
|
83
|
+
if sys.platform == "darwin":
|
|
84
|
+
candidates.extend((Path("/Volumes")).iterdir() if Path("/Volumes").is_dir() else [])
|
|
85
|
+
elif sys.platform.startswith("win"):
|
|
86
|
+
candidates.extend(Path(f"{letter}:\\") for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
|
87
|
+
else:
|
|
88
|
+
user = os.environ.get("USER") or os.environ.get("USERNAME") or ""
|
|
89
|
+
mount_bases = [Path("/mnt")]
|
|
90
|
+
if user:
|
|
91
|
+
mount_bases.extend([Path("/media") / user, Path("/run/media") / user])
|
|
92
|
+
for base in mount_bases:
|
|
93
|
+
if base.is_dir():
|
|
94
|
+
candidates.extend(base.iterdir())
|
|
95
|
+
|
|
96
|
+
roots: list[str] = []
|
|
97
|
+
root_resolved = Path("/").resolve()
|
|
98
|
+
for candidate in candidates:
|
|
99
|
+
try:
|
|
100
|
+
if candidate.name.startswith(".") or not candidate.is_dir():
|
|
101
|
+
continue
|
|
102
|
+
resolved = candidate.resolve()
|
|
103
|
+
if resolved == root_resolved:
|
|
104
|
+
continue
|
|
105
|
+
roots.append(str(candidate))
|
|
106
|
+
except Exception:
|
|
107
|
+
continue
|
|
108
|
+
return roots
|
|
109
|
+
|
|
110
|
+
|
|
69
111
|
def default_roots() -> list[str]:
|
|
70
112
|
home = Path.home()
|
|
71
113
|
configured = os.environ.get("NEXO_LOCAL_INDEX_DEFAULT_ROOTS", "").strip()
|
|
72
114
|
if configured:
|
|
73
|
-
return [
|
|
74
|
-
return [
|
|
115
|
+
return _dedupe_roots([item for item in configured.split(os.pathsep) if item.strip()])
|
|
116
|
+
return _dedupe_roots([str(home), *_mounted_volume_roots()])
|
|
75
117
|
|
|
76
118
|
|
|
77
119
|
def ensure_default_roots() -> dict:
|
|
78
|
-
|
|
79
|
-
if existing:
|
|
80
|
-
return {"ok": True, "created": 0, "roots": existing}
|
|
120
|
+
existing_paths = {row["root_path"] for row in list_roots()}
|
|
81
121
|
created = []
|
|
82
122
|
for root in default_roots():
|
|
123
|
+
if root in existing_paths:
|
|
124
|
+
continue
|
|
83
125
|
candidate = Path(root).expanduser()
|
|
84
126
|
if candidate.exists() and candidate.is_dir():
|
|
85
127
|
created.append(add_root(str(candidate), mode="normal", depth=2))
|