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.
Files changed (46) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +5 -1
  3. package/bin/windows-wsl-bridge.js +9 -0
  4. package/package.json +1 -1
  5. package/src/causal_graph.py +763 -0
  6. package/src/classifier_local.py +44 -0
  7. package/src/cognitive/_core.py +3 -0
  8. package/src/cognitive_control_observatory.py +2 -0
  9. package/src/db/__init__.py +8 -0
  10. package/src/db/_commitments.py +344 -0
  11. package/src/db/_entities.py +98 -11
  12. package/src/db/_memory_v2.py +130 -2
  13. package/src/db/_schema.py +565 -0
  14. package/src/desktop_bridge.py +1 -1
  15. package/src/doctor/providers/runtime.py +9 -3
  16. package/src/enforcement_engine.py +128 -2
  17. package/src/entity_live_profile.py +1073 -0
  18. package/src/failure_prevention.py +1052 -0
  19. package/src/hook_guardrails.py +104 -0
  20. package/src/knowledge_graph.py +46 -9
  21. package/src/local_context/api.py +54 -22
  22. package/src/local_context/usage_events.py +273 -8
  23. package/src/memory_executive.py +620 -0
  24. package/src/memory_utility.py +952 -0
  25. package/src/plugin_loader.py +9 -5
  26. package/src/plugins/entities.py +84 -7
  27. package/src/plugins/entity_live_profile.py +101 -0
  28. package/src/plugins/failure_prevention.py +162 -0
  29. package/src/plugins/memory_export.py +55 -18
  30. package/src/plugins/protocol.py +133 -0
  31. package/src/plugins/semantic_layers.py +138 -0
  32. package/src/pre_answer_router.py +622 -28
  33. package/src/pre_answer_runtime.py +463 -18
  34. package/src/r14_correction_learning.py +3 -3
  35. package/src/requirements.txt +5 -1
  36. package/src/runtime_versioning.py +11 -1
  37. package/src/saved_not_used_audit.py +44 -3
  38. package/src/scripts/nexo-followup-runner.py +194 -0
  39. package/src/semantic_layers.py +1153 -0
  40. package/src/semantic_reasoner.py +2 -2
  41. package/src/semantic_router.py +58 -11
  42. package/src/server.py +41 -3
  43. package/src/tools_sessions.py +88 -31
  44. package/src/tools_transcripts.py +38 -22
  45. package/src/user_state_model.py +971 -0
  46. package/tool-enforcement-map.json +230 -0
@@ -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,