nexo-brain 5.3.12 → 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.
Files changed (32) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/package.json +1 -1
  3. package/src/crons/sync.py +18 -4
  4. package/src/db/_entities.py +1 -1
  5. package/src/plugins/agents.py +10 -3
  6. package/src/plugins/schedule.py +2 -1
  7. package/src/requirements.txt +1 -1
  8. package/src/runtime_power.py +18 -1
  9. package/src/scripts/check-context.py +7 -1
  10. package/src/scripts/deep-sleep/extract.py +8 -2
  11. package/src/scripts/deep-sleep/synthesize.py +8 -1
  12. package/src/scripts/nexo-catchup.py +8 -2
  13. package/src/scripts/nexo-daily-self-audit.py +7 -1
  14. package/src/scripts/nexo-evolution-run.py +10 -2
  15. package/src/scripts/nexo-immune.py +8 -1
  16. package/src/scripts/nexo-learning-validator.py +9 -1
  17. package/src/scripts/nexo-postmortem-consolidator.py +9 -1
  18. package/src/scripts/nexo-sleep.py +7 -1
  19. package/src/scripts/nexo-synthesis.py +8 -1
  20. package/templates/launchagents/com.nexo.auto-close-sessions.plist +1 -1
  21. package/templates/launchagents/com.nexo.catchup.plist +1 -1
  22. package/templates/launchagents/com.nexo.dashboard.plist +1 -1
  23. package/templates/launchagents/com.nexo.deep-sleep.plist +1 -1
  24. package/templates/launchagents/com.nexo.evolution.plist +1 -1
  25. package/templates/launchagents/com.nexo.followup-hygiene.plist +1 -1
  26. package/templates/launchagents/com.nexo.immune.plist +1 -1
  27. package/templates/launchagents/com.nexo.postmortem.plist +1 -1
  28. package/templates/launchagents/com.nexo.self-audit.plist +1 -1
  29. package/templates/launchagents/com.nexo.synthesis.plist +1 -1
  30. package/templates/launchagents/com.nexo.watchdog.plist +1 -1
  31. package/templates/script-template.py +5 -4
  32. package/templates/skill-script-template.py +2 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "5.3.12",
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.12",
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
@@ -21,6 +21,12 @@ if str(NEXO_CODE) not in sys.path:
21
21
  sys.path.insert(0, str(NEXO_CODE))
22
22
 
23
23
  from agent_runner import AutomationBackendUnavailableError, run_automation_prompt
24
+ try:
25
+ from client_preferences import resolve_user_model as _resolve_user_model
26
+ _USER_MODEL = _resolve_user_model()
27
+ except Exception:
28
+ _USER_MODEL = ""
29
+
24
30
 
25
31
 
26
32
  class ContextChecker:
@@ -192,7 +198,7 @@ Rules:
192
198
  try:
193
199
  result = run_automation_prompt(
194
200
  prompt,
195
- model="opus",
201
+ model=_USER_MODEL or "opus",
196
202
  timeout=300,
197
203
  output_format="text",
198
204
  append_system_prompt="Return exactly one valid JSON object.",
@@ -26,6 +26,12 @@ if str(NEXO_CODE) not in sys.path:
26
26
  sys.path.insert(0, str(NEXO_CODE))
27
27
 
28
28
  from agent_runner import AutomationBackendUnavailableError, run_automation_prompt
29
+ try:
30
+ from client_preferences import resolve_user_model as _resolve_user_model
31
+ _USER_MODEL = _resolve_user_model()
32
+ except Exception:
33
+ _USER_MODEL = ""
34
+
29
35
 
30
36
  # No timeout -- headless automation can take as long as needed
31
37
  CLAUDE_TIMEOUT = 21600 # 3h safety net (prevents zombie processes)
@@ -128,7 +134,7 @@ def analyze_session(
128
134
 
129
135
  result = run_automation_prompt(
130
136
  prompt,
131
- model="opus",
137
+ model=_USER_MODEL or "opus",
132
138
  timeout=CLAUDE_TIMEOUT,
133
139
  output_format="text",
134
140
  append_system_prompt=JSON_SYSTEM_PROMPT,
@@ -158,7 +164,7 @@ def analyze_session(
158
164
  )
159
165
  convert_result = run_automation_prompt(
160
166
  convert_prompt,
161
- model="sonnet",
167
+ model=_USER_MODEL or "sonnet",
162
168
  timeout=120,
163
169
  output_format="text",
164
170
  append_system_prompt=JSON_SYSTEM_PROMPT,
@@ -18,6 +18,13 @@ import hashlib
18
18
  from datetime import datetime
19
19
  from pathlib import Path
20
20
 
21
+
22
+ try:
23
+ from client_preferences import resolve_user_model as _resolve_user_model
24
+ _USER_MODEL = _resolve_user_model()
25
+ except Exception:
26
+ _USER_MODEL = ""
27
+
21
28
  NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
22
29
  NEXO_CODE = Path(os.environ.get("NEXO_CODE", str(Path(__file__).resolve().parents[2])))
23
30
  DEEP_SLEEP_DIR = NEXO_HOME / "operations" / "deep-sleep"
@@ -233,7 +240,7 @@ def main():
233
240
  try:
234
241
  result = run_automation_prompt(
235
242
  prompt,
236
- model="opus",
243
+ model=_USER_MODEL or "opus",
237
244
  timeout=CLAUDE_TIMEOUT,
238
245
  output_format="text",
239
246
  allowed_tools="Read,Grep,Bash",
@@ -13,6 +13,13 @@ import sys
13
13
  from datetime import datetime
14
14
  from pathlib import Path
15
15
 
16
+
17
+ try:
18
+ from client_preferences import resolve_user_model as _resolve_user_model
19
+ _USER_MODEL = _resolve_user_model()
20
+ except Exception:
21
+ _USER_MODEL = ""
22
+
16
23
  _SCRIPT_DIR = Path(__file__).resolve().parent
17
24
  _DEFAULT_RUNTIME_ROOT = _SCRIPT_DIR.parent
18
25
  _runtime_root = Path(os.environ.get("NEXO_CODE", str(_DEFAULT_RUNTIME_ROOT)))
@@ -126,7 +133,6 @@ def _heal_personal_schedules() -> dict:
126
133
  summary = {"created": 0, "repaired": 0, "invalid": 0, "error": ""}
127
134
  try:
128
135
  from script_registry import reconcile_personal_scripts
129
-
130
136
  result = reconcile_personal_scripts(dry_run=False)
131
137
  ensured = result.get("ensure_schedules", {})
132
138
  summary["created"] = len(ensured.get("created", []))
@@ -273,7 +279,7 @@ Format:
273
279
  try:
274
280
  result = run_automation_prompt(
275
281
  prompt,
276
- model="opus",
282
+ model=_USER_MODEL or "opus",
277
283
  timeout=21600,
278
284
  output_format="text",
279
285
  allowed_tools="Read,Write,Edit,Glob,Grep,Bash,mcp__nexo__*",
@@ -40,6 +40,12 @@ from agent_runner import AutomationBackendUnavailableError, run_automation_promp
40
40
  import db as nexo_db
41
41
  from public_evolution_queue import queue_public_port_candidate
42
42
 
43
+ try:
44
+ from client_preferences import resolve_user_model as _resolve_user_model
45
+ _USER_MODEL = _resolve_user_model()
46
+ except Exception:
47
+ _USER_MODEL = ""
48
+
43
49
  LOG_DIR = NEXO_HOME / "logs"
44
50
  LOG_DIR.mkdir(parents=True, exist_ok=True)
45
51
  AUDIT_HISTORY_DIR = LOG_DIR / "self-audit"
@@ -2043,7 +2049,7 @@ Also write the machine-readable summary to {LOG_DIR}/self-audit-summary.json.
2043
2049
  try:
2044
2050
  result = run_automation_prompt(
2045
2051
  prompt,
2046
- model="opus",
2052
+ model=_USER_MODEL or "opus",
2047
2053
  timeout=21600,
2048
2054
  output_format="text",
2049
2055
  allowed_tools="Read,Write,Edit,Glob,Grep,Bash,mcp__nexo__*",
@@ -20,6 +20,14 @@ import sys
20
20
  from datetime import datetime, date, timedelta
21
21
  from pathlib import Path
22
22
 
23
+
24
+ try:
25
+ from client_preferences import resolve_user_model as _resolve_user_model
26
+ _USER_MODEL = _resolve_user_model()
27
+ except Exception:
28
+ _USER_MODEL = ""
29
+
30
+
23
31
  NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
24
32
  # Auto-detect: if running from repo (src/scripts/), use src/ as NEXO_CODE
25
33
  _script_dir = Path(__file__).resolve().parent
@@ -218,7 +226,7 @@ def call_claude_cli(prompt: str) -> str:
218
226
  """Call the configured automation backend for the managed evolution prompt."""
219
227
  result = run_automation_prompt(
220
228
  prompt,
221
- model="opus",
229
+ model=_USER_MODEL or "opus",
222
230
  timeout=CLI_TIMEOUT,
223
231
  output_format="text",
224
232
  allowed_tools="Read,Write,Edit,Glob,Grep,Bash,mcp__nexo__*",
@@ -234,7 +242,7 @@ def call_public_claude_cli(prompt: str, *, cwd: Path) -> str:
234
242
  prompt,
235
243
  cwd=cwd,
236
244
  env={"NEXO_PUBLIC_CONTRIBUTION": "1"},
237
- model="opus",
245
+ model=_USER_MODEL or "opus",
238
246
  timeout=CLI_TIMEOUT,
239
247
  output_format="text",
240
248
  allowed_tools="Read,Write,Edit,Glob,Grep,Bash",
@@ -23,6 +23,13 @@ import time
23
23
  from datetime import datetime, date, timedelta
24
24
  from pathlib import Path
25
25
 
26
+
27
+ try:
28
+ from client_preferences import resolve_user_model as _resolve_user_model
29
+ _USER_MODEL = _resolve_user_model()
30
+ except Exception:
31
+ _USER_MODEL = ""
32
+
26
33
  NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
27
34
  _script_dir = Path(__file__).resolve().parent
28
35
  _repo_src = _script_dir.parent
@@ -908,7 +915,7 @@ Write the report. Be concise — max 40 lines."""
908
915
  try:
909
916
  result = run_automation_prompt(
910
917
  prompt,
911
- model="opus",
918
+ model=_USER_MODEL or "opus",
912
919
  timeout=21600,
913
920
  output_format="text",
914
921
  allowed_tools="Read,Write,Edit,Glob,Grep,Bash,mcp__nexo__*",
@@ -37,6 +37,13 @@ if str(NEXO_CODE) not in sys.path:
37
37
 
38
38
  from agent_runner import AutomationBackendUnavailableError, run_automation_prompt
39
39
 
40
+ try:
41
+ from client_preferences import resolve_user_model as _resolve_user_model
42
+ _USER_MODEL = _resolve_user_model()
43
+ except Exception:
44
+ _USER_MODEL = ""
45
+
46
+
40
47
 
41
48
  NEXO_DB = NEXO_HOME / "data" / "nexo.db"
42
49
  JSON_ONLY_SYSTEM_PROMPT = (
@@ -151,7 +158,7 @@ Rules:
151
158
  try:
152
159
  result = run_automation_prompt(
153
160
  prompt,
154
- model="sonnet",
161
+ model=_USER_MODEL or "sonnet",
155
162
  timeout=60,
156
163
  output_format="text",
157
164
  append_system_prompt=JSON_ONLY_SYSTEM_PROMPT,
@@ -231,6 +238,7 @@ def _extract_keywords(text: str) -> set:
231
238
 
232
239
  def main():
233
240
  import argparse
241
+
234
242
  parser = argparse.ArgumentParser(description="Validate findings against existing NEXO learnings")
235
243
  parser.add_argument("finding", help="The finding text to validate")
236
244
  parser.add_argument("--category", "-c", help="Filter learnings by category")
@@ -38,6 +38,13 @@ sys.path.insert(0, str(NEXO_CODE))
38
38
 
39
39
  from agent_runner import AutomationBackendUnavailableError, run_automation_prompt
40
40
 
41
+ try:
42
+ from client_preferences import resolve_user_model as _resolve_user_model
43
+ _USER_MODEL = _resolve_user_model()
44
+ except Exception:
45
+ _USER_MODEL = ""
46
+
47
+
41
48
  NEXO_DB = NEXO_HOME / "data" / "nexo.db"
42
49
  # Memory directory — adjust to match your project's memory location
43
50
  MEMORY_DIR = NEXO_HOME / "memory"
@@ -212,7 +219,7 @@ Execute without asking."""
212
219
  try:
213
220
  result = run_automation_prompt(
214
221
  prompt,
215
- model="opus",
222
+ model=_USER_MODEL or "opus",
216
223
  timeout=21600,
217
224
  output_format="text",
218
225
  allowed_tools="Read,Write,Edit,Glob,Grep,Bash,mcp__nexo__*",
@@ -348,6 +355,7 @@ def analyze_force_events():
348
355
  log(f" {len(today_forces)} --force events")
349
356
 
350
357
  from collections import Counter
358
+
351
359
  memory_counts = Counter(r["memory_id"] for r in today_forces)
352
360
  for mem_id, count in memory_counts.most_common():
353
361
  mem = db.execute(
@@ -37,6 +37,12 @@ if str(NEXO_CODE) not in sys.path:
37
37
  sys.path.insert(0, str(NEXO_CODE))
38
38
 
39
39
  from agent_runner import AutomationBackendUnavailableError, run_automation_prompt
40
+ try:
41
+ from client_preferences import resolve_user_model as _resolve_user_model
42
+ _USER_MODEL = _resolve_user_model()
43
+ except Exception:
44
+ _USER_MODEL = ""
45
+
40
46
 
41
47
  # ─── Paths ────────────────────────────────────────────────────────────────────
42
48
  CLAUDE_DIR = NEXO_HOME
@@ -439,7 +445,7 @@ Execute without asking."""
439
445
  try:
440
446
  result = run_automation_prompt(
441
447
  prompt,
442
- model="opus",
448
+ model=_USER_MODEL or "opus",
443
449
  timeout=21600,
444
450
  output_format="text",
445
451
  allowed_tools="Read,Write,Edit,Glob,Grep,Bash,mcp__nexo__*",
@@ -18,6 +18,13 @@ import sys
18
18
  from datetime import datetime, date, timedelta
19
19
  from pathlib import Path
20
20
 
21
+
22
+ try:
23
+ from client_preferences import resolve_user_model as _resolve_user_model
24
+ _USER_MODEL = _resolve_user_model()
25
+ except Exception:
26
+ _USER_MODEL = ""
27
+
21
28
  HOME = Path.home()
22
29
  NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
23
30
  _script_dir = Path(__file__).resolve().parent
@@ -340,7 +347,7 @@ Execute without asking."""
340
347
  try:
341
348
  result = run_automation_prompt(
342
349
  prompt,
343
- model="opus",
350
+ model=_USER_MODEL or "opus",
344
351
  timeout=21600,
345
352
  output_format="text",
346
353
  allowed_tools="Read,Write,Edit,Glob,Grep,Bash,mcp__nexo__*",
@@ -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