nexo-brain 5.3.23 → 5.3.24
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/package.json +1 -1
- package/src/cli.py +17 -2
- package/src/model_defaults.py +32 -11
- package/src/plugins/update.py +19 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.24",
|
|
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.
|
|
3
|
+
"version": "5.3.24",
|
|
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/cli.py
CHANGED
|
@@ -938,7 +938,21 @@ def _prompt_model_recommendations(*, interactive: bool) -> None:
|
|
|
938
938
|
from model_defaults import detect_outdated_recommendations, client_default
|
|
939
939
|
|
|
940
940
|
preferences = load_client_preferences()
|
|
941
|
-
|
|
941
|
+
result = detect_outdated_recommendations(preferences)
|
|
942
|
+
pending = result.get("pending") or []
|
|
943
|
+
auto_ack = result.get("auto_ack") or {}
|
|
944
|
+
|
|
945
|
+
# Apply silent acknowledgements first (user already on current model, or
|
|
946
|
+
# has customized their model — either way no prompt needed). This avoids
|
|
947
|
+
# repeated stderr hints in cron/headless updates.
|
|
948
|
+
if auto_ack:
|
|
949
|
+
try:
|
|
950
|
+
existing_ack = dict(preferences.get("acknowledged_model_recommendations") or {})
|
|
951
|
+
existing_ack.update({k: int(v) for k, v in auto_ack.items()})
|
|
952
|
+
save_client_preferences(acknowledged_model_recommendations=existing_ack)
|
|
953
|
+
except Exception:
|
|
954
|
+
pass
|
|
955
|
+
|
|
942
956
|
if not pending:
|
|
943
957
|
return
|
|
944
958
|
|
|
@@ -955,7 +969,8 @@ def _prompt_model_recommendations(*, interactive: bool) -> None:
|
|
|
955
969
|
|
|
956
970
|
updated_profiles = dict(preferences.get("client_runtime_profiles") or {})
|
|
957
971
|
updated_ack = dict(preferences.get("acknowledged_model_recommendations") or {})
|
|
958
|
-
|
|
972
|
+
updated_ack.update({k: int(v) for k, v in auto_ack.items()})
|
|
973
|
+
changed = bool(auto_ack)
|
|
959
974
|
for entry in pending:
|
|
960
975
|
client = entry["client"]
|
|
961
976
|
effort_str = f" / {entry['current_effort']}" if entry["current_effort"] else ""
|
package/src/model_defaults.py
CHANGED
|
@@ -126,9 +126,22 @@ def heal_runtime_profiles(profiles: dict) -> tuple[dict, list[str]]:
|
|
|
126
126
|
|
|
127
127
|
def detect_outdated_recommendations(
|
|
128
128
|
preferences: dict,
|
|
129
|
-
) ->
|
|
130
|
-
"""
|
|
131
|
-
|
|
129
|
+
) -> dict:
|
|
130
|
+
"""Classify clients into "needs interactive prompt" vs "silent ack".
|
|
131
|
+
|
|
132
|
+
Returns a dict with two keys:
|
|
133
|
+
- ``pending``: list of entries that require an interactive prompt
|
|
134
|
+
because the user is still on an older NEXO-recommended model and a
|
|
135
|
+
newer recommendation is available.
|
|
136
|
+
- ``auto_ack``: mapping of ``{client: recommendation_version}`` that
|
|
137
|
+
should be acknowledged silently without prompting (because either
|
|
138
|
+
the user already matches the current recommended model, or they
|
|
139
|
+
have customized their model and we must respect that silently).
|
|
140
|
+
|
|
141
|
+
Saving the ``auto_ack`` entries prevents repeated stderr spam in
|
|
142
|
+
non-interactive (cron/headless) update runs.
|
|
143
|
+
|
|
144
|
+
``pending`` entry shape:
|
|
132
145
|
{
|
|
133
146
|
"client": "codex",
|
|
134
147
|
"current_model": "gpt-5.9",
|
|
@@ -139,7 +152,8 @@ def detect_outdated_recommendations(
|
|
|
139
152
|
"display_name": "GPT-5.9 with high reasoning",
|
|
140
153
|
}
|
|
141
154
|
"""
|
|
142
|
-
|
|
155
|
+
pending: list[dict] = []
|
|
156
|
+
auto_ack: dict[str, int] = {}
|
|
143
157
|
profiles = preferences.get("client_runtime_profiles") or {}
|
|
144
158
|
acknowledged = preferences.get("acknowledged_model_recommendations") or {}
|
|
145
159
|
for client in ("claude_code", "codex"):
|
|
@@ -153,15 +167,22 @@ def detect_outdated_recommendations(
|
|
|
153
167
|
ack_v = int(acknowledged.get(client) or 0)
|
|
154
168
|
if current_v <= ack_v:
|
|
155
169
|
continue
|
|
156
|
-
#
|
|
157
|
-
#
|
|
170
|
+
# User customized their model entirely (not a previously recommended
|
|
171
|
+
# NEXO default) — respect their choice and record ack silently so we
|
|
172
|
+
# don't log a hint every `nexo update`.
|
|
158
173
|
if not was_nexo_default(client, user_model):
|
|
174
|
+
auto_ack[client] = current_v
|
|
159
175
|
continue
|
|
160
|
-
#
|
|
161
|
-
#
|
|
162
|
-
|
|
176
|
+
# User is already on the current recommended model. Even if their
|
|
177
|
+
# reasoning_effort differs (e.g. they picked "max" at onboarding and
|
|
178
|
+
# the JSON stores ""), nothing to migrate — their effort is a
|
|
179
|
+
# personal choice layered on top of the recommended model.
|
|
180
|
+
if user_model == default["model"]:
|
|
181
|
+
auto_ack[client] = current_v
|
|
163
182
|
continue
|
|
164
|
-
|
|
183
|
+
# User is on a prior NEXO default and the current recommendation is
|
|
184
|
+
# a different model → offer interactive upgrade.
|
|
185
|
+
pending.append({
|
|
165
186
|
"client": client,
|
|
166
187
|
"current_model": default["model"],
|
|
167
188
|
"current_effort": default["reasoning_effort"],
|
|
@@ -170,4 +191,4 @@ def detect_outdated_recommendations(
|
|
|
170
191
|
"user_effort": user_effort,
|
|
171
192
|
"display_name": default.get("display_name") or default["model"],
|
|
172
193
|
})
|
|
173
|
-
return
|
|
194
|
+
return {"pending": pending, "auto_ack": auto_ack}
|
package/src/plugins/update.py
CHANGED
|
@@ -442,9 +442,17 @@ def _restore_code_tree(backup_dir: str) -> str | None:
|
|
|
442
442
|
|
|
443
443
|
def _normalize_preferences_for_client_sync() -> dict:
|
|
444
444
|
from client_preferences import normalize_client_preferences
|
|
445
|
+
from model_defaults import heal_runtime_profiles
|
|
445
446
|
|
|
446
447
|
schedule_path = NEXO_HOME / "config" / "schedule.json"
|
|
447
448
|
schedule_payload = json.loads(schedule_path.read_text()) if schedule_path.exists() else {}
|
|
449
|
+
# Heal invalid models (e.g. Claude-family written into codex profile by
|
|
450
|
+
# earlier buggy versions). Must run BEFORE normalize so the healed values
|
|
451
|
+
# propagate into preferences and downstream client config files.
|
|
452
|
+
existing_profiles = schedule_payload.get("client_runtime_profiles") or {}
|
|
453
|
+
healed_profiles, _heal_messages = heal_runtime_profiles(existing_profiles)
|
|
454
|
+
if _heal_messages:
|
|
455
|
+
schedule_payload["client_runtime_profiles"] = healed_profiles
|
|
448
456
|
normalized_preferences = normalize_client_preferences(schedule_payload)
|
|
449
457
|
if normalized_preferences != {
|
|
450
458
|
key: schedule_payload.get(key)
|
|
@@ -902,9 +910,20 @@ def handle_update(remote: str = "origin", branch: str = "main", progress_fn=None
|
|
|
902
910
|
_emit_progress(progress_fn, "Refreshing shared client configs...")
|
|
903
911
|
from client_sync import sync_all_clients
|
|
904
912
|
from client_preferences import normalize_client_preferences
|
|
913
|
+
from model_defaults import heal_runtime_profiles
|
|
905
914
|
|
|
906
915
|
schedule_path = NEXO_HOME / "config" / "schedule.json"
|
|
907
916
|
schedule_payload = json.loads(schedule_path.read_text()) if schedule_path.exists() else {}
|
|
917
|
+
# Heal Claude-family models written into Codex profile by earlier
|
|
918
|
+
# buggy versions. Must run BEFORE normalize so healed values
|
|
919
|
+
# propagate into the saved preferences.
|
|
920
|
+
existing_profiles = schedule_payload.get("client_runtime_profiles") or {}
|
|
921
|
+
healed_profiles, heal_messages = heal_runtime_profiles(existing_profiles)
|
|
922
|
+
if heal_messages:
|
|
923
|
+
schedule_payload["client_runtime_profiles"] = healed_profiles
|
|
924
|
+
for msg in heal_messages:
|
|
925
|
+
_emit_progress(progress_fn, msg)
|
|
926
|
+
steps_done.append("model-heal")
|
|
908
927
|
normalized_preferences = normalize_client_preferences(schedule_payload)
|
|
909
928
|
if normalized_preferences != {
|
|
910
929
|
key: schedule_payload.get(key)
|