nexo-brain 7.31.4 → 7.31.5
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.31.
|
|
3
|
+
"version": "7.31.5",
|
|
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,9 +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.31.
|
|
21
|
+
Version `7.31.5` is the current packaged-runtime line. Patch release over v7.31.4 - the breaker resume notice exists: when a notified engine pause recovers, the operator gets exactly one resume email in their language, signed by their agent.
|
|
22
22
|
|
|
23
|
-
Previously in `7.31.
|
|
23
|
+
Previously in `7.31.4`: patch release over v7.31.3 - memory recall honours absolute time ranges (ISO dates, start..end ranges, datetimes, epochs) and enforces the window in SQL, so asking about a specific past day returns that day.
|
|
24
24
|
|
|
25
25
|
Previously in `7.30.33`: patch release over v7.30.32 - personal agent/script status now keeps the newest real run between manual executions and cron history, so a successful manual agent run cannot be hidden behind an older scheduled failure.
|
|
26
26
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.31.
|
|
3
|
+
"version": "7.31.5",
|
|
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",
|
|
@@ -190,6 +190,9 @@ def record_session_outcome(
|
|
|
190
190
|
"consecutive_failures": 0,
|
|
191
191
|
"closed_at": _now(),
|
|
192
192
|
"recovered_from": entry.get("reason") if was_open else None,
|
|
193
|
+
# The pause email promises "another notice when work resumes":
|
|
194
|
+
# arm that notice only when the OPENING was actually notified.
|
|
195
|
+
"resume_notice_pending": bool(was_open and entry.get("operator_notified_at")),
|
|
193
196
|
}
|
|
194
197
|
_save_state(state)
|
|
195
198
|
return state[backend]
|
|
@@ -213,6 +216,21 @@ def record_session_outcome(
|
|
|
213
216
|
return entry
|
|
214
217
|
|
|
215
218
|
|
|
219
|
+
def should_notify_operator_resumed(backend: str) -> bool:
|
|
220
|
+
"""True exactly once after a notified opening closes (engine resumed).
|
|
221
|
+
|
|
222
|
+
The pause notice tells the operator "you will get another notice when
|
|
223
|
+
work resumes" — this is that notice's gate. Clears the flag on read.
|
|
224
|
+
"""
|
|
225
|
+
state = _load_state()
|
|
226
|
+
entry = _entry(state, backend)
|
|
227
|
+
if entry.get("state") == "closed" and entry.get("resume_notice_pending"):
|
|
228
|
+
entry["resume_notice_pending"] = False
|
|
229
|
+
_save_state(state)
|
|
230
|
+
return True
|
|
231
|
+
return False
|
|
232
|
+
|
|
233
|
+
|
|
216
234
|
def should_notify_operator(backend: str) -> bool:
|
|
217
235
|
"""True exactly once per opening — callers use it to send ONE notice."""
|
|
218
236
|
state = _load_state()
|
|
@@ -2443,6 +2443,52 @@ def _decrement_attempts(email_ids):
|
|
|
2443
2443
|
log.warning(f"Failed to decrement attempts: {e}")
|
|
2444
2444
|
|
|
2445
2445
|
|
|
2446
|
+
def _notify_provider_breaker_resumed_once():
|
|
2447
|
+
"""Send the resume notice the pause email promises — once per recovery."""
|
|
2448
|
+
try:
|
|
2449
|
+
from provider_circuit_breaker import should_notify_operator_resumed
|
|
2450
|
+
for backend in ("claude_code", "codex"):
|
|
2451
|
+
if not should_notify_operator_resumed(backend):
|
|
2452
|
+
continue
|
|
2453
|
+
operator_name, assistant_name, operator_language = _get_operator_info()
|
|
2454
|
+
config = load_config()
|
|
2455
|
+
operator_email = config.get("operator_email", "") if config else ""
|
|
2456
|
+
if not operator_email:
|
|
2457
|
+
return
|
|
2458
|
+
if _uses_spanish(operator_language):
|
|
2459
|
+
subject = f"[{assistant_name}] Motor {backend} reanudado"
|
|
2460
|
+
body = (
|
|
2461
|
+
f"Hola {operator_name},\n\n"
|
|
2462
|
+
f"El motor {backend} vuelve a estar disponible y he reanudado las automatizaciones.\n\n"
|
|
2463
|
+
"La cola pendiente se está procesando ya, en orden. No tienes que hacer nada.\n\n"
|
|
2464
|
+
f"— {assistant_name}"
|
|
2465
|
+
)
|
|
2466
|
+
else:
|
|
2467
|
+
subject = f"[{assistant_name}] Engine {backend} resumed"
|
|
2468
|
+
body = (
|
|
2469
|
+
f"Hello {operator_name},\n\n"
|
|
2470
|
+
f"The {backend} engine is available again and I have resumed the automations.\n\n"
|
|
2471
|
+
"The pending queue is being processed now, in order. Nothing is needed from you.\n\n"
|
|
2472
|
+
f"— {assistant_name}"
|
|
2473
|
+
)
|
|
2474
|
+
body_file = BASE_DIR / ".breaker-resume-body.txt"
|
|
2475
|
+
body_file.write_text(body, encoding="utf-8")
|
|
2476
|
+
send_script = get_send_reply_script_path(local_script_dir=_script_dir)
|
|
2477
|
+
subprocess.run(
|
|
2478
|
+
[
|
|
2479
|
+
sys.executable, str(send_script),
|
|
2480
|
+
"--to", f"{operator_name} <{operator_email}>",
|
|
2481
|
+
"--subject", subject,
|
|
2482
|
+
"--body-file", str(body_file),
|
|
2483
|
+
],
|
|
2484
|
+
timeout=30,
|
|
2485
|
+
capture_output=True,
|
|
2486
|
+
)
|
|
2487
|
+
log.info(f"Breaker resume notice sent for {backend}")
|
|
2488
|
+
except Exception as e:
|
|
2489
|
+
log.warning(f"Breaker resume notice failed: {e}")
|
|
2490
|
+
|
|
2491
|
+
|
|
2446
2492
|
def _notify_provider_breaker_open_once(error):
|
|
2447
2493
|
"""Fase 1.6 — ONE operator notice per breaker opening, in their language.
|
|
2448
2494
|
|
|
@@ -2675,6 +2721,7 @@ def main():
|
|
|
2675
2721
|
backoff_state = load_empty_inbox_backoff_state()
|
|
2676
2722
|
debt_block = scan_debt()
|
|
2677
2723
|
|
|
2724
|
+
_notify_provider_breaker_resumed_once()
|
|
2678
2725
|
reconcile_orphaned_seen(config, hours=24)
|
|
2679
2726
|
reconcile_terminal_unseen(config, hours=48)
|
|
2680
2727
|
# Recovery window widened from 24h to 7 days (168h): a single email can
|