nexo-brain 1.5.4 → 1.7.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 (180) hide show
  1. package/README.md +99 -77
  2. package/bin/nexo-brain.js +82 -3
  3. package/bin/postinstall.js +46 -0
  4. package/package.json +11 -2
  5. package/scripts/migrate-v1.5-to-v1.6.py +778 -0
  6. package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
  7. package/src/__pycache__/claim_graph.cpython-314.pyc +0 -0
  8. package/src/__pycache__/evolution_cycle.cpython-314.pyc +0 -0
  9. package/src/__pycache__/hnsw_index.cpython-314.pyc +0 -0
  10. package/src/__pycache__/kg_populate.cpython-314.pyc +0 -0
  11. package/src/__pycache__/knowledge_graph.cpython-314.pyc +0 -0
  12. package/src/__pycache__/maintenance.cpython-314.pyc +0 -0
  13. package/src/__pycache__/migrate_embeddings.cpython-314.pyc +0 -0
  14. package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
  15. package/src/__pycache__/server.cpython-314.pyc +0 -0
  16. package/src/__pycache__/storage_router.cpython-314.pyc +0 -0
  17. package/src/__pycache__/tools_coordination.cpython-314.pyc +0 -0
  18. package/src/__pycache__/tools_credentials.cpython-314.pyc +0 -0
  19. package/src/__pycache__/tools_learnings.cpython-314.pyc +0 -0
  20. package/src/__pycache__/tools_menu.cpython-314.pyc +0 -0
  21. package/src/__pycache__/tools_reminders.cpython-314.pyc +0 -0
  22. package/src/__pycache__/tools_reminders_crud.cpython-314.pyc +0 -0
  23. package/src/__pycache__/tools_sessions.cpython-314.pyc +0 -0
  24. package/src/__pycache__/tools_task_history.cpython-314.pyc +0 -0
  25. package/src/auto_close_sessions.py +4 -2
  26. package/src/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
  27. package/src/cognitive/__pycache__/_core.cpython-314.pyc +0 -0
  28. package/src/cognitive/__pycache__/_decay.cpython-314.pyc +0 -0
  29. package/src/cognitive/__pycache__/_ingest.cpython-314.pyc +0 -0
  30. package/src/cognitive/__pycache__/_memory.cpython-314.pyc +0 -0
  31. package/src/cognitive/__pycache__/_search.cpython-314.pyc +0 -0
  32. package/src/cognitive/__pycache__/_trust.cpython-314.pyc +0 -0
  33. package/src/cognitive/_core.py +1 -1
  34. package/src/cognitive/_memory.py +7 -3
  35. package/src/cognitive/_search.py +11 -10
  36. package/src/cognitive/_trust.py +2 -2
  37. package/src/dashboard/__pycache__/__init__.cpython-314.pyc +0 -0
  38. package/src/dashboard/__pycache__/app.cpython-314.pyc +0 -0
  39. package/src/dashboard/app.py +497 -5
  40. package/src/dashboard/static/favicon.svg +32 -0
  41. package/src/dashboard/static/nexo-logo.png +0 -0
  42. package/src/dashboard/static/nexo-logo.svg +40 -0
  43. package/src/dashboard/static/style.css +2458 -0
  44. package/src/dashboard/templates/adaptive.html +214 -65
  45. package/src/dashboard/templates/calendar.html +720 -0
  46. package/src/dashboard/templates/dashboard.html +621 -0
  47. package/src/dashboard/templates/graph.html +176 -95
  48. package/src/dashboard/templates/inbox.html +367 -0
  49. package/src/dashboard/templates/memory.html +185 -48
  50. package/src/dashboard/templates/operations.html +725 -0
  51. package/src/dashboard/templates/sessions.html +163 -41
  52. package/src/dashboard/templates/somatic.html +168 -48
  53. package/src/db/__init__.py +2 -1
  54. package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
  55. package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
  56. package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
  57. package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
  58. package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
  59. package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
  60. package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
  61. package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
  62. package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
  63. package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
  64. package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
  65. package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
  66. package/src/db/_core.py +2 -2
  67. package/src/db/_episodic.py +71 -1
  68. package/src/db/_fts.py +12 -12
  69. package/src/db/_reminders.py +53 -14
  70. package/src/db/_schema.py +10 -0
  71. package/src/hooks/__pycache__/auto_capture.cpython-314.pyc +0 -0
  72. package/src/hooks/capture-tool-logs.sh +50 -0
  73. package/src/hooks/daily-briefing-check.sh +33 -0
  74. package/src/hooks/inbox-hook.sh +74 -0
  75. package/src/hooks/session-start.sh +97 -5
  76. package/src/hooks/session-stop.sh +17 -31
  77. package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
  78. package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
  79. package/src/plugins/__pycache__/agents.cpython-314.pyc +0 -0
  80. package/src/plugins/__pycache__/artifact_registry.cpython-314.pyc +0 -0
  81. package/src/plugins/__pycache__/backup.cpython-314.pyc +0 -0
  82. package/src/plugins/__pycache__/cognitive_memory.cpython-314.pyc +0 -0
  83. package/src/plugins/__pycache__/core_rules.cpython-314.pyc +0 -0
  84. package/src/plugins/__pycache__/cortex.cpython-314.pyc +0 -0
  85. package/src/plugins/__pycache__/entities.cpython-314.pyc +0 -0
  86. package/src/plugins/__pycache__/episodic_memory.cpython-314.pyc +0 -0
  87. package/src/plugins/__pycache__/evolution.cpython-314.pyc +0 -0
  88. package/src/plugins/__pycache__/guard.cpython-314.pyc +0 -0
  89. package/src/plugins/__pycache__/knowledge_graph_tools.cpython-314.pyc +0 -0
  90. package/src/plugins/__pycache__/preferences.cpython-314.pyc +0 -0
  91. package/src/plugins/agents.py +8 -8
  92. package/src/plugins/backup.py +2 -2
  93. package/src/plugins/cognitive_memory.py +1 -1
  94. package/src/plugins/episodic_memory.py +73 -55
  95. package/src/plugins/evolution.py +2 -1
  96. package/src/plugins/guard.py +25 -6
  97. package/src/rules/__pycache__/__init__.cpython-314.pyc +0 -0
  98. package/src/rules/__pycache__/migrate.cpython-314.pyc +0 -0
  99. package/src/scripts/__pycache__/check-context.cpython-314.pyc +0 -0
  100. package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
  101. package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
  102. package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
  103. package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
  104. package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
  105. package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
  106. package/src/scripts/__pycache__/nexo-github-monitor.cpython-314.pyc +0 -0
  107. package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
  108. package/src/scripts/__pycache__/nexo-install.cpython-314.pyc +0 -0
  109. package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-314.pyc +0 -0
  110. package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
  111. package/src/scripts/__pycache__/nexo-migrate.cpython-314.pyc +0 -0
  112. package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
  113. package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
  114. package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
  115. package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
  116. package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
  117. package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
  118. package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
  119. package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
  120. package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
  121. package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
  122. package/src/scripts/check-context.py +4 -2
  123. package/src/scripts/deep-sleep/__pycache__/analyze_session.cpython-314.pyc +0 -0
  124. package/src/scripts/deep-sleep/__pycache__/apply_findings.cpython-314.pyc +0 -0
  125. package/src/scripts/deep-sleep/__pycache__/collect_transcripts.cpython-314.pyc +0 -0
  126. package/src/scripts/deep-sleep/analyze_session.py +3 -1
  127. package/src/scripts/deep-sleep/apply_findings.py +7 -4
  128. package/src/scripts/deep-sleep/collect_transcripts.py +3 -1
  129. package/src/scripts/deep-sleep/prompt.md +13 -13
  130. package/src/scripts/nexo-auto-update.py +0 -0
  131. package/src/scripts/nexo-backup.sh +33 -0
  132. package/src/scripts/nexo-catchup.py +4 -4
  133. package/src/scripts/nexo-cognitive-decay.py +8 -5
  134. package/src/scripts/nexo-daily-self-audit.py +19 -16
  135. package/src/scripts/nexo-deep-sleep.sh +4 -3
  136. package/src/scripts/nexo-evolution-run.py +12 -9
  137. package/src/scripts/nexo-followup-hygiene.py +7 -4
  138. package/src/scripts/nexo-github-monitor.py +252 -0
  139. package/src/scripts/nexo-immune.py +67 -29
  140. package/src/scripts/nexo-inbox-hook.sh +1 -1
  141. package/src/scripts/nexo-install.py +227 -0
  142. package/src/scripts/nexo-learning-housekeep.py +241 -0
  143. package/src/scripts/nexo-learning-validator.py +2 -0
  144. package/src/scripts/nexo-migrate.py +226 -0
  145. package/src/scripts/nexo-postmortem-consolidator.py +49 -46
  146. package/src/scripts/nexo-pre-commit.py +1 -1
  147. package/src/scripts/nexo-proactive-dashboard.py +23 -21
  148. package/src/scripts/nexo-reflection.py +0 -0
  149. package/src/scripts/nexo-runtime-preflight.py +1 -1
  150. package/src/scripts/nexo-send-email.py +1 -1
  151. package/src/scripts/nexo-send-reply.py +1 -1
  152. package/src/scripts/nexo-sleep.py +2 -0
  153. package/src/scripts/nexo-synthesis.py +3 -3
  154. package/src/scripts/nexo-watchdog.sh +275 -131
  155. package/src/server.py +34 -20
  156. package/src/tools_coordination.py +15 -15
  157. package/src/tools_credentials.py +13 -13
  158. package/src/tools_learnings.py +36 -27
  159. package/src/tools_menu.py +51 -50
  160. package/src/tools_reminders.py +12 -6
  161. package/src/tools_reminders_crud.py +19 -19
  162. package/src/tools_sessions.py +56 -186
  163. package/src/tools_task_history.py +10 -10
  164. package/templates/CLAUDE.md.template +92 -446
  165. package/templates/launchagents/README.md +133 -0
  166. package/templates/launchagents/com.nexo.auto-close-sessions.plist +35 -0
  167. package/templates/launchagents/com.nexo.catchup.plist +34 -0
  168. package/templates/launchagents/com.nexo.cognitive-decay.plist +33 -0
  169. package/templates/launchagents/com.nexo.dashboard.plist +39 -0
  170. package/templates/launchagents/com.nexo.deep-sleep.plist +39 -0
  171. package/templates/launchagents/com.nexo.evolution.plist +41 -0
  172. package/templates/launchagents/com.nexo.followup-hygiene.plist +41 -0
  173. package/templates/launchagents/com.nexo.github-monitor.plist +43 -0
  174. package/templates/launchagents/com.nexo.immune.plist +37 -0
  175. package/templates/launchagents/com.nexo.postmortem.plist +41 -0
  176. package/templates/launchagents/com.nexo.self-audit.plist +43 -0
  177. package/templates/launchagents/com.nexo.synthesis.plist +41 -0
  178. package/templates/launchagents/com.nexo.watchdog.plist +33 -0
  179. package/tests/test_migrations.py +3 -3
  180. package/src/dashboard/templates/overview.html +0 -75
@@ -21,8 +21,9 @@ from db import (
21
21
  SESSION_STALE_SECONDS,
22
22
  )
23
23
 
24
- LOG_DIR = os.path.expanduser("~/claude/operations/tool-logs")
25
- AUTO_CLOSE_LOG = os.path.expanduser("~/claude/coordination/auto-close.log")
24
+ NEXO_HOME = os.environ.get("NEXO_HOME", os.path.expanduser("~/.nexo"))
25
+ LOG_DIR = os.path.join(NEXO_HOME, "operations", "tool-logs")
26
+ AUTO_CLOSE_LOG = os.path.join(NEXO_HOME, "coordination", "auto-close.log")
26
27
 
27
28
 
28
29
  def get_tool_log_summary(sid: str) -> str:
@@ -120,6 +121,7 @@ def main():
120
121
 
121
122
  orphans = get_orphan_sessions(SESSION_STALE_SECONDS)
122
123
  if not orphans:
124
+ print(f"[{datetime.datetime.now().isoformat(timespec='seconds')}] No stale sessions")
123
125
  return
124
126
 
125
127
  for session in orphans:
@@ -11,7 +11,7 @@ from datetime import datetime, timedelta
11
11
  from pathlib import Path
12
12
  from typing import Optional
13
13
 
14
- COGNITIVE_DB = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cognitive.db")
14
+ COGNITIVE_DB = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "cognitive.db")
15
15
  EMBEDDING_DIM = 768
16
16
  LAMBDA_STM = 0.1 # half-life ~7 days
17
17
  LAMBDA_LTM = 0.012 # half-life ~60 days
@@ -398,18 +398,20 @@ def get_stats() -> dict:
398
398
  """Return statistics about the cognitive memory system."""
399
399
  db = _get_db()
400
400
 
401
- stm_active = db.execute("SELECT COUNT(*) FROM stm_memories WHERE promoted_to_ltm = 0").fetchone()[0]
401
+ stm_active = db.execute("SELECT COUNT(*) FROM stm_memories WHERE lifecycle_state = 'active' AND promoted_to_ltm = 0").fetchone()[0]
402
+ stm_promoted = db.execute("SELECT COUNT(*) FROM stm_memories WHERE promoted_to_ltm = 1").fetchone()[0]
403
+ stm_total = db.execute("SELECT COUNT(*) FROM stm_memories WHERE lifecycle_state = 'active'").fetchone()[0]
402
404
  ltm_active = db.execute("SELECT COUNT(*) FROM ltm_memories WHERE is_dormant = 0").fetchone()[0]
403
405
  ltm_dormant = db.execute("SELECT COUNT(*) FROM ltm_memories WHERE is_dormant = 1").fetchone()[0]
404
406
 
405
- avg_stm = db.execute("SELECT AVG(strength) FROM stm_memories WHERE promoted_to_ltm = 0").fetchone()[0] or 0.0
407
+ avg_stm = db.execute("SELECT AVG(strength) FROM stm_memories WHERE lifecycle_state = 'active' AND promoted_to_ltm = 0").fetchone()[0] or 0.0
406
408
  avg_ltm = db.execute("SELECT AVG(strength) FROM ltm_memories WHERE is_dormant = 0").fetchone()[0] or 0.0
407
409
 
408
410
  total_retrievals = db.execute("SELECT COUNT(*) FROM retrieval_log").fetchone()[0]
409
411
  avg_retrieval_score = db.execute("SELECT AVG(top_score) FROM retrieval_log").fetchone()[0] or 0.0
410
412
 
411
413
  top_domains_stm = db.execute(
412
- "SELECT domain, COUNT(*) as cnt FROM stm_memories WHERE promoted_to_ltm = 0 AND domain != '' GROUP BY domain ORDER BY cnt DESC LIMIT 5"
414
+ "SELECT domain, COUNT(*) as cnt FROM stm_memories WHERE lifecycle_state = 'active' AND promoted_to_ltm = 0 AND domain != '' GROUP BY domain ORDER BY cnt DESC LIMIT 5"
413
415
  ).fetchall()
414
416
  top_domains_ltm = db.execute(
415
417
  "SELECT domain, COUNT(*) as cnt FROM ltm_memories WHERE is_dormant = 0 AND domain != '' GROUP BY domain ORDER BY cnt DESC LIMIT 5"
@@ -420,6 +422,8 @@ def get_stats() -> dict:
420
422
 
421
423
  return {
422
424
  "stm_active": stm_active,
425
+ "stm_promoted": stm_promoted,
426
+ "stm_total": stm_total,
423
427
  "ltm_active": ltm_active,
424
428
  "ltm_dormant": ltm_dormant,
425
429
  "avg_stm_strength": round(avg_stm, 3),
@@ -112,11 +112,12 @@ def _rrf_fuse(vector_results: list[dict], bm25_results: list[dict],
112
112
 
113
113
  # If we have the original cosine score, blend it in to preserve semantic confidence
114
114
  if vec_result and "score" in vec_result:
115
- # Weighted blend: RRF for ranking + cosine for confidence
116
- result["score"] = 0.6 * vec_result["score"] + 0.4 * (rrf_score * k * 3)
115
+ # Weighted blend: cosine for confidence + RRF for ranking boost
116
+ rrf_normalized = min(1.0, rrf_score * k) # normalize to 0-1 range
117
+ result["score"] = 0.7 * vec_result["score"] + 0.3 * rrf_normalized
117
118
  else:
118
- # BM25-only result: use RRF score scaled to ~0.5-0.7 range
119
- result["score"] = min(0.85, rrf_score * k * 3)
119
+ # BM25-only result: use RRF score scaled to ~0.3-0.7 range
120
+ result["score"] = min(0.75, rrf_score * k)
120
121
 
121
122
  result["bm25_boosted"] = key in bm25_lookup
122
123
  result["bm25_only"] = key not in vec_lookup
@@ -199,8 +200,8 @@ def _apply_temporal_boost(results: list[dict], query_text: str) -> list[dict]:
199
200
  # Bounded exponential decay boost
200
201
  boost = alpha * math.exp(-ln2 * age_days / half_life_days)
201
202
 
202
- # Apply boost (capped at 1.0)
203
- r["score"] = min(1.0, r["score"] + boost)
203
+ # Apply boost (capped at 0.95 — reserve 1.0 for exact matches only)
204
+ r["score"] = min(0.95, r["score"] + boost)
204
205
  if boost > 0.001:
205
206
  r["temporal_boost"] = round(boost, 4)
206
207
 
@@ -282,7 +283,7 @@ def _kg_boost_results(results: list[dict], max_boost: float = 0.08) -> list[dict
282
283
  for idx in ref_map.get(node_ref, []):
283
284
  r = results[idx]
284
285
  if r.get("score", 0) >= 0.45: # Same relevance gate as temporal
285
- r["score"] = min(1.0, r["score"] + boost)
286
+ r["score"] = min(0.95, r["score"] + boost)
286
287
  r["kg_boost"] = round(boost, 4)
287
288
  r["kg_connections"] = connections
288
289
 
@@ -613,7 +614,7 @@ def _rehearse_results(results: list[dict], skip_ids: set = None):
613
614
  continue
614
615
  table = "stm_memories" if r["store"] == "stm" else "ltm_memories"
615
616
  db.execute(
616
- f"UPDATE {table} SET strength = 1.0, access_count = access_count + 1, last_accessed = ? WHERE id = ?",
617
+ f"UPDATE {table} SET strength = MIN(1.0, strength + 0.08), access_count = access_count + 1, last_accessed = ? WHERE id = ?",
617
618
  (now, r["id"])
618
619
  )
619
620
  db.commit()
@@ -834,7 +835,7 @@ def search(
834
835
  if is_temporal_query:
835
836
  for r in results:
836
837
  if r.get("temporal_date"):
837
- r["score"] = min(1.0, r["score"] + 0.05)
838
+ r["score"] = min(0.95, r["score"] + 0.05)
838
839
 
839
840
  # Recency temporal boost: recent memories get additive bonus (query-adaptive)
840
841
  results = _apply_temporal_boost(results, query_text)
@@ -866,7 +867,7 @@ def search(
866
867
  existing_hashes.add(co_hash)
867
868
  if co_hash in neighbor_boosts:
868
869
  boost = neighbor_boosts[co_hash]
869
- r["score"] = min(1.0, r["score"] + boost)
870
+ r["score"] = min(0.95, r["score"] + boost)
870
871
  r["co_activation_boost"] = boost
871
872
 
872
873
  # Add neighbor memories not already in results
@@ -316,7 +316,7 @@ def detect_sentiment(text: str) -> dict:
316
316
  if intensity > 0.7:
317
317
  guidance = "MODE: Ultra-conciso. Cero explicaciones. Resolver y mostrar resultado."
318
318
  else:
319
- guidance = "MODE: Conciso. Menos contexto, más acción directa."
319
+ guidance = "MODE: Concise. Less context, more direct action."
320
320
  elif pos_score > neg_score and pos_score >= 1:
321
321
  sentiment = "positive"
322
322
  intensity = min(1.0, 0.3 + pos_score * 0.15)
@@ -324,7 +324,7 @@ def detect_sentiment(text: str) -> dict:
324
324
  elif urgency_hits:
325
325
  sentiment = "urgent"
326
326
  intensity = 0.8
327
- guidance = "MODE: Acción inmediata. Sin preámbulos."
327
+ guidance = "MODE: Immediate action. No preambles."
328
328
  else:
329
329
  sentiment = "neutral"
330
330
  intensity = 0.5