nexo-brain 7.1.6 → 7.1.7
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.1.
|
|
3
|
+
"version": "7.1.7",
|
|
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,7 @@
|
|
|
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.1.
|
|
21
|
+
Version `7.1.7` is the current packaged-runtime line. It hardens operator-facing email automation language: `email-monitor` now carries the calibrated operator language through its prompt contract and localizes direct `needs_interactive` escalation emails, so Spanish operators stop receiving fallback English monitor mail. No coordinated Desktop release was needed for this patch; the fix lives in Brain.
|
|
22
22
|
|
|
23
23
|
Previously in `7.0.1`: hotfix over v7.0.0 (db._core.DB_PATH was only caller still hardcoded to legacy ~/.nexo/data/nexo.db; every shared-DB command silently returned empty results post-migration). Previously in `7.0.0`: **BREAKING — Plan Consolidado fase F0.6**: physical separation of the runtime tree into `~/.nexo/{core,personal,runtime}/`. The flat layout (`~/.nexo/scripts/`, `brain/`, `data/`, `operations/`, ...) is gone. Operators on v6.x are auto-migrated on first `nexo update`; fresh installs land directly in the new tree. New `paths.py` helpers are transition-aware.
|
|
24
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.7",
|
|
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",
|
|
@@ -88,6 +88,7 @@ DEFAULT_AUTOMATION_TASK_PROFILE = "fast"
|
|
|
88
88
|
CONCURRENT_THRESHOLD_MINUTES = 15
|
|
89
89
|
MAX_CONCURRENT_SESSIONS = 2
|
|
90
90
|
MAX_EMAIL_ATTEMPTS = 3
|
|
91
|
+
DEFAULT_OPERATOR_LANGUAGE = "en"
|
|
91
92
|
EMPTY_INBOX_BACKOFF_STEPS = (
|
|
92
93
|
(12, 60 * 60),
|
|
93
94
|
(6, 30 * 60),
|
|
@@ -1502,9 +1503,47 @@ def _get_operator_info():
|
|
|
1502
1503
|
return (
|
|
1503
1504
|
str(profile.get("operator_name") or "the operator"),
|
|
1504
1505
|
str(profile.get("assistant_name") or DEFAULT_ASSISTANT_NAME),
|
|
1506
|
+
str(profile.get("language") or DEFAULT_OPERATOR_LANGUAGE).strip() or DEFAULT_OPERATOR_LANGUAGE,
|
|
1505
1507
|
)
|
|
1506
1508
|
|
|
1507
1509
|
|
|
1510
|
+
def _uses_spanish(language: str) -> bool:
|
|
1511
|
+
normalized = str(language or "").strip().lower().replace("_", "-")
|
|
1512
|
+
return normalized == "es" or normalized.startswith("es-")
|
|
1513
|
+
|
|
1514
|
+
|
|
1515
|
+
def _localized_operator_escalation_email(
|
|
1516
|
+
*,
|
|
1517
|
+
operator_name: str,
|
|
1518
|
+
assistant_name: str,
|
|
1519
|
+
operator_language: str,
|
|
1520
|
+
exhausted_count: int,
|
|
1521
|
+
details: str,
|
|
1522
|
+
) -> tuple[str, str]:
|
|
1523
|
+
if _uses_spanish(operator_language):
|
|
1524
|
+
subject = f"[NEXO] Emails que necesitan atención manual ({exhausted_count})"
|
|
1525
|
+
body = (
|
|
1526
|
+
f"Hola {operator_name},\n\n"
|
|
1527
|
+
f"Los siguientes emails ya se han intentado {MAX_EMAIL_ATTEMPTS} veces "
|
|
1528
|
+
f"sin completarse correctamente (la sesión cae antes de terminar):\n\n{details}\n\n"
|
|
1529
|
+
"Los he marcado como `needs_interactive`. "
|
|
1530
|
+
f"Abre {assistant_name} Desktop y pregúntale por ese email para resolverlo manualmente.\n\n"
|
|
1531
|
+
f"— {assistant_name}"
|
|
1532
|
+
)
|
|
1533
|
+
return subject, body
|
|
1534
|
+
|
|
1535
|
+
subject = f"[NEXO] Emails requiring manual attention ({exhausted_count})"
|
|
1536
|
+
body = (
|
|
1537
|
+
f"Hello {operator_name},\n\n"
|
|
1538
|
+
f"The following emails have already been attempted {MAX_EMAIL_ATTEMPTS} times "
|
|
1539
|
+
f"without succeeding (the session dies before completion):\n\n{details}\n\n"
|
|
1540
|
+
f"I marked them as `needs_interactive`. "
|
|
1541
|
+
f"Open {assistant_name} Desktop and ask about the affected email so it can be resolved manually.\n\n"
|
|
1542
|
+
f"— {assistant_name}"
|
|
1543
|
+
)
|
|
1544
|
+
return subject, body
|
|
1545
|
+
|
|
1546
|
+
|
|
1508
1547
|
def _operator_aliases(config) -> list[str]:
|
|
1509
1548
|
aliases: list[str] = []
|
|
1510
1549
|
profile = get_operator_profile()
|
|
@@ -1573,6 +1612,7 @@ def build_processing_prompt(
|
|
|
1573
1612
|
config,
|
|
1574
1613
|
operator_name: str,
|
|
1575
1614
|
assistant_name: str,
|
|
1615
|
+
operator_language: str,
|
|
1576
1616
|
operator_email: str,
|
|
1577
1617
|
operator_aliases_label: str,
|
|
1578
1618
|
trusted_domains_label: str,
|
|
@@ -1628,6 +1668,7 @@ def build_processing_prompt(
|
|
|
1628
1668
|
recent_hot_context=recent_hot_context,
|
|
1629
1669
|
project_atlas_path=project_atlas_path,
|
|
1630
1670
|
operator_name=operator_name,
|
|
1671
|
+
operator_language=operator_language,
|
|
1631
1672
|
email_db_path=EMAIL_DB_PATH,
|
|
1632
1673
|
debt_sla_hours=DEBT_SLA_HOURS,
|
|
1633
1674
|
zombie_timeout_hours=ZOMBIE_TIMEOUT_HOURS,
|
|
@@ -1650,7 +1691,7 @@ def launch_nexo(config, debt_block="", target_emails=None):
|
|
|
1650
1691
|
"""Launch NEXO through the configured automation backend to process emails.
|
|
1651
1692
|
target_emails: optional list of dicts with message_id, subject, attempts."""
|
|
1652
1693
|
|
|
1653
|
-
operator_name, assistant_name = _get_operator_info()
|
|
1694
|
+
operator_name, assistant_name, operator_language = _get_operator_info()
|
|
1654
1695
|
agent_email = str(config.get("email") or "").strip()
|
|
1655
1696
|
operator_email = config.get("operator_email", "")
|
|
1656
1697
|
operator_aliases = _operator_aliases(config)
|
|
@@ -1678,6 +1719,7 @@ def launch_nexo(config, debt_block="", target_emails=None):
|
|
|
1678
1719
|
config=config,
|
|
1679
1720
|
operator_name=operator_name,
|
|
1680
1721
|
assistant_name=assistant_name,
|
|
1722
|
+
operator_language=operator_language,
|
|
1681
1723
|
operator_email=operator_email,
|
|
1682
1724
|
operator_aliases_label=operator_aliases_label,
|
|
1683
1725
|
trusted_domains_label=trusted_domains_label,
|
|
@@ -1816,7 +1858,7 @@ def _escalate_exhausted_emails(config, batch):
|
|
|
1816
1858
|
_mark_needs_interactive(ids)
|
|
1817
1859
|
log.info(f"Marked {len(ids)} email(s) as needs_interactive after exhausting retries")
|
|
1818
1860
|
|
|
1819
|
-
operator_name, assistant_name = _get_operator_info()
|
|
1861
|
+
operator_name, assistant_name, operator_language = _get_operator_info()
|
|
1820
1862
|
operator_email = config.get("operator_email", "")
|
|
1821
1863
|
if not operator_email:
|
|
1822
1864
|
log.warning("No operator_email configured — cannot send escalation email")
|
|
@@ -1826,13 +1868,12 @@ def _escalate_exhausted_emails(config, batch):
|
|
|
1826
1868
|
f" - Subject: {e.get('subject', '?')} | From: {e.get('from_addr', '?')}"
|
|
1827
1869
|
for e in exhausted
|
|
1828
1870
|
)
|
|
1829
|
-
body = (
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
f"— {assistant_name}"
|
|
1871
|
+
subject, body = _localized_operator_escalation_email(
|
|
1872
|
+
operator_name=operator_name,
|
|
1873
|
+
assistant_name=assistant_name,
|
|
1874
|
+
operator_language=operator_language,
|
|
1875
|
+
exhausted_count=len(exhausted),
|
|
1876
|
+
details=details,
|
|
1836
1877
|
)
|
|
1837
1878
|
body_file = BASE_DIR / ".escalation-body.txt"
|
|
1838
1879
|
body_file.write_text(body, encoding="utf-8")
|
|
@@ -1843,7 +1884,7 @@ def _escalate_exhausted_emails(config, batch):
|
|
|
1843
1884
|
[
|
|
1844
1885
|
sys.executable, str(send_script),
|
|
1845
1886
|
"--to", f"{operator_name} <{operator_email}>",
|
|
1846
|
-
"--subject",
|
|
1887
|
+
"--subject", subject,
|
|
1847
1888
|
"--body-file", str(body_file),
|
|
1848
1889
|
],
|
|
1849
1890
|
timeout=30,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
You are [[assistant_name]] — the operator's autonomous co-operator. This is your mailbox ([[agent_mailbox]]).
|
|
2
2
|
Your CLAUDE.md is already loaded with your working context. USE IT. You are the same NEXO runtime, now operating through email.
|
|
3
|
+
When emailing [[operator_name]] or sending operator-facing escalations, ALWAYS use the operator's preferred language: [[operator_language]].
|
|
4
|
+
For replies to other senders, match the thread's language unless the thread clearly requires another language.
|
|
3
5
|
|
|
4
6
|
== PRELOADED FRESH MEMORY (LAST 24H) ==
|
|
5
7
|
[[recent_hot_context]]
|