nexo-brain 5.3.13 → 5.3.14

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": "5.3.13",
3
+ "version": "5.3.14",
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "5.3.13",
3
+ "version": "5.3.14",
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
@@ -32,6 +32,23 @@ if str(_runtime_root) not in sys.path:
32
32
  sys.path.insert(0, str(_runtime_root))
33
33
 
34
34
  from cron_recovery import resolve_declared_schedule, should_run_at_load
35
+ try:
36
+ from runtime_power import resolve_launchagent_path
37
+ except ImportError:
38
+ def resolve_launchagent_path() -> str:
39
+ """Fallback when runtime_power is not importable."""
40
+ home = Path.home()
41
+ parts = ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin",
42
+ str(home / ".local/bin"), str(home / ".nexo/bin")]
43
+ nvm_dir = home / ".nvm/versions/node"
44
+ if nvm_dir.is_dir():
45
+ versions = sorted(nvm_dir.iterdir(), key=lambda p: p.stat().st_mtime, reverse=True)
46
+ for v in versions:
47
+ node_bin = v / "bin"
48
+ if (node_bin / "node").exists():
49
+ parts.insert(0, str(node_bin))
50
+ break
51
+ return ":".join(parts)
35
52
 
36
53
  NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
37
54
  SOURCE_ROOT = Path(os.environ.get("NEXO_CODE", str(Path(__file__).resolve().parent.parent)))
@@ -221,10 +238,7 @@ def build_plist(cron: dict) -> dict:
221
238
  "StandardOutPath": str(LOG_DIR / f"{cron_id}-stdout.log"),
222
239
  "StandardErrorPath": str(LOG_DIR / f"{cron_id}-stderr.log"),
223
240
  "EnvironmentVariables": {
224
- "PATH": "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:"
225
- + str(Path.home() / ".local" / "bin") + ":"
226
- + str(Path.home() / ".nvm/versions/node/v22.14.0/bin") + ":"
227
- + "/Library/Frameworks/Python.framework/Versions/3.12/bin",
241
+ "PATH": resolve_launchagent_path(),
228
242
  "HOME": str(Path.home()),
229
243
  "NEXO_HOME": str(NEXO_HOME),
230
244
  "NEXO_CODE": str(RUNTIME_ROOT),
@@ -125,7 +125,7 @@ def delete_preference(key: str) -> bool:
125
125
 
126
126
  # ── Agents ────────────────────────────────────────────────────────
127
127
 
128
- def create_agent(id: str, name: str, specialization: str, model: str = "sonnet",
128
+ def create_agent(id: str, name: str, specialization: str, model: str = "",
129
129
  tools: str = "", context_files: str = "", rules: str = "") -> dict:
130
130
  """Register a new agent. Uses INSERT OR REPLACE to allow re-registration."""
131
131
  conn = get_db()
@@ -1,6 +1,12 @@
1
1
  """Agents plugin — registry of known agent types with their configs."""
2
2
  from db import create_agent, get_agent, list_agents, update_agent, delete_agent
3
3
 
4
+ try:
5
+ from client_preferences import resolve_user_model
6
+ _DEFAULT_AGENT_MODEL = resolve_user_model()
7
+ except Exception:
8
+ _DEFAULT_AGENT_MODEL = ""
9
+
4
10
  def handle_agent_get(id: str) -> str:
5
11
  """Get an agent's full profile by ID."""
6
12
  a = get_agent(id)
@@ -11,11 +17,12 @@ def handle_agent_get(id: str) -> str:
11
17
  if a["rules"]: lines.append(f" Reglas: {a['rules']}")
12
18
  return "\n".join(lines)
13
19
 
14
- def handle_agent_create(id: str, name: str, specialization: str, model: str = "sonnet",
20
+ def handle_agent_create(id: str, name: str, specialization: str, model: str = "",
15
21
  tools: str = "", context_files: str = "", rules: str = "") -> str:
16
22
  """Register a new agent in the registry."""
17
- create_agent(id, name, specialization, model, tools, context_files, rules)
18
- return f"Agent '{id}' ({name}) registered. Model: {model}"
23
+ effective_model = model or _DEFAULT_AGENT_MODEL
24
+ create_agent(id, name, specialization, effective_model, tools, context_files, rules)
25
+ return f"Agent '{id}' ({name}) registered. Model: {effective_model}"
19
26
 
20
27
  def handle_agent_update(id: str, name: str = "", specialization: str = "", model: str = "",
21
28
  tools: str = "", context_files: str = "", rules: str = "") -> str:
@@ -13,6 +13,7 @@ from db import (
13
13
  upsert_personal_script, register_personal_script_schedule,
14
14
  get_personal_script_schedule,
15
15
  )
16
+ from runtime_power import resolve_launchagent_path
16
17
  from script_registry import (
17
18
  PERSONAL_SCHEDULE_MANAGED_ENV,
18
19
  parse_inline_metadata,
@@ -447,7 +448,7 @@ def _add_launchagent(cron_id, script_path, wrapper_path, schedule, interval_seco
447
448
  "EnvironmentVariables": {
448
449
  "HOME": str(Path.home()),
449
450
  "NEXO_HOME": str(nexo_home),
450
- "PATH": "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:" + str(Path.home() / ".local/bin"),
451
+ "PATH": resolve_launchagent_path(),
451
452
  PERSONAL_SCHEDULE_MANAGED_ENV: "1",
452
453
  "NEXO_PERSONAL_CRON_ID": cron_id,
453
454
  },
@@ -2,6 +2,7 @@
2
2
  # Core (required)
3
3
  fastmcp>=2.9.0
4
4
  numpy
5
+ tomli; python_version < "3.11"
5
6
 
6
7
  # Embedding model (optional but recommended for cognitive features)
7
8
  fastembed
@@ -11,4 +12,3 @@ fastapi
11
12
  uvicorn
12
13
  pydantic
13
14
  jinja2
14
- tomli; python_version < "3.11"
@@ -72,6 +72,23 @@ DEFAULT_CODEX_MODEL = DEFAULT_CLAUDE_CODE_MODEL
72
72
  DEFAULT_CODEX_REASONING_EFFORT = ""
73
73
 
74
74
 
75
+ def resolve_launchagent_path() -> str:
76
+ """Build a PATH string for LaunchAgent plists that includes nvm node if present."""
77
+ home = Path.home()
78
+ parts = ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin",
79
+ str(home / ".local/bin"), str(home / ".nexo/bin")]
80
+ # Detect nvm node
81
+ nvm_dir = home / ".nvm/versions/node"
82
+ if nvm_dir.is_dir():
83
+ versions = sorted(nvm_dir.iterdir(), key=lambda p: p.stat().st_mtime, reverse=True)
84
+ for v in versions:
85
+ node_bin = v / "bin"
86
+ if (node_bin / "node").exists():
87
+ parts.insert(0, str(node_bin))
88
+ break
89
+ return ":".join(parts)
90
+
91
+
75
92
  def _schedule_defaults() -> dict:
76
93
  return {
77
94
  "timezone": "UTC",
@@ -714,7 +731,7 @@ def _macos_prevent_sleep_plist() -> tuple[Path, dict]:
714
731
  "HOME": str(Path.home()),
715
732
  "NEXO_HOME": str(NEXO_HOME),
716
733
  "NEXO_CODE": str(NEXO_HOME),
717
- "PATH": "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:" + str(Path.home() / ".local/bin"),
734
+ "PATH": resolve_launchagent_path(),
718
735
  },
719
736
  }
720
737
  return plist_path, plist
@@ -27,7 +27,7 @@
27
27
  <key>EnvironmentVariables</key>
28
28
  <dict>
29
29
  <key>PATH</key>
30
- <string>/usr/bin:/usr/local/bin:/bin:/opt/homebrew/bin</string>
30
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:__NEXO_HOME__/bin</string>
31
31
  <key>NEXO_SKIP_FS_INDEX</key>
32
32
  <string>1</string>
33
33
  <key>NEXO_HOME</key>
@@ -29,7 +29,7 @@
29
29
  <key>HOME</key>
30
30
  <string>{{HOME}}</string>
31
31
  <key>PATH</key>
32
- <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
32
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:__NEXO_HOME__/bin</string>
33
33
  <key>NEXO_HOME</key>
34
34
  <string>{{NEXO_HOME}}</string>
35
35
  <key>NEXO_CODE</key>
@@ -33,7 +33,7 @@
33
33
  <key>EnvironmentVariables</key>
34
34
  <dict>
35
35
  <key>PATH</key>
36
- <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
36
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:__NEXO_HOME__/bin</string>
37
37
  <key>NEXO_HOME</key>
38
38
  <string>{{NEXO_HOME}}</string>
39
39
  <key>NEXO_CODE</key>
@@ -31,7 +31,7 @@
31
31
  <key>EnvironmentVariables</key>
32
32
  <dict>
33
33
  <key>PATH</key>
34
- <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
34
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:__NEXO_HOME__/bin</string>
35
35
  <key>HOME</key>
36
36
  <string>{{HOME}}</string>
37
37
  <key>NEXO_HOME</key>
@@ -34,7 +34,7 @@
34
34
  <key>HOME</key>
35
35
  <string>{{HOME}}</string>
36
36
  <key>PATH</key>
37
- <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
37
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:__NEXO_HOME__/bin</string>
38
38
  <key>NEXO_HOME</key>
39
39
  <string>{{NEXO_HOME}}</string>
40
40
  <key>NEXO_CODE</key>
@@ -35,7 +35,7 @@
35
35
  <key>HOME</key>
36
36
  <string>{{HOME}}</string>
37
37
  <key>PATH</key>
38
- <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
38
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:__NEXO_HOME__/bin</string>
39
39
  <key>NEXO_HOME</key>
40
40
  <string>{{NEXO_HOME}}</string>
41
41
  <key>NEXO_CODE</key>
@@ -31,7 +31,7 @@
31
31
  <key>HOME</key>
32
32
  <string>{{HOME}}</string>
33
33
  <key>PATH</key>
34
- <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
34
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:__NEXO_HOME__/bin</string>
35
35
  <key>NEXO_HOME</key>
36
36
  <string>{{NEXO_HOME}}</string>
37
37
  <key>NEXO_CODE</key>
@@ -35,7 +35,7 @@
35
35
  <key>HOME</key>
36
36
  <string>{{HOME}}</string>
37
37
  <key>PATH</key>
38
- <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
38
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:__NEXO_HOME__/bin</string>
39
39
  <key>NEXO_HOME</key>
40
40
  <string>{{NEXO_HOME}}</string>
41
41
  <key>NEXO_CODE</key>
@@ -37,7 +37,7 @@
37
37
  <key>HOME</key>
38
38
  <string>{{HOME}}</string>
39
39
  <key>PATH</key>
40
- <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
40
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:__NEXO_HOME__/bin</string>
41
41
  <key>NEXO_HOME</key>
42
42
  <string>{{NEXO_HOME}}</string>
43
43
  <key>NEXO_CODE</key>
@@ -35,7 +35,7 @@
35
35
  <key>HOME</key>
36
36
  <string>{{HOME}}</string>
37
37
  <key>PATH</key>
38
- <string>/usr/bin:/bin:/usr/local/bin:/opt/homebrew/bin</string>
38
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:__NEXO_HOME__/bin</string>
39
39
  <key>NEXO_HOME</key>
40
40
  <string>{{NEXO_HOME}}</string>
41
41
  <key>NEXO_CODE</key>
@@ -27,7 +27,7 @@
27
27
  <key>EnvironmentVariables</key>
28
28
  <dict>
29
29
  <key>PATH</key>
30
- <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
30
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:__NEXO_HOME__/bin</string>
31
31
  <key>NEXO_HOME</key>
32
32
  <string>{{NEXO_HOME}}</string>
33
33
  <key>NEXO_CODE</key>
@@ -33,13 +33,14 @@ except ImportError:
33
33
  from nexo_helper import call_tool_text, run_automation_text
34
34
 
35
35
  # If this script ever needs an autonomous model call:
36
- # 1. use run_automation_text(...)
37
- # 2. pass a legacy task profile like model="opus" when useful
38
- # 3. DO NOT hardcode `claude -p` or provider-specific model defaults
36
+ # 1. use resolve_user_model() to get the user's configured model
37
+ # 2. pass it to run_automation_text(...)
38
+ # 3. DO NOT hardcode model names the user picks their model once
39
39
  # Example:
40
+ # from client_preferences import resolve_user_model
40
41
  # result = run_automation_text(
41
42
  # "Summarize pending issues",
42
- # model="opus", # legacy task profile; NEXO maps it per backend
43
+ # model=resolve_user_model(),
43
44
  # )
44
45
 
45
46
 
@@ -38,8 +38,9 @@ def main() -> int:
38
38
 
39
39
 
40
40
  # Agentic example for future edits:
41
+ # from client_preferences import resolve_user_model
41
42
  # from nexo_helper import run_automation_text
42
- # result = run_automation_text("Analyze this", model="opus")
43
+ # result = run_automation_text("Analyze this", model=resolve_user_model())
43
44
  # print(result)
44
45
 
45
46