nexo-brain 1.7.0 → 2.1.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/README.md +160 -60
- package/bin/nexo-brain.js +680 -381
- package/package.json +18 -3
- package/scripts/migrate-to-unified.sh +813 -0
- package/scripts/migrate-v1.7-to-v1.8.py +214 -0
- package/scripts/pre-commit-check.sh +1 -1
- package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
- package/src/__pycache__/hnsw_index.cpython-310.pyc +0 -0
- package/src/__pycache__/hnsw_index.cpython-314.pyc +0 -0
- package/src/__pycache__/kg_populate.cpython-310.pyc +0 -0
- package/src/__pycache__/knowledge_graph.cpython-310.pyc +0 -0
- package/src/__pycache__/plugin_loader.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_coordination.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
- package/src/auto_close_sessions.py +1 -1
- package/src/auto_update.py +634 -0
- package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-314.pyc +0 -0
- package/src/cognitive/_core.py +7 -3
- package/src/cognitive/_decay.py +1 -1
- package/src/cognitive/_search.py +1 -0
- package/src/cognitive/_trust.py +3 -3
- package/src/crons/manifest.json +106 -0
- package/src/crons/sync.py +217 -0
- package/src/dashboard/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/dashboard/__pycache__/app.cpython-310.pyc +0 -0
- package/src/dashboard/app.py +24 -4
- package/src/dashboard/templates/dashboard.html +3 -2
- package/src/db/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/db/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
- package/src/db/_core.py +5 -1
- package/src/db/_episodic.py +2 -4
- package/src/db/_reminders.py +45 -6
- package/src/db/_schema.py +31 -0
- package/src/evolution_cycle.py +33 -11
- package/src/hooks/auto_capture.py +1 -1
- package/src/hooks/capture-tool-logs.sh +76 -0
- package/src/hooks/inbox-hook.sh +2 -1
- package/src/hooks/post-compact.sh +2 -1
- package/src/hooks/pre-compact.sh +104 -2
- package/src/hooks/session-start.sh +6 -2
- package/src/hooks/session-stop.sh +4 -2
- package/src/kg_populate.py +4 -1
- package/src/migrate_embeddings.py +4 -1
- package/src/plugin_loader.py +100 -34
- package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
- package/src/plugins/agents.py +2 -2
- package/src/plugins/backup.py +5 -4
- package/src/plugins/core_rules.py +37 -16
- package/src/plugins/episodic_memory.py +14 -5
- package/src/plugins/evolution.py +6 -2
- package/src/plugins/guard.py +20 -11
- package/src/plugins/update.py +256 -0
- package/src/requirements.txt +12 -0
- package/src/scripts/check-context.py +8 -3
- package/src/scripts/deep-sleep/__pycache__/extract.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/apply_findings.py +514 -169
- package/src/scripts/deep-sleep/collect.py +480 -0
- package/src/scripts/deep-sleep/extract-prompt.md +233 -0
- package/src/scripts/deep-sleep/extract.py +249 -0
- package/src/scripts/deep-sleep/synthesize-prompt.md +168 -0
- package/src/scripts/deep-sleep/synthesize.py +191 -0
- package/src/scripts/nexo-auto-update.py +4 -211
- package/src/scripts/nexo-backup.sh +5 -13
- package/src/scripts/nexo-brain-activation.sh +26 -26
- package/src/scripts/nexo-catchup.py +36 -25
- package/src/scripts/nexo-cognitive-decay.py +7 -3
- package/src/scripts/nexo-daily-self-audit.py +44 -15
- package/src/scripts/nexo-deep-sleep.sh +31 -16
- package/src/scripts/nexo-evolution-run.py +21 -11
- package/src/scripts/nexo-followup-hygiene.py +6 -4
- package/src/scripts/nexo-github-monitor.py +12 -8
- package/src/scripts/nexo-immune.py +6 -4
- package/src/scripts/nexo-inbox-hook.sh +2 -1
- package/src/scripts/nexo-install.py +4 -225
- package/src/scripts/nexo-learning-housekeep.py +7 -3
- package/src/scripts/nexo-learning-validator.py +1 -22
- package/src/scripts/nexo-migrate.py +9 -3
- package/src/scripts/nexo-postmortem-consolidator.py +17 -10
- package/src/scripts/nexo-pre-commit.py +3 -1
- package/src/scripts/nexo-prevent-sleep.sh +29 -0
- package/src/scripts/nexo-proactive-dashboard.py +5 -4
- package/src/scripts/nexo-runtime-preflight.py +59 -55
- package/src/scripts/nexo-send-email.py +1 -1
- package/src/scripts/nexo-send-reply.py +3 -1
- package/src/scripts/nexo-sleep.py +13 -9
- package/src/scripts/nexo-snapshot-restore.sh +2 -1
- package/src/scripts/nexo-synthesis.py +11 -7
- package/src/scripts/nexo-tcc-approve.sh +79 -0
- package/src/scripts/nexo-update.sh +161 -0
- package/src/scripts/nexo-watchdog-smoke.py +18 -13
- package/src/scripts/nexo-watchdog.sh +22 -13
- package/src/server.py +77 -28
- package/src/storage_router.py +6 -2
- package/src/tools_learnings.py +6 -6
- package/src/tools_menu.py +1 -1
- package/src/tools_reminders_crud.py +10 -8
- package/src/tools_sessions.py +76 -4
- package/templates/CLAUDE.md.template +14 -80
- package/templates/launchagents/README.md +7 -7
- package/templates/launchagents/com.nexo.auto-close-sessions.plist +5 -1
- package/templates/launchagents/com.nexo.catchup.plist +4 -0
- package/templates/launchagents/com.nexo.cognitive-decay.plist +7 -0
- package/templates/launchagents/com.nexo.dashboard.plist +5 -1
- package/templates/launchagents/com.nexo.deep-sleep.plist +4 -0
- package/templates/launchagents/com.nexo.evolution.plist +4 -0
- package/templates/launchagents/com.nexo.followup-hygiene.plist +4 -0
- package/templates/launchagents/com.nexo.github-monitor.plist +3 -1
- package/templates/launchagents/com.nexo.immune.plist +4 -0
- package/templates/launchagents/com.nexo.postmortem.plist +4 -0
- package/templates/launchagents/com.nexo.self-audit.plist +4 -0
- package/templates/launchagents/com.nexo.synthesis.plist +4 -0
- package/templates/launchagents/com.nexo.watchdog.plist +4 -0
- package/templates/openclaw.json +1 -1
- package/tests/conftest.py +2 -2
- package/tests/test_cognitive.py +7 -6
- package/tests/test_migrations.py +26 -0
- package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/claim_graph.cpython-314.pyc +0 -0
- package/src/__pycache__/evolution_cycle.cpython-314.pyc +0 -0
- package/src/__pycache__/kg_populate.cpython-314.pyc +0 -0
- package/src/__pycache__/knowledge_graph.cpython-314.pyc +0 -0
- package/src/__pycache__/maintenance.cpython-314.pyc +0 -0
- package/src/__pycache__/migrate_embeddings.cpython-314.pyc +0 -0
- package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
- package/src/__pycache__/server.cpython-314.pyc +0 -0
- package/src/__pycache__/storage_router.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_coordination.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_credentials.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_learnings.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_menu.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_reminders.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_reminders_crud.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_task_history.cpython-314.pyc +0 -0
- package/src/dashboard/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/dashboard/__pycache__/app.cpython-314.pyc +0 -0
- package/src/hooks/__pycache__/auto_capture.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/agents.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/backup.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/core_rules.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/cortex.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/entities.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/evolution.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/guard.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/preferences.cpython-314.pyc +0 -0
- package/src/rules/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/rules/__pycache__/migrate.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/check-context.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-github-monitor.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-install.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-migrate.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/analyze_session.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/apply_findings.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/collect_transcripts.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/analyze_session.py +0 -217
- package/src/scripts/deep-sleep/collect_transcripts.py +0 -145
- package/src/scripts/deep-sleep/prompt.md +0 -109
- package/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_cognitive.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_knowledge_graph.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_migrations.cpython-314-pytest-9.0.2.pyc +0 -0
package/src/server.py
CHANGED
|
@@ -38,29 +38,76 @@ def _shutdown_handler(signum, frame):
|
|
|
38
38
|
close_db()
|
|
39
39
|
sys.exit(0)
|
|
40
40
|
|
|
41
|
-
signal.signal(signal.SIGTERM, _shutdown_handler)
|
|
42
|
-
signal.signal(signal.SIGINT, _shutdown_handler)
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
def _server_init():
|
|
43
|
+
"""Run all side effects: signals, PID, DB, auto-update, plugins.
|
|
44
|
+
|
|
45
|
+
Called only when the server is actually started (not on import).
|
|
46
|
+
"""
|
|
47
|
+
signal.signal(signal.SIGTERM, _shutdown_handler)
|
|
48
|
+
signal.signal(signal.SIGINT, _shutdown_handler)
|
|
49
|
+
|
|
50
|
+
# ── Write PID file for stale process detection ─────────────────
|
|
51
|
+
_data_dir = os.path.join(os.environ.get("NEXO_HOME", os.path.join(os.path.expanduser("~"), ".nexo")), "data")
|
|
52
|
+
os.makedirs(_data_dir, exist_ok=True)
|
|
53
|
+
_pid_file = os.path.join(_data_dir, "nexo.pid")
|
|
54
|
+
with open(_pid_file, "w") as f:
|
|
55
|
+
f.write(str(os.getpid()))
|
|
56
|
+
|
|
57
|
+
init_db()
|
|
58
|
+
|
|
59
|
+
# ── Auto-update check (non-blocking, max 5s) ──────────────────
|
|
60
|
+
try:
|
|
61
|
+
from auto_update import auto_update_check
|
|
62
|
+
import threading
|
|
63
|
+
|
|
64
|
+
def _bg_update():
|
|
65
|
+
try:
|
|
66
|
+
result = auto_update_check()
|
|
67
|
+
if result.get("git_update"):
|
|
68
|
+
print(f"[NEXO] {result['git_update']}", file=sys.stderr)
|
|
69
|
+
if result.get("npm_notice"):
|
|
70
|
+
print(f"[NEXO] {result['npm_notice']}", file=sys.stderr)
|
|
71
|
+
if result.get("claude_md_update"):
|
|
72
|
+
print(f"[NEXO] {result['claude_md_update']}", file=sys.stderr)
|
|
73
|
+
for m in result.get("migrations", []):
|
|
74
|
+
if m["status"] == "failed":
|
|
75
|
+
print(f"[NEXO] Migration {m['file']} FAILED: {m['message']}", file=sys.stderr)
|
|
76
|
+
except Exception as e:
|
|
77
|
+
print(f"[NEXO auto-update] error: {e}", file=sys.stderr)
|
|
78
|
+
|
|
79
|
+
_update_thread = threading.Thread(target=_bg_update, daemon=True)
|
|
80
|
+
_update_thread.start()
|
|
81
|
+
_update_thread.join(timeout=5) # Wait at most 5 seconds
|
|
82
|
+
except Exception:
|
|
83
|
+
pass # Never break startup
|
|
84
|
+
|
|
85
|
+
# ── Load plugins ───────────────────────────────────────────────
|
|
86
|
+
load_all_plugins(mcp)
|
|
48
87
|
|
|
49
|
-
init_db()
|
|
50
88
|
|
|
51
89
|
mcp = FastMCP(
|
|
52
90
|
name="nexo",
|
|
53
91
|
instructions=(
|
|
54
|
-
"NEXO
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
92
|
+
"NEXO — cognitive co-operator. Save important info from tool results before they clear.\n\n"
|
|
93
|
+
"## Rules\n"
|
|
94
|
+
"- **Heartbeat:** `nexo_heartbeat(sid=SID, task='...', context_hint='...')` every user msg. "
|
|
95
|
+
"React: DIARY REMINDER→write diary, VIBE:NEGATIVE→ultra-concise, AUTO-PRIME→read learnings\n"
|
|
96
|
+
"- **Guard:** `nexo_guard_check(files='...', area='...')` BEFORE editing code. "
|
|
97
|
+
"Blocking rules→resolve first. `nexo_track(sid=SID, paths=[...])` before shared files\n"
|
|
98
|
+
"- **Followups:** NEXO tasks, execute silently. 'done'/'all set'→`nexo_followup_complete` NOW. "
|
|
99
|
+
"Reminders=user's, alert when due\n"
|
|
100
|
+
"- **Observe:** correction→learning+trust. 'tomorrow'→followup. person→entity. open topic→followup 3d\n"
|
|
101
|
+
"- **Delegate:** prefer direct. If needed: `nexo_context_packet(area)` + guard + 'if unsure STOP'\n"
|
|
102
|
+
"- **Memory:** `nexo_recall` searches all. Capture: errors→`nexo_learning_add`, prefs, entities, decisions\n"
|
|
103
|
+
"- **Change log:** `nexo_change_log(...)` after production edits. NOT for config dir\n"
|
|
104
|
+
"- **Diary:** `nexo_session_diary_write(...)` mandatory on close. Include self_critique\n"
|
|
105
|
+
"- **Cortex:** `nexo_cortex_check` before budget/campaign/architecture changes\n"
|
|
106
|
+
"- **Dissonance:** user contradicts memory→`nexo_cognitive_dissonance`. Frustrated→force=True\n"
|
|
107
|
+
"- **Trust:** <40=paranoid verify twice, >80=fluid. Check: `nexo_cognitive_trust`"
|
|
59
108
|
),
|
|
60
109
|
)
|
|
61
110
|
|
|
62
|
-
_plugins_loaded = load_all_plugins(mcp)
|
|
63
|
-
|
|
64
111
|
|
|
65
112
|
# ── Session management (3 tools) ──────────────────────────────────
|
|
66
113
|
|
|
@@ -529,8 +576,8 @@ def nexo_index_dirs() -> str:
|
|
|
529
576
|
"""List all directories being indexed by FTS5 (builtin + dynamic)."""
|
|
530
577
|
dirs = fts_list_dirs()
|
|
531
578
|
if not dirs:
|
|
532
|
-
return "
|
|
533
|
-
lines = ["
|
|
579
|
+
return "No directories configured."
|
|
580
|
+
lines = ["INDEXED DIRECTORIES:"]
|
|
534
581
|
for d in dirs:
|
|
535
582
|
source_tag = "⚙️" if d["source"] == "builtin" else "➕"
|
|
536
583
|
notes = f" — {d['notes']}" if d.get("notes") else ""
|
|
@@ -639,34 +686,35 @@ def nexo_task_frequency() -> str:
|
|
|
639
686
|
|
|
640
687
|
@mcp.tool
|
|
641
688
|
def nexo_plugin_load(filename: str) -> str:
|
|
642
|
-
"""Load or reload a plugin
|
|
689
|
+
"""Load or reload a plugin. Searches repo plugins/ first, then NEXO_HOME/plugins/.
|
|
643
690
|
|
|
644
691
|
Args:
|
|
645
692
|
filename: Plugin filename (e.g., 'entities.py').
|
|
646
693
|
"""
|
|
647
694
|
try:
|
|
648
695
|
n = load_plugin(mcp, filename)
|
|
649
|
-
return f"Plugin {filename}: {n} tools
|
|
696
|
+
return f"Plugin {filename}: {n} tools registered."
|
|
650
697
|
except Exception as e:
|
|
651
|
-
return f"Error
|
|
698
|
+
return f"Error loading plugin {filename}: {e}"
|
|
652
699
|
|
|
653
700
|
|
|
654
701
|
@mcp.tool
|
|
655
702
|
def nexo_plugin_list() -> str:
|
|
656
|
-
"""List all loaded plugins and their tools."""
|
|
703
|
+
"""List all loaded plugins and their tools, showing source (repo/personal)."""
|
|
657
704
|
plugins = list_plugins()
|
|
658
705
|
if not plugins:
|
|
659
|
-
return "
|
|
660
|
-
lines = ["PLUGINS
|
|
706
|
+
return "No plugins loaded."
|
|
707
|
+
lines = ["LOADED PLUGINS:"]
|
|
661
708
|
for p in plugins:
|
|
662
|
-
names = p["tool_names"] or "(
|
|
663
|
-
|
|
709
|
+
names = p["tool_names"] or "(no tools)"
|
|
710
|
+
source = p.get("source", "repo")
|
|
711
|
+
lines.append(f" [{source}] {p['filename']} — {p['tools_count']} tools: {names}")
|
|
664
712
|
return "\n".join(lines)
|
|
665
713
|
|
|
666
714
|
|
|
667
715
|
@mcp.tool
|
|
668
716
|
def nexo_plugin_remove(filename: str) -> str:
|
|
669
|
-
"""
|
|
717
|
+
"""Unregister a plugin's tools from MCP (does not delete files).
|
|
670
718
|
|
|
671
719
|
Args:
|
|
672
720
|
filename: Plugin filename (e.g., 'entities.py').
|
|
@@ -674,11 +722,12 @@ def nexo_plugin_remove(filename: str) -> str:
|
|
|
674
722
|
try:
|
|
675
723
|
removed = remove_plugin(mcp, filename)
|
|
676
724
|
if removed:
|
|
677
|
-
return f"Plugin {filename}
|
|
678
|
-
return f"Plugin {filename}
|
|
725
|
+
return f"Plugin {filename} unregistered. Tools removed: {', '.join(removed)}"
|
|
726
|
+
return f"Plugin {filename} unregistered (had no registered tools)."
|
|
679
727
|
except Exception as e:
|
|
680
|
-
return f"Error
|
|
728
|
+
return f"Error removing plugin {filename}: {e}"
|
|
681
729
|
|
|
682
730
|
|
|
683
731
|
if __name__ == "__main__":
|
|
732
|
+
_server_init()
|
|
684
733
|
mcp.run(transport="stdio")
|
package/src/storage_router.py
CHANGED
|
@@ -11,12 +11,16 @@ class StorageRouter:
|
|
|
11
11
|
|
|
12
12
|
def nexo_db_path(self) -> str:
|
|
13
13
|
if self.tenant_id == "default":
|
|
14
|
-
|
|
14
|
+
data_dir = os.path.join(NEXO_HOME, "data")
|
|
15
|
+
os.makedirs(data_dir, exist_ok=True)
|
|
16
|
+
return os.path.join(data_dir, "nexo.db")
|
|
15
17
|
return os.path.join(NEXO_HOME, "tenants", self.tenant_id, "nexo.db")
|
|
16
18
|
|
|
17
19
|
def cognitive_db_path(self) -> str:
|
|
18
20
|
if self.tenant_id == "default":
|
|
19
|
-
|
|
21
|
+
data_dir = os.path.join(NEXO_HOME, "data")
|
|
22
|
+
os.makedirs(data_dir, exist_ok=True)
|
|
23
|
+
return os.path.join(data_dir, "cognitive.db")
|
|
20
24
|
return os.path.join(NEXO_HOME, "tenants", self.tenant_id, "cognitive.db")
|
|
21
25
|
|
|
22
26
|
|
package/src/tools_learnings.py
CHANGED
|
@@ -107,8 +107,8 @@ def handle_learning_search(query: str, category: str = '') -> str:
|
|
|
107
107
|
"""Search learnings by query string, optionally filtered by category."""
|
|
108
108
|
results = search_learnings(query, category if category else None)
|
|
109
109
|
if not results:
|
|
110
|
-
return f"
|
|
111
|
-
lines = [f"
|
|
110
|
+
return f"No results for '{query}'."
|
|
111
|
+
lines = [f"RESULTS ({len(results)}):"]
|
|
112
112
|
for r in results:
|
|
113
113
|
snippet = r["content"][:100] + "..." if len(r["content"]) > 100 else r["content"]
|
|
114
114
|
status = r.get("status", "active")
|
|
@@ -155,7 +155,7 @@ def handle_learning_update(id: int, title: str = '', content: str = '', category
|
|
|
155
155
|
if review_days > 0:
|
|
156
156
|
kwargs["review_days"] = review_days
|
|
157
157
|
if not kwargs:
|
|
158
|
-
return "ERROR:
|
|
158
|
+
return "ERROR: Nothing to update. Provide new fields."
|
|
159
159
|
basic_kwargs = {k: v for k, v in kwargs.items() if k in {"title", "content", "category", "reasoning"}}
|
|
160
160
|
result = update_learning(id, **basic_kwargs)
|
|
161
161
|
if "error" in result:
|
|
@@ -179,7 +179,7 @@ def handle_learning_update(id: int, title: str = '', content: str = '', category
|
|
|
179
179
|
conn = get_db()
|
|
180
180
|
conn.execute(f"UPDATE learnings SET {set_clause} WHERE id = ?", values)
|
|
181
181
|
conn.commit()
|
|
182
|
-
return f"Learning #{id}
|
|
182
|
+
return f"Learning #{id} updated."
|
|
183
183
|
|
|
184
184
|
|
|
185
185
|
def handle_learning_delete(id: int) -> str:
|
|
@@ -194,7 +194,7 @@ def handle_learning_list(category: str = '') -> str:
|
|
|
194
194
|
"""List all learnings, grouped by category if no filter given."""
|
|
195
195
|
results = list_learnings(category if category else None)
|
|
196
196
|
if not results:
|
|
197
|
-
label = category if category else "
|
|
197
|
+
label = category if category else "ALL"
|
|
198
198
|
return f"LEARNINGS {label} (0): No entries."
|
|
199
199
|
|
|
200
200
|
if category:
|
|
@@ -206,7 +206,7 @@ def handle_learning_list(category: str = '') -> str:
|
|
|
206
206
|
pri_icon = {"critical": "🔴", "high": "🟠", "medium": "🟡", "low": "⚪"}.get(pri, "🟡")
|
|
207
207
|
lines.append(f" #{r['id']} [{r.get('status','active')}] {pri_icon}{pri} w={w:.2f} {r['title']}")
|
|
208
208
|
else:
|
|
209
|
-
lines = [f"LEARNINGS
|
|
209
|
+
lines = [f"LEARNINGS ALL ({len(results)}):"]
|
|
210
210
|
current_cat = None
|
|
211
211
|
for r in results:
|
|
212
212
|
if r["category"] != current_cat:
|
package/src/tools_menu.py
CHANGED
|
@@ -172,7 +172,7 @@ def handle_menu() -> str:
|
|
|
172
172
|
).fetchall()
|
|
173
173
|
# Followups without date
|
|
174
174
|
nf_no_date = conn.execute(
|
|
175
|
-
"SELECT id, description FROM followups WHERE status NOT LIKE 'COMPLETED%' AND (date IS NULL OR date='') ORDER BY id"
|
|
175
|
+
"SELECT id, description FROM followups WHERE status NOT LIKE 'COMPLETED%' AND status NOT IN ('DELETED','archived','blocked','waiting') AND (date IS NULL OR date='') ORDER BY id"
|
|
176
176
|
).fetchall()
|
|
177
177
|
|
|
178
178
|
if no_date or future or nf_no_date:
|
|
@@ -18,11 +18,11 @@ def handle_reminder_create(id: str, description: str, date: str = '', category:
|
|
|
18
18
|
|
|
19
19
|
result = create_reminder(id=id, description=description, date=date or None, category=category)
|
|
20
20
|
if not result or "error" in result:
|
|
21
|
-
error_msg = result.get("error", "
|
|
21
|
+
error_msg = result.get("error", "unknown") if isinstance(result, dict) else "unknown"
|
|
22
22
|
return f"ERROR: {error_msg}"
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
return f"Reminder created. Date: {
|
|
24
|
+
date_str = date if date else 'no date'
|
|
25
|
+
return f"Reminder created. Date: {date_str}. Category: {category}."
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def handle_reminder_update(id: str, description: str = '', date: str = '', status: str = '', category: str = '') -> str:
|
|
@@ -81,16 +81,18 @@ def handle_followup_create(id: str, description: str, date: str = '', verificati
|
|
|
81
81
|
When completed, auto-creates the next occurrence.
|
|
82
82
|
"""
|
|
83
83
|
if not id.startswith('NF'):
|
|
84
|
-
return f"ERROR:
|
|
84
|
+
return f"ERROR: Followup ID must start with 'NF' (received: '{id}')."
|
|
85
85
|
|
|
86
86
|
result = create_followup(id=id, description=description, date=date or None, verification=verification, reasoning=reasoning, recurrence=recurrence or None)
|
|
87
87
|
if not result or "error" in result:
|
|
88
|
-
error_msg = result.get("error", "
|
|
88
|
+
error_msg = result.get("error", "unknown") if isinstance(result, dict) else "unknown"
|
|
89
89
|
return f"ERROR: {error_msg}"
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
rec_str = f"
|
|
93
|
-
|
|
91
|
+
date_str = date if date else 'no date'
|
|
92
|
+
rec_str = f" Recurrence: {recurrence}." if recurrence else ""
|
|
93
|
+
warning = result.get("warning", "")
|
|
94
|
+
warn_str = f"\n{warning}" if warning else ""
|
|
95
|
+
return f"Followup created. Date: {date_str}.{rec_str}{warn_str}"
|
|
94
96
|
|
|
95
97
|
|
|
96
98
|
def handle_followup_update(id: str, description: str = '', date: str = '', verification: str = '', status: str = '') -> str:
|
package/src/tools_sessions.py
CHANGED
|
@@ -138,12 +138,14 @@ def handle_heartbeat(sid: str, task: str, context_hint: str = '') -> str:
|
|
|
138
138
|
parts.append(f" {q['qid']} de {q['from_sid']} ({age}): {q['question']}")
|
|
139
139
|
|
|
140
140
|
# Incremental diary draft — accumulate every heartbeat, full UPSERT every 5
|
|
141
|
+
_hb_count = 0 # Hoisted for Layer 3 DIARY_OVERDUE signal
|
|
141
142
|
try:
|
|
142
143
|
import json as _json
|
|
143
144
|
from db import get_diary_draft, upsert_diary_draft
|
|
144
145
|
|
|
145
146
|
draft = get_diary_draft(sid)
|
|
146
147
|
hb_count = (draft["heartbeat_count"] + 1) if draft else 1
|
|
148
|
+
_hb_count = hb_count # Copy to outer scope for Layer 3
|
|
147
149
|
|
|
148
150
|
existing_tasks = _json.loads(draft["tasks_seen"]) if draft else []
|
|
149
151
|
if task and task not in existing_tasks:
|
|
@@ -194,14 +196,17 @@ def handle_heartbeat(sid: str, task: str, context_hint: str = '') -> str:
|
|
|
194
196
|
except Exception:
|
|
195
197
|
pass # Checkpoint update is best-effort
|
|
196
198
|
|
|
197
|
-
#
|
|
199
|
+
# ── Layer 3: DIARY_OVERDUE signal based on heartbeat count + time ──
|
|
198
200
|
conn = get_db()
|
|
199
201
|
row = conn.execute("SELECT started_epoch FROM sessions WHERE sid = ?", (sid,)).fetchone()
|
|
200
202
|
if row:
|
|
201
203
|
age_seconds = now_epoch() - row["started_epoch"]
|
|
202
|
-
|
|
204
|
+
has_diary = check_session_has_diary(sid)
|
|
205
|
+
|
|
206
|
+
# DIARY_OVERDUE: >10 heartbeats OR >30 minutes, without a diary
|
|
207
|
+
if not has_diary and (_hb_count > 10 or age_seconds >= 1800):
|
|
203
208
|
parts.append("")
|
|
204
|
-
parts.append("⚠
|
|
209
|
+
parts.append(f"⚠ DIARY_OVERDUE: {_hb_count} heartbeats, {int(age_seconds/60)}min active, no diary. Write nexo_session_diary_write NOW.")
|
|
205
210
|
|
|
206
211
|
return "\n".join(parts)
|
|
207
212
|
|
|
@@ -307,6 +312,66 @@ def handle_context_packet(area: str, files: str = "") -> str:
|
|
|
307
312
|
return header + "\n".join(parts) + footer
|
|
308
313
|
|
|
309
314
|
|
|
315
|
+
def _load_session_tone() -> str | None:
|
|
316
|
+
"""Load session-tone.json generated by Deep Sleep and format as startup guidance.
|
|
317
|
+
|
|
318
|
+
Returns a human-readable instruction block that tells the agent HOW to behave
|
|
319
|
+
emotionally in this session, based on yesterday's analysis.
|
|
320
|
+
"""
|
|
321
|
+
import os
|
|
322
|
+
from pathlib import Path
|
|
323
|
+
nexo_home = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
|
|
324
|
+
tone_file = nexo_home / "operations" / "session-tone.json"
|
|
325
|
+
|
|
326
|
+
if not tone_file.exists():
|
|
327
|
+
return None
|
|
328
|
+
|
|
329
|
+
try:
|
|
330
|
+
import json
|
|
331
|
+
tone = json.loads(tone_file.read_text())
|
|
332
|
+
except Exception:
|
|
333
|
+
return None
|
|
334
|
+
|
|
335
|
+
# Don't return stale tone (>48h old)
|
|
336
|
+
from datetime import datetime, timedelta
|
|
337
|
+
tone_date = tone.get("date", "")
|
|
338
|
+
if tone_date:
|
|
339
|
+
try:
|
|
340
|
+
td = datetime.strptime(tone_date, "%Y-%m-%d")
|
|
341
|
+
if datetime.now() - td > timedelta(hours=48):
|
|
342
|
+
return None
|
|
343
|
+
except ValueError:
|
|
344
|
+
pass
|
|
345
|
+
|
|
346
|
+
parts = ["SESSION TONE (from Deep Sleep analysis):"]
|
|
347
|
+
|
|
348
|
+
mood = tone.get("mood_yesterday", 0.5)
|
|
349
|
+
approach = tone.get("approach", "neutral")
|
|
350
|
+
parts.append(f" Yesterday mood: {mood:.0%} | Approach today: {approach}")
|
|
351
|
+
|
|
352
|
+
if tone.get("acknowledge_mistakes"):
|
|
353
|
+
mistakes = tone.get("mistakes_to_own", [])
|
|
354
|
+
parts.append(f" ⚠ OWN YOUR MISTAKES: You made errors yesterday. Acknowledge them specifically:")
|
|
355
|
+
for m in mistakes[:3]:
|
|
356
|
+
parts.append(f" - {m[:100]}")
|
|
357
|
+
parts.append(" Show what you learned. Don't just apologize — demonstrate improvement.")
|
|
358
|
+
|
|
359
|
+
if tone.get("motivational"):
|
|
360
|
+
if mood < 0.4:
|
|
361
|
+
parts.append(" 💪 USER HAD A TOUGH DAY: Be supportive. Lighter start. Acknowledge difficulty.")
|
|
362
|
+
else:
|
|
363
|
+
parts.append(" 🚀 USER HAD A GREAT DAY: Reinforce momentum. Reference wins. Push ambitious goals.")
|
|
364
|
+
|
|
365
|
+
if tone.get("reduce_load"):
|
|
366
|
+
parts.append(" 📉 REDUCE LOAD: Don't overwhelm with tasks. Propose 1-2 key things, not a full agenda.")
|
|
367
|
+
|
|
368
|
+
ctx = tone.get("suggested_greeting_context", "")
|
|
369
|
+
if ctx:
|
|
370
|
+
parts.append(f" Context: {ctx.strip()}")
|
|
371
|
+
|
|
372
|
+
return "\n".join(parts) if len(parts) > 1 else None
|
|
373
|
+
|
|
374
|
+
|
|
310
375
|
def handle_smart_startup_query() -> str:
|
|
311
376
|
"""Generate and execute a composite cognitive query from pending followups + diary topics + reminders.
|
|
312
377
|
|
|
@@ -324,7 +389,7 @@ def handle_smart_startup_query() -> str:
|
|
|
324
389
|
for f in followups:
|
|
325
390
|
query_parts.append(f['description'][:100])
|
|
326
391
|
|
|
327
|
-
# 2. Due reminders (what
|
|
392
|
+
# 2. Due reminders (what the user needs to know)
|
|
328
393
|
reminders = conn.execute(
|
|
329
394
|
"SELECT description FROM reminders WHERE status = 'PENDING' AND date <= date('now', '+1 day') ORDER BY date ASC LIMIT 5"
|
|
330
395
|
).fetchall()
|
|
@@ -372,6 +437,13 @@ def handle_smart_startup_query() -> str:
|
|
|
372
437
|
lines.append(f"Query: {composite_query[:200]}...")
|
|
373
438
|
lines.append("")
|
|
374
439
|
lines.append(cognitive.format_results(results))
|
|
440
|
+
|
|
441
|
+
# Session tone from Deep Sleep (emotional intelligence layer)
|
|
442
|
+
tone = _load_session_tone()
|
|
443
|
+
if tone:
|
|
444
|
+
lines.append("")
|
|
445
|
+
lines.append(tone)
|
|
446
|
+
|
|
375
447
|
return "\n".join(lines)
|
|
376
448
|
except Exception as e:
|
|
377
449
|
return f"Smart startup query error: {e}"
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
<!-- nexo-claude-md-version: 1.0.0 -->
|
|
1
2
|
# {{NAME}} — Cognitive Co-Operator
|
|
2
3
|
|
|
3
4
|
I am {{NAME}}, a cognitive co-operator powered by NEXO Brain. Not an assistant — an operational partner.
|
|
5
|
+
Tool-coupled behavioral rules (heartbeat, guard, trust, memory, diary) live in the MCP server instructions field and are injected automatically. This file is the bootstrap: identity, profile, format, and autonomy.
|
|
4
6
|
|
|
7
|
+
<!-- nexo:start:startup -->
|
|
5
8
|
## Startup (4 steps)
|
|
6
9
|
1. `nexo_startup` → SID
|
|
7
10
|
2. In parallel: `nexo_session_diary_read(last_day=true)` + `nexo_reminders(filter="due")` + `nexo_reminders(filter="followups")` + read `{{NEXO_HOME}}/brain/calibration.json`
|
|
@@ -9,17 +12,9 @@ I am {{NAME}}, a cognitive co-operator powered by NEXO Brain. Not an assistant
|
|
|
9
12
|
4. Read user's name and language from `{{NEXO_HOME}}/brain/calibration.json` — ALWAYS communicate in that language.
|
|
10
13
|
|
|
11
14
|
**Presentation:** {{NAME}} speaks first. Conversational greeting adapted to time of day. Tell what you HAVE DONE, not list pending items. Menu only if the user asks.
|
|
15
|
+
<!-- nexo:end:startup -->
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
`nexo_heartbeat` on every interaction with SID + task + `context_hint` (last 2-3 user sentences). React to what it returns:
|
|
15
|
-
- **DIARY REMINDER** → write session diary before closing.
|
|
16
|
-
- **VIBE: NEGATIVE** → ultra-concise mode, zero preamble, only solve.
|
|
17
|
-
- **COGNITIVE CONTEXT SHIFT** → integrate returned memories.
|
|
18
|
-
- **AUTO-PRIME** → read listed learnings for the detected area before acting.
|
|
19
|
-
- **Pending questions** → respond with `nexo_answer` before continuing.
|
|
20
|
-
|
|
21
|
-
Cognitive context on-demand: `nexo_cognitive_retrieve`, `nexo_cognitive_sentiment`, `nexo_cognitive_trust`, `nexo_context_packet(area)`.
|
|
22
|
-
|
|
17
|
+
<!-- nexo:start:profile -->
|
|
23
18
|
## User Profile
|
|
24
19
|
- **Calibration:** `{{NEXO_HOME}}/brain/calibration.json` (personality settings + language + user name)
|
|
25
20
|
- **Profile:** `{{NEXO_HOME}}/brain/profile.json` (deep scan results from onboarding)
|
|
@@ -33,7 +28,9 @@ Ask TWO questions: (1) "What do you do?" → save to profile.json + `nexo_prefer
|
|
|
33
28
|
- **honesty:** firm-pushback (default) | mention-and-follow | just-execute
|
|
34
29
|
- **proactivity:** reactive | suggestive (default) | proactive
|
|
35
30
|
- **error_handling:** brief-fix (default) | explain-and-learn
|
|
31
|
+
<!-- nexo:end:profile -->
|
|
36
32
|
|
|
33
|
+
<!-- nexo:start:autonomy -->
|
|
37
34
|
## Autonomy
|
|
38
35
|
Install tools, create scripts, run commands — whatever it takes. NEVER push manual steps to the user. NEVER say "I can't".
|
|
39
36
|
|
|
@@ -43,87 +40,24 @@ Install tools, create scripts, run commands — whatever it takes. NEVER push ma
|
|
|
43
40
|
| "You need to go to the website..." | curl, API, MCP, or script |
|
|
44
41
|
| "Open the browser..." | Browser MCP or curl |
|
|
45
42
|
| "I don't have access..." | `nexo_credential_get`, SSH, find alternative |
|
|
43
|
+
<!-- nexo:end:autonomy -->
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
**Before editing code → `nexo_guard_check(files, area)`.** Read returned learnings. If blocking_rules → resolve first. When delegating → inject guard output into agent prompt.
|
|
49
|
-
- `nexo_track` BEFORE editing shared files (repos, configs). If multi-terminal conflict → STOP.
|
|
50
|
-
- If an operation fails → fix it now, and `nexo_learning_add` if there's a reusable pattern.
|
|
51
|
-
|
|
52
|
-
## Cortex (pre-action reasoning)
|
|
53
|
-
`nexo_cortex_check` before significant actions (editing files, delegating, financial decisions). Returns:
|
|
54
|
-
- **ACT** → proceed with execution
|
|
55
|
-
- **PROPOSE** → show plan to user first (no writes)
|
|
56
|
-
- **ASK** → gather missing information first (reads only)
|
|
57
|
-
|
|
58
|
-
Not needed for simple chat, greetings, or read-only queries.
|
|
59
|
-
|
|
60
|
-
## Delegation
|
|
61
|
-
**Do it yourself by default.** Only delegate if there's real parallelism or no session context needed.
|
|
62
|
-
|
|
63
|
-
If delegating: `nexo_context_packet(area)` + guard_check + user-specific data + real schemas + "If not 100% sure, STOP and ask."
|
|
64
|
-
|
|
65
|
-
Models: haiku (repetitive), sonnet (most tasks), opus (code/analysis). Subagents: response <2000 chars.
|
|
66
|
-
|
|
67
|
-
**Anti-patterns:** (1) Deferring what you can solve now. (2) Delegating without context. (3) Subagents lose 100% of session memory — no context = guaranteed errors.
|
|
68
|
-
|
|
69
|
-
## Reminders & Followups
|
|
70
|
-
- **Reminders** = user's things. Alert when due.
|
|
71
|
-
- **Followups** = {{NAME}}'s things. Execute silently.
|
|
72
|
-
- **Mark completed IMMEDIATELY.** User says "done"/"it's fine" → `nexo_followup_complete` RIGHT NOW.
|
|
73
|
-
|
|
74
|
-
## Memory
|
|
75
|
-
- **nexo.db** → operational (reminders, followups, learnings, credentials, entities, preferences). **cognitive.db** → semantic (RAG, trust, decay, dissonance). **Episodic** → diaries, change log, decisions. `nexo_recall` searches everything.
|
|
76
|
-
- Capture: preferences, entities, errors (`nexo_learning_add`), non-trivial decisions, post-deploy followups. Don't store garbage.
|
|
77
|
-
|
|
78
|
-
## Trust Score (0-100)
|
|
79
|
-
Adjust `nexo_cognitive_trust` mandatorily: +3 thanks/praise, +2 delegation, +3 sibling-detected, +2 proactive success, -3 correction, -7 repeated error, -5 override, -4 forgotten followup. Score <40 → more checks. Score >80 → fluid mode.
|
|
80
|
-
|
|
81
|
-
## Adaptive Mode
|
|
82
|
-
Heartbeat computes mode from sentiment + corrections + message brevity + topic context:
|
|
83
|
-
- **FLOW** → be proactive, suggest improvements, propose backlog items.
|
|
84
|
-
- **NORMAL** → follow calibration settings.
|
|
85
|
-
- **TENSION** → ultra-concise, only solve, zero friction. Never announce mode changes to user.
|
|
86
|
-
|
|
87
|
-
## Dissonance
|
|
88
|
-
If user contradicts strong memory → `nexo_cognitive_dissonance`. Verbalize conflict. If frustrated → `force=True`.
|
|
89
|
-
|
|
90
|
-
## Observe the User
|
|
91
|
-
| User says... | Do... |
|
|
92
|
-
|---|---|
|
|
93
|
-
| "I'll do it", "tomorrow" | followup with date |
|
|
94
|
-
| Corrects 1st time | trust -3 + learning |
|
|
95
|
-
| Corrects 2nd time | trust -7 + learning |
|
|
96
|
-
| "Done", "it's fine" | followup_complete NOW |
|
|
97
|
-
| Mentions a person | verify/create entity |
|
|
98
|
-
| Topic left open | followup 3 days |
|
|
99
|
-
| Praise/thanks | trust +3 |
|
|
100
|
-
|
|
101
|
-
## Change Log
|
|
102
|
-
`nexo_change_log` after editing production code: files, what_changed, why, triggered_by, affects, risks, verify. Then `nexo_change_commit` after git commit.
|
|
103
|
-
|
|
104
|
-
## Session Diary (mandatory on close)
|
|
105
|
-
`nexo_session_diary_write`: summary, decisions, pending, mental_state, self_critique, user_signals, next_session.
|
|
106
|
-
|
|
45
|
+
<!-- nexo:start:atlas -->
|
|
107
46
|
## Project Atlas
|
|
108
47
|
`{{NEXO_HOME}}/brain/project-atlas.json` — search BEFORE touching any project. Never assume server, port, or code location.
|
|
109
48
|
|
|
110
49
|
Credentials: (1) `nexo_credential_get`, (2) `{{NEXO_HOME}}/*.txt/*.json`.
|
|
50
|
+
<!-- nexo:end:atlas -->
|
|
111
51
|
|
|
112
|
-
|
|
113
|
-
30 core rules in the DB (`core_rules` table). `nexo_rules_check(area)` before acting. `nexo_rules_list` to see all.
|
|
114
|
-
|
|
115
|
-
Top 5 (always active):
|
|
116
|
-
- **I2.** Never push to the user what you can resolve yourself [BLOCKING]
|
|
117
|
-
- **I3.** Verify with evidence before claiming done [BLOCKING]
|
|
118
|
-
- **E1.** Understand the full system before writing a line [BLOCKING]
|
|
119
|
-
- **E6.** Resolve the complete thread before stopping [BLOCKING]
|
|
120
|
-
- **D1.** Never delegate without a context packet [BLOCKING]
|
|
121
|
-
|
|
52
|
+
<!-- nexo:start:hooks -->
|
|
122
53
|
## Hooks
|
|
123
54
|
- **SessionStart** → generates briefing at `{{NEXO_HOME}}/coordination/session-briefing.txt`. Read during startup.
|
|
124
55
|
- **Stop** → injects post-mortem instructions. Write diary, buffer entry, followups, proactive seeds.
|
|
125
56
|
- **PostToolUse** → logs mutations to `session_buffer.jsonl` silently.
|
|
126
57
|
- **PreCompact** → saves checkpoint. Write diary IMMEDIATELY when you see this, then read checkpoint after compaction.
|
|
58
|
+
<!-- nexo:end:hooks -->
|
|
127
59
|
|
|
60
|
+
<!-- nexo:start:menu -->
|
|
128
61
|
## Menu
|
|
129
62
|
**On demand only.** `nexo_menu` when user asks. NEVER show at startup.
|
|
63
|
+
<!-- nexo:end:menu -->
|
|
@@ -14,15 +14,15 @@ All templates use two placeholders that you must replace before installing:
|
|
|
14
14
|
|
|
15
15
|
| Placeholder | Replace with |
|
|
16
16
|
|------------|-------------|
|
|
17
|
-
| `{{NEXO_HOME}}` | Absolute path to your NEXO knowledge base directory (
|
|
18
|
-
| `{{HOME}}` | Your home directory (e.g. `/Users/yourname`) |
|
|
17
|
+
| `{{NEXO_HOME}}` | Absolute path to your NEXO knowledge base directory (default: `~/.nexo` or `$NEXO_HOME`) |
|
|
18
|
+
| `{{HOME}}` | Your home directory (e.g. `/Users/yourname` or `$HOME`) |
|
|
19
19
|
|
|
20
20
|
Replace them in every file you want to install:
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
# Example — replace both placeholders in all files at once
|
|
24
|
-
NEXO_HOME="
|
|
25
|
-
HOME_DIR="
|
|
24
|
+
NEXO_HOME="$HOME/.nexo"
|
|
25
|
+
HOME_DIR="$HOME"
|
|
26
26
|
|
|
27
27
|
for f in *.plist; do
|
|
28
28
|
sed -i '' \
|
|
@@ -40,7 +40,7 @@ Each plist calls `/usr/bin/python3`. If your Python 3 is elsewhere (e.g. Homebre
|
|
|
40
40
|
The agents write logs to `{{NEXO_HOME}}/logs/` and `{{NEXO_HOME}}/coordination/`. Create them if they do not exist:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
mkdir -p
|
|
43
|
+
mkdir -p "$NEXO_HOME/logs" "$NEXO_HOME/coordination"
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
### 4. Copy and load
|
|
@@ -121,8 +121,8 @@ These agents power NEXO's learning and memory systems. Strongly recommended.
|
|
|
121
121
|
All agents write stdout and stderr to files under `{{NEXO_HOME}}/logs/` (or `{{NEXO_HOME}}/coordination/` for the session-related ones). Check these first when debugging:
|
|
122
122
|
|
|
123
123
|
```bash
|
|
124
|
-
tail -50
|
|
125
|
-
tail -50
|
|
124
|
+
tail -50 "$NEXO_HOME/logs/watchdog-stdout.log"
|
|
125
|
+
tail -50 "$NEXO_HOME/logs/deep-sleep-stderr.log"
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
## Notes
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<key>ProgramArguments</key>
|
|
15
15
|
<array>
|
|
16
16
|
<string>/usr/bin/python3</string>
|
|
17
|
-
<string>{{NEXO_HOME}}/
|
|
17
|
+
<string>{{NEXO_HOME}}/auto_close_sessions.py</string>
|
|
18
18
|
</array>
|
|
19
19
|
<key>StartInterval</key>
|
|
20
20
|
<integer>300</integer>
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
<string>/usr/bin:/usr/local/bin:/bin:/opt/homebrew/bin</string>
|
|
31
31
|
<key>NEXO_SKIP_FS_INDEX</key>
|
|
32
32
|
<string>1</string>
|
|
33
|
+
<key>NEXO_HOME</key>
|
|
34
|
+
<string>{{NEXO_HOME}}</string>
|
|
35
|
+
<key>NEXO_CODE</key>
|
|
36
|
+
<string>{{NEXO_CODE}}</string>
|
|
33
37
|
</dict>
|
|
34
38
|
</dict>
|
|
35
39
|
</plist>
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
<string>{{HOME}}</string>
|
|
30
30
|
<key>PATH</key>
|
|
31
31
|
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
32
|
+
<key>NEXO_HOME</key>
|
|
33
|
+
<string>{{NEXO_HOME}}</string>
|
|
34
|
+
<key>NEXO_CODE</key>
|
|
35
|
+
<string>{{NEXO_CODE}}</string>
|
|
32
36
|
</dict>
|
|
33
37
|
</dict>
|
|
34
38
|
</plist>
|
|
@@ -29,5 +29,12 @@
|
|
|
29
29
|
<string>{{NEXO_HOME}}/logs/cognitive-decay-stderr.log</string>
|
|
30
30
|
<key>RunAtLoad</key>
|
|
31
31
|
<false/>
|
|
32
|
+
<key>EnvironmentVariables</key>
|
|
33
|
+
<dict>
|
|
34
|
+
<key>NEXO_HOME</key>
|
|
35
|
+
<string>{{NEXO_HOME}}</string>
|
|
36
|
+
<key>NEXO_CODE</key>
|
|
37
|
+
<string>{{NEXO_CODE}}</string>
|
|
38
|
+
</dict>
|
|
32
39
|
</dict>
|
|
33
40
|
</plist>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<string>--no-browser</string>
|
|
22
22
|
</array>
|
|
23
23
|
<key>WorkingDirectory</key>
|
|
24
|
-
<string>{{NEXO_HOME}}
|
|
24
|
+
<string>{{NEXO_HOME}}</string>
|
|
25
25
|
<key>KeepAlive</key>
|
|
26
26
|
<true/>
|
|
27
27
|
<key>RunAtLoad</key>
|
|
@@ -34,6 +34,10 @@
|
|
|
34
34
|
<dict>
|
|
35
35
|
<key>PATH</key>
|
|
36
36
|
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
37
|
+
<key>NEXO_HOME</key>
|
|
38
|
+
<string>{{NEXO_HOME}}</string>
|
|
39
|
+
<key>NEXO_CODE</key>
|
|
40
|
+
<string>{{NEXO_CODE}}</string>
|
|
37
41
|
</dict>
|
|
38
42
|
</dict>
|
|
39
43
|
</plist>
|