nexo-brain 7.27.3 → 7.28.0
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/bin/windows-wsl-bridge.js +9 -0
- package/package.json +1 -1
- package/src/causal_graph.py +763 -0
- package/src/classifier_local.py +44 -0
- package/src/cognitive/_core.py +3 -0
- package/src/cognitive_control_observatory.py +2 -0
- package/src/db/__init__.py +8 -0
- package/src/db/_commitments.py +344 -0
- package/src/db/_entities.py +98 -11
- package/src/db/_memory_v2.py +130 -2
- package/src/db/_schema.py +565 -0
- package/src/desktop_bridge.py +1 -1
- package/src/doctor/providers/runtime.py +9 -3
- package/src/enforcement_engine.py +128 -2
- package/src/entity_live_profile.py +1073 -0
- package/src/failure_prevention.py +1052 -0
- package/src/hook_guardrails.py +104 -0
- package/src/knowledge_graph.py +46 -9
- package/src/local_context/api.py +54 -22
- package/src/local_context/usage_events.py +273 -8
- package/src/memory_executive.py +620 -0
- package/src/memory_utility.py +952 -0
- package/src/plugin_loader.py +9 -5
- package/src/plugins/entities.py +84 -7
- package/src/plugins/entity_live_profile.py +101 -0
- package/src/plugins/failure_prevention.py +162 -0
- package/src/plugins/memory_export.py +55 -18
- package/src/plugins/protocol.py +133 -0
- package/src/plugins/semantic_layers.py +138 -0
- package/src/pre_answer_router.py +622 -28
- package/src/pre_answer_runtime.py +463 -18
- package/src/r14_correction_learning.py +3 -3
- package/src/requirements.txt +5 -1
- package/src/runtime_versioning.py +11 -1
- package/src/saved_not_used_audit.py +44 -3
- package/src/scripts/nexo-followup-runner.py +194 -0
- package/src/semantic_layers.py +1153 -0
- package/src/semantic_reasoner.py +2 -2
- package/src/semantic_router.py +58 -11
- package/src/server.py +41 -3
- package/src/tools_sessions.py +88 -31
- package/src/tools_transcripts.py +38 -22
- package/src/user_state_model.py +971 -0
- package/tool-enforcement-map.json +230 -0
package/src/db/_memory_v2.py
CHANGED
|
@@ -129,6 +129,55 @@ def _redact_value(value: Any) -> tuple[Any, bool]:
|
|
|
129
129
|
return value, False
|
|
130
130
|
|
|
131
131
|
|
|
132
|
+
def _memory_executive_shadow_decision(
|
|
133
|
+
*,
|
|
134
|
+
event_uid: str,
|
|
135
|
+
source_type: str,
|
|
136
|
+
source_id: str,
|
|
137
|
+
event_type: str,
|
|
138
|
+
actor: str,
|
|
139
|
+
session_id: str,
|
|
140
|
+
project_key: str,
|
|
141
|
+
text: str,
|
|
142
|
+
metadata: dict[str, Any],
|
|
143
|
+
privacy_level: str,
|
|
144
|
+
idempotency_key: str,
|
|
145
|
+
created_at: float,
|
|
146
|
+
) -> dict[str, Any]:
|
|
147
|
+
try:
|
|
148
|
+
from memory_executive import audit_record, decide
|
|
149
|
+
except Exception as exc:
|
|
150
|
+
return {"error": f"memory_executive_unavailable:{_truncate(str(exc), 160)}"}
|
|
151
|
+
|
|
152
|
+
raw_refs = metadata.get("evidence_refs") or metadata.get("evidence_ref") or []
|
|
153
|
+
if isinstance(raw_refs, str):
|
|
154
|
+
evidence_refs = [raw_refs]
|
|
155
|
+
elif isinstance(raw_refs, (list, tuple, set)):
|
|
156
|
+
evidence_refs = [str(item) for item in raw_refs if str(item).strip()]
|
|
157
|
+
else:
|
|
158
|
+
evidence_refs = []
|
|
159
|
+
event = {
|
|
160
|
+
"event_uid": event_uid,
|
|
161
|
+
"source_type": source_type,
|
|
162
|
+
"source_id": source_id,
|
|
163
|
+
"event_type": event_type,
|
|
164
|
+
"actor": actor,
|
|
165
|
+
"session_id": session_id,
|
|
166
|
+
"project_key": project_key,
|
|
167
|
+
"text": _truncate(text, 1200),
|
|
168
|
+
"metadata": metadata,
|
|
169
|
+
"evidence_refs": evidence_refs,
|
|
170
|
+
"privacy_level": privacy_level,
|
|
171
|
+
"idempotency_key": idempotency_key,
|
|
172
|
+
"created_at": str(created_at),
|
|
173
|
+
}
|
|
174
|
+
try:
|
|
175
|
+
decision = decide(event, shadow_mode=True)
|
|
176
|
+
return audit_record(event, decision)
|
|
177
|
+
except Exception as exc:
|
|
178
|
+
return {"error": f"memory_executive_error:{_truncate(str(exc), 160)}"}
|
|
179
|
+
|
|
180
|
+
|
|
132
181
|
def _stable_hash(value: Any) -> str:
|
|
133
182
|
if value in (None, ""):
|
|
134
183
|
return ""
|
|
@@ -282,6 +331,35 @@ def record_memory_event(
|
|
|
282
331
|
idempotency_key=idempotency_key,
|
|
283
332
|
created_at=now,
|
|
284
333
|
)
|
|
334
|
+
executive_text = " ".join(
|
|
335
|
+
str(part or "").strip()
|
|
336
|
+
for part in (
|
|
337
|
+
clean_meta.get("summary") if isinstance(clean_meta, dict) else "",
|
|
338
|
+
clean_meta.get("statement") if isinstance(clean_meta, dict) else "",
|
|
339
|
+
clean_meta.get("goal") if isinstance(clean_meta, dict) else "",
|
|
340
|
+
clean_meta.get("outcome") if isinstance(clean_meta, dict) else "",
|
|
341
|
+
clean_command,
|
|
342
|
+
clean_raw_ref,
|
|
343
|
+
)
|
|
344
|
+
if str(part or "").strip()
|
|
345
|
+
)
|
|
346
|
+
if isinstance(clean_meta, dict) and "memory_executive" not in clean_meta:
|
|
347
|
+
clean_meta["memory_executive"] = _memory_executive_shadow_decision(
|
|
348
|
+
event_uid=uid,
|
|
349
|
+
source_type=clean_source_type,
|
|
350
|
+
source_id=_truncate(source_id, 200),
|
|
351
|
+
event_type=clean_event_type,
|
|
352
|
+
actor=_truncate(actor, 120),
|
|
353
|
+
session_id=_truncate(session_id, 160),
|
|
354
|
+
project_key=_truncate(project_key, 120),
|
|
355
|
+
text=executive_text,
|
|
356
|
+
metadata=clean_meta,
|
|
357
|
+
privacy_level="secret" if redaction_applied else (privacy_level or "normal"),
|
|
358
|
+
idempotency_key=idempotency_key or uid,
|
|
359
|
+
created_at=now,
|
|
360
|
+
)
|
|
361
|
+
if clean_meta["memory_executive"].get("decision_kind") == "quarantine":
|
|
362
|
+
enqueue_observation = False
|
|
285
363
|
|
|
286
364
|
try:
|
|
287
365
|
cursor = conn.execute(
|
|
@@ -1028,7 +1106,7 @@ def backfill_memory_observations(
|
|
|
1028
1106
|
return {"ok": True, "sources": sorted(requested), "seen": seen, "created_or_updated": created}
|
|
1029
1107
|
|
|
1030
1108
|
|
|
1031
|
-
def memory_observation_health() -> dict:
|
|
1109
|
+
def memory_observation_health(*, pending_sla_seconds: int = 3600, now: float | None = None) -> dict:
|
|
1032
1110
|
conn = _core().get_db()
|
|
1033
1111
|
tables = {
|
|
1034
1112
|
"memory_events": _table_exists(conn, "memory_events"),
|
|
@@ -1044,11 +1122,40 @@ def memory_observation_health() -> dict:
|
|
|
1044
1122
|
if tables["memory_observations"]:
|
|
1045
1123
|
counts["observations"] = int(conn.execute("SELECT COUNT(*) FROM memory_observations").fetchone()[0])
|
|
1046
1124
|
latest["observation_created_at"] = conn.execute("SELECT MAX(created_at) FROM memory_observations").fetchone()[0]
|
|
1125
|
+
pending_sla = max(1, int(pending_sla_seconds or 3600))
|
|
1126
|
+
pending_older_than_sla = 0
|
|
1127
|
+
oldest_pending = None
|
|
1128
|
+
max_pending_age_seconds = 0.0
|
|
1047
1129
|
if tables["memory_observation_queue"]:
|
|
1048
1130
|
rows = conn.execute(
|
|
1049
1131
|
"SELECT status, COUNT(*) AS cnt FROM memory_observation_queue GROUP BY status"
|
|
1050
1132
|
).fetchall()
|
|
1051
1133
|
counts["queue"] = {row["status"]: int(row["cnt"]) for row in rows}
|
|
1134
|
+
stamp = float(now if now is not None else _core().now_epoch())
|
|
1135
|
+
stale_cutoff = stamp - pending_sla
|
|
1136
|
+
pending_older_than_sla = int(
|
|
1137
|
+
conn.execute(
|
|
1138
|
+
"""
|
|
1139
|
+
SELECT COUNT(*)
|
|
1140
|
+
FROM memory_observation_queue
|
|
1141
|
+
WHERE status IN ('pending', 'failed')
|
|
1142
|
+
AND created_at <= ?
|
|
1143
|
+
""",
|
|
1144
|
+
(stale_cutoff,),
|
|
1145
|
+
).fetchone()[0]
|
|
1146
|
+
)
|
|
1147
|
+
oldest = conn.execute(
|
|
1148
|
+
"""
|
|
1149
|
+
SELECT event_uid, status, created_at, updated_at, last_error
|
|
1150
|
+
FROM memory_observation_queue
|
|
1151
|
+
WHERE status IN ('pending', 'failed')
|
|
1152
|
+
ORDER BY created_at ASC, id ASC
|
|
1153
|
+
LIMIT 1
|
|
1154
|
+
"""
|
|
1155
|
+
).fetchone()
|
|
1156
|
+
if oldest:
|
|
1157
|
+
oldest_pending = dict(oldest)
|
|
1158
|
+
max_pending_age_seconds = max(0.0, stamp - float(oldest["created_at"] or stamp))
|
|
1052
1159
|
|
|
1053
1160
|
fts_enabled = _is_virtual_fts_table(conn, "memory_observations_fts")
|
|
1054
1161
|
fts_queryable = False
|
|
@@ -1061,12 +1168,33 @@ def memory_observation_health() -> dict:
|
|
|
1061
1168
|
|
|
1062
1169
|
missing_required = [name for name in ("memory_events", "memory_observations", "memory_observation_queue") if not tables[name]]
|
|
1063
1170
|
failed_queue = int(counts["queue"].get("failed", 0))
|
|
1171
|
+
warnings = []
|
|
1172
|
+
if pending_older_than_sla:
|
|
1173
|
+
warnings.append(
|
|
1174
|
+
{
|
|
1175
|
+
"code": "pending_sla_breached",
|
|
1176
|
+
"pending_older_than_sla": pending_older_than_sla,
|
|
1177
|
+
"pending_sla_seconds": pending_sla,
|
|
1178
|
+
"max_pending_age_seconds": max_pending_age_seconds,
|
|
1179
|
+
"oldest_pending": oldest_pending,
|
|
1180
|
+
}
|
|
1181
|
+
)
|
|
1182
|
+
if failed_queue:
|
|
1183
|
+
warnings.append({"code": "queue_failed", "failed": failed_queue})
|
|
1064
1184
|
return {
|
|
1065
|
-
"ok": not missing_required and failed_queue == 0,
|
|
1185
|
+
"ok": not missing_required and failed_queue == 0 and pending_older_than_sla == 0,
|
|
1066
1186
|
"tables": tables,
|
|
1067
1187
|
"missing_required": missing_required,
|
|
1068
1188
|
"counts": counts,
|
|
1069
1189
|
"latest": latest,
|
|
1190
|
+
"queue_sla": {
|
|
1191
|
+
"pending_sla_seconds": pending_sla,
|
|
1192
|
+
"pending_sla_ok": pending_older_than_sla == 0,
|
|
1193
|
+
"pending_older_than_sla": pending_older_than_sla,
|
|
1194
|
+
"oldest_pending": oldest_pending,
|
|
1195
|
+
"max_pending_age_seconds": max_pending_age_seconds,
|
|
1196
|
+
},
|
|
1197
|
+
"warnings": warnings,
|
|
1070
1198
|
"fts_enabled": fts_enabled,
|
|
1071
1199
|
"fts_degraded": tables["memory_observations_fts"] and not fts_enabled,
|
|
1072
1200
|
"fts_queryable": fts_queryable,
|