nexo-brain 7.31.8 → 7.31.10
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/README.md +5 -1
- package/package.json +1 -1
- package/src/local_context/api.py +113 -1
- package/src/plugins/protocol.py +74 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.31.
|
|
3
|
+
"version": "7.31.10",
|
|
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,11 @@
|
|
|
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.10` is the current packaged-runtime line. Patch release over v7.31.9 - Local Memory search now downranks boilerplate emails when stronger documents match the same query.
|
|
22
|
+
|
|
23
|
+
Previously in `7.31.9`: patch release over v7.31.8 - UI release closeout now has to prove the original reported symptom was reopened with observable evidence before claiming the release is ready.
|
|
24
|
+
|
|
25
|
+
Previously in `7.31.8`: patch release over v7.31.7 - email monitor debt scans no longer escalate intentionally waiting threads as unresolved commitments when recent resolution or hot-context state proves the thread is waiting on the user or a third party. Real unresolved commitments still surface.
|
|
22
26
|
|
|
23
27
|
Previously in `7.31.7`: patch release over v7.31.6 - stateful answers now require evidence before claiming release, commit, branch, server, ticket, deployment, sent/uploaded, installed, verified, or closed status. Guardian defaults promote identity coherence to hard/core and add a hard/core pre-answer evidence gate, while closeout and Local Context telemetry now leave stronger proof trails.
|
|
24
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.31.
|
|
3
|
+
"version": "7.31.10",
|
|
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/local_context/api.py
CHANGED
|
@@ -4101,6 +4101,118 @@ def _search_text_score(query: str, text: str) -> float:
|
|
|
4101
4101
|
return len(q & tokens) / max(len(q), 1)
|
|
4102
4102
|
|
|
4103
4103
|
|
|
4104
|
+
_CONTEXT_STRONG_DOCUMENT_TERMS = {
|
|
4105
|
+
"acuerdo",
|
|
4106
|
+
"agreement",
|
|
4107
|
+
"balance",
|
|
4108
|
+
"certificado",
|
|
4109
|
+
"comunicado",
|
|
4110
|
+
"contract",
|
|
4111
|
+
"contrato",
|
|
4112
|
+
"declaracion",
|
|
4113
|
+
"declaración",
|
|
4114
|
+
"factura",
|
|
4115
|
+
"invoice",
|
|
4116
|
+
"nomina",
|
|
4117
|
+
"nómina",
|
|
4118
|
+
"payroll",
|
|
4119
|
+
"presupuesto",
|
|
4120
|
+
"quote",
|
|
4121
|
+
"transferencia",
|
|
4122
|
+
}
|
|
4123
|
+
|
|
4124
|
+
|
|
4125
|
+
_CONTEXT_BOILERPLATE_TERMS = {
|
|
4126
|
+
"-ms-text-size-adjust",
|
|
4127
|
+
"-webkit-text-size-adjust",
|
|
4128
|
+
"body, table",
|
|
4129
|
+
"cancelar suscripcion",
|
|
4130
|
+
"cancelar suscripción",
|
|
4131
|
+
"css",
|
|
4132
|
+
"newsletter",
|
|
4133
|
+
"no puedes ver el correo",
|
|
4134
|
+
"politica de privacidad",
|
|
4135
|
+
"política de privacidad",
|
|
4136
|
+
"unsubscribe",
|
|
4137
|
+
"version web",
|
|
4138
|
+
"versión web",
|
|
4139
|
+
}
|
|
4140
|
+
|
|
4141
|
+
|
|
4142
|
+
_CONTEXT_MARKETING_TERMS = {
|
|
4143
|
+
"bajan los precios",
|
|
4144
|
+
"campaña",
|
|
4145
|
+
"descuento",
|
|
4146
|
+
"encuesta satisfaccion",
|
|
4147
|
+
"encuesta satisfacción",
|
|
4148
|
+
"hazte con tu regalo",
|
|
4149
|
+
"oferta",
|
|
4150
|
+
"promocion",
|
|
4151
|
+
"promoción",
|
|
4152
|
+
"prueba gratis",
|
|
4153
|
+
"satisfaccion clientes",
|
|
4154
|
+
"satisfacción clientes",
|
|
4155
|
+
"view online",
|
|
4156
|
+
}
|
|
4157
|
+
|
|
4158
|
+
|
|
4159
|
+
_CONTEXT_LOW_SIGNAL_EMAIL_TERMS = {
|
|
4160
|
+
"acceso bloqueado",
|
|
4161
|
+
"actividad inusual",
|
|
4162
|
+
"bloqueada temporalmente",
|
|
4163
|
+
"cuenta esta en revision",
|
|
4164
|
+
"cuenta está en revisión",
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
|
|
4168
|
+
def _contains_any_text(text: str, terms: set[str]) -> bool:
|
|
4169
|
+
if not text:
|
|
4170
|
+
return False
|
|
4171
|
+
return any(term in text for term in terms)
|
|
4172
|
+
|
|
4173
|
+
|
|
4174
|
+
def _context_quality_adjusted_score(score: float, row: Any) -> float:
|
|
4175
|
+
"""Apply deterministic tie-breaks for local search candidates.
|
|
4176
|
+
|
|
4177
|
+
The base scorer is intentionally simple, which can make boilerplate emails
|
|
4178
|
+
tie with PDFs/contracts when a query has a few common terms. Keep this
|
|
4179
|
+
adjustment small and explainable: strong document signals stay high, while
|
|
4180
|
+
newsletter/CSS/legal-footer noise loses the tie.
|
|
4181
|
+
"""
|
|
4182
|
+
try:
|
|
4183
|
+
path = str(row["path"] or "")
|
|
4184
|
+
file_type = str(row["file_type"] or "").strip().lower()
|
|
4185
|
+
text = str(row["text"] or "")
|
|
4186
|
+
summary = str(row["summary"] or "")
|
|
4187
|
+
except Exception:
|
|
4188
|
+
return max(0.0, min(float(score), 1.6))
|
|
4189
|
+
|
|
4190
|
+
haystack = f"{path}\n{summary}\n{text}".lower()
|
|
4191
|
+
suffix = Path(path).suffix.lower()
|
|
4192
|
+
adjustment = 0.0
|
|
4193
|
+
|
|
4194
|
+
if suffix in HIGH_VALUE_DOCUMENT_SUFFIXES or file_type in {"document", "spreadsheet", "presentation"}:
|
|
4195
|
+
adjustment += 0.12
|
|
4196
|
+
has_strong_document_signal = _contains_any_text(haystack, _CONTEXT_STRONG_DOCUMENT_TERMS)
|
|
4197
|
+
if has_strong_document_signal:
|
|
4198
|
+
adjustment += 0.12
|
|
4199
|
+
|
|
4200
|
+
if file_type == "email" or suffix in EMAIL_DOCUMENT_SUFFIXES:
|
|
4201
|
+
if _contains_any_text(haystack, _CONTEXT_BOILERPLATE_TERMS):
|
|
4202
|
+
adjustment -= 0.22
|
|
4203
|
+
if _contains_any_text(haystack, _CONTEXT_MARKETING_TERMS):
|
|
4204
|
+
adjustment -= 0.18
|
|
4205
|
+
if _contains_any_text(haystack, _CONTEXT_LOW_SIGNAL_EMAIL_TERMS):
|
|
4206
|
+
adjustment -= 0.16
|
|
4207
|
+
if has_strong_document_signal:
|
|
4208
|
+
adjustment += 0.08
|
|
4209
|
+
else:
|
|
4210
|
+
adjustment -= 0.22
|
|
4211
|
+
|
|
4212
|
+
base = min(float(score), 1.6)
|
|
4213
|
+
return max(0.0, min(base + adjustment, 1.6))
|
|
4214
|
+
|
|
4215
|
+
|
|
4104
4216
|
_QUERY_STOPWORDS = {
|
|
4105
4217
|
"about",
|
|
4106
4218
|
"archivos",
|
|
@@ -4739,7 +4851,7 @@ def _context_query_conn(
|
|
|
4739
4851
|
# unrelated chunks from a long document outrank direct evidence.
|
|
4740
4852
|
score = max(score, min(0.48, 0.28 + entity_score * 0.2))
|
|
4741
4853
|
if score > 0:
|
|
4742
|
-
scored.append((
|
|
4854
|
+
scored.append((_context_quality_adjusted_score(float(score), row), row))
|
|
4743
4855
|
scored.sort(key=lambda item: item[0], reverse=True)
|
|
4744
4856
|
scored = _rerank_scored_candidates(search_query, scored, limit=int(limit))
|
|
4745
4857
|
assets = []
|
package/src/plugins/protocol.py
CHANGED
|
@@ -676,6 +676,42 @@ def _has_live_surface_verification(evidence: str) -> bool:
|
|
|
676
676
|
return bool(re.search(r"\b(producci[oó]n|production|live)\b.*\b(logs?|bd|db|browser|navegador|playwright|url|http|tema)\b", text))
|
|
677
677
|
|
|
678
678
|
|
|
679
|
+
UI_CHANGE_HINT_RE = re.compile(
|
|
680
|
+
r"\b(ui|ux|front[- ]?end|frontend|renderer|web|browser|navegador|pantalla|modal|selector|bot[oó]n|button|css|html|react|vue|electron|theme|tema|storefront)\b",
|
|
681
|
+
re.IGNORECASE,
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
RELEASE_READY_CLAIM_RE = re.compile(
|
|
685
|
+
r"\b(release\s+lista|feature\s+completa|desplegado\s+ok|despliegue\s+ok|lista\s+para\s+release|release\s+ready|feature\s+complete|deployed\s+ok)\b",
|
|
686
|
+
re.IGNORECASE,
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
ORIGINAL_SYMPTOM_REPRO_RE = re.compile(
|
|
690
|
+
r"\b(s[ií]ntoma|symptom|original|repro|reproduj|reproduc|reportad[oa]|observad[oa])\b",
|
|
691
|
+
re.IGNORECASE,
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
ORIGINAL_SYMPTOM_EVIDENCE_RE = re.compile(
|
|
695
|
+
r"\b(curl|http\s*200|url\s+(?:p[uú]blica|repro)|screenshot|captura|headed|browser|navegador|playwright)\b",
|
|
696
|
+
re.IGNORECASE,
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def _requires_original_symptom_verification(task: dict, closure_text: str) -> bool:
|
|
701
|
+
if not RELEASE_READY_CLAIM_RE.search(closure_text or ""):
|
|
702
|
+
return False
|
|
703
|
+
combined = " ".join(
|
|
704
|
+
str(task.get(field) or "")
|
|
705
|
+
for field in ("goal", "area", "project_hint", "context_hint", "verification_step", "files")
|
|
706
|
+
)
|
|
707
|
+
return bool(UI_CHANGE_HINT_RE.search(combined))
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
def _has_original_symptom_verification(evidence: str) -> bool:
|
|
711
|
+
text = evidence or ""
|
|
712
|
+
return bool(ORIGINAL_SYMPTOM_REPRO_RE.search(text) and ORIGINAL_SYMPTOM_EVIDENCE_RE.search(text))
|
|
713
|
+
|
|
714
|
+
|
|
679
715
|
def _requires_production_change_log(
|
|
680
716
|
task: dict,
|
|
681
717
|
outcome: str,
|
|
@@ -1870,6 +1906,44 @@ def handle_task_close(
|
|
|
1870
1906
|
indent=2,
|
|
1871
1907
|
)
|
|
1872
1908
|
|
|
1909
|
+
original_symptom_evidence = "\n".join(
|
|
1910
|
+
part
|
|
1911
|
+
for part in (clean_evidence, clean_change_verify, outcome_notes, verification, summary, result)
|
|
1912
|
+
if part
|
|
1913
|
+
)
|
|
1914
|
+
if (
|
|
1915
|
+
clean_outcome == "done"
|
|
1916
|
+
and _requires_original_symptom_verification(task, closure_text)
|
|
1917
|
+
and not _has_original_symptom_verification(original_symptom_evidence)
|
|
1918
|
+
):
|
|
1919
|
+
debt = _ensure_open_debt(
|
|
1920
|
+
task["session_id"],
|
|
1921
|
+
task_id,
|
|
1922
|
+
"verify_original_symptom_missing",
|
|
1923
|
+
severity="error",
|
|
1924
|
+
evidence=(
|
|
1925
|
+
"UI release-ready claim attempted without evidence that the original symptom was reproduced. "
|
|
1926
|
+
f"Goal: {task.get('goal','')}. Evidence provided: {original_symptom_evidence[:240]!r}"
|
|
1927
|
+
),
|
|
1928
|
+
debts=debts_created,
|
|
1929
|
+
)
|
|
1930
|
+
return json.dumps(
|
|
1931
|
+
{
|
|
1932
|
+
"ok": False,
|
|
1933
|
+
"error": "Cannot close UI release as done without original-symptom verification evidence.",
|
|
1934
|
+
"hint": (
|
|
1935
|
+
"Attach proof that the reported symptom itself was reopened and reproduced/verified: "
|
|
1936
|
+
"repro URL, headed screenshot/browser evidence, Playwright output, or curl output."
|
|
1937
|
+
),
|
|
1938
|
+
"task_id": task_id,
|
|
1939
|
+
"blocked_by": "verify_original_symptom",
|
|
1940
|
+
"debt_id": debt.get("id"),
|
|
1941
|
+
"debt_type": "verify_original_symptom_missing",
|
|
1942
|
+
},
|
|
1943
|
+
ensure_ascii=False,
|
|
1944
|
+
indent=2,
|
|
1945
|
+
)
|
|
1946
|
+
|
|
1873
1947
|
live_surface_required = _requires_live_surface_verification(task, clean_outcome)
|
|
1874
1948
|
live_surface_evidence = "\n".join(
|
|
1875
1949
|
part
|