nexo-brain 2.3.0 → 2.3.2
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 +1 -1
- package/bin/nexo-brain.js +92 -9
- package/bin/postinstall.js +22 -15
- package/package.json +7 -4
- package/src/auto_update.py +194 -5
- package/src/crons/sync.py +6 -2
- package/src/db/_core.py +1 -0
- package/src/db/_entities.py +1 -0
- package/src/db/_episodic.py +1 -0
- package/src/db/_learnings.py +1 -0
- package/src/db/_reminders.py +1 -0
- package/src/db/_schema.py +11 -1
- package/src/db/_sessions.py +1 -0
- package/src/db/_skills.py +1 -0
- package/src/hooks/capture-tool-logs.sh +23 -6
- package/src/hooks/session-start.sh +4 -3
- package/src/plugin_loader.py +1 -0
- package/src/plugins/update.py +377 -26
- package/src/scripts/deep-sleep/apply_findings.py +1 -0
- package/src/scripts/deep-sleep/collect.py +1 -0
- package/src/scripts/deep-sleep/extract.py +1 -0
- package/src/scripts/deep-sleep/synthesize.py +1 -0
- package/src/scripts/nexo-catchup.py +29 -4
- package/src/scripts/nexo-daily-self-audit.py +21 -1
- package/src/scripts/nexo-evolution-run.py +21 -1
- package/src/scripts/nexo-learning-housekeep.py +1 -0
- package/src/scripts/nexo-postmortem-consolidator.py +34 -9
- package/src/scripts/nexo-sleep.py +32 -10
- package/src/scripts/nexo-synthesis.py +29 -9
- package/src/scripts/nexo-update.sh +109 -7
- package/src/scripts/nexo-watchdog.sh +122 -58
- package/src/server.py +66 -1
- package/src/tools_coordination.py +1 -0
- package/src/tools_sessions.py +1 -0
- package/scripts/migrate-to-unified 2.sh +0 -813
- package/scripts/migrate-to-unified.sh +0 -813
- package/scripts/migrate-v1.5-to-v1.6 2.py +0 -778
- package/scripts/migrate-v1.5-to-v1.6.py +0 -778
- package/scripts/migrate-v1.7-to-v1.8 2.py +0 -214
- package/scripts/migrate-v1.7-to-v1.8.py +0 -214
- package/scripts/nexo-preflight.sh +0 -236
- package/scripts/pre-commit-check 2.sh +0 -55
- package/scripts/pre-commit-check.sh +0 -55
- package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
- 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__/plugin_loader.cpython-314.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 2.py +0 -159
- package/src/auto_update 2.py +0 -634
- package/src/claim_graph 2.py +0 -323
- package/src/cognitive/__init__ 2.py +0 -62
- 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 2.py +0 -567
- package/src/cognitive/_decay 2.py +0 -382
- package/src/cognitive/_ingest 2.py +0 -892
- package/src/cognitive/_memory 2.py +0 -912
- package/src/cognitive/_search 2.py +0 -949
- package/src/cognitive/_trust 2.py +0 -464
- package/src/crons/__pycache__/sync.cpython-314.pyc +0 -0
- package/src/crons/manifest 2.json +0 -106
- package/src/crons/sync 2.py +0 -217
- package/src/dashboard/__init__ 2.py +0 -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 2.py +0 -789
- package/src/db/__init__ 2.py +0 -89
- 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__/_cron_runs.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_cron_runs.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__/_skills.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_skills.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_skills.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 2.py +0 -417
- package/src/db/_credentials 2.py +0 -124
- package/src/db/_entities 2.py +0 -178
- package/src/db/_episodic 2.py +0 -738
- package/src/db/_evolution 2.py +0 -54
- package/src/db/_fts 2.py +0 -406
- package/src/db/_learnings 2.py +0 -168
- package/src/db/_reminders 2.py +0 -338
- package/src/db/_schema 2.py +0 -364
- package/src/db/_sessions 2.py +0 -300
- package/src/db/_tasks 2.py +0 -91
- package/src/evolution_cycle 2.py +0 -266
- package/src/hnsw_index 2.py +0 -254
- package/src/hooks/auto_capture 2.py +0 -208
- package/src/hooks/caffeinate-guard 2.sh +0 -8
- package/src/hooks/capture-session 2.sh +0 -21
- package/src/hooks/capture-tool-logs 2.sh +0 -127
- package/src/hooks/daily-briefing-check 2.sh +0 -33
- package/src/hooks/inbox-hook 2.sh +0 -76
- package/src/hooks/post-compact 2.sh +0 -148
- package/src/hooks/pre-compact 2.sh +0 -151
- package/src/hooks/session-start 2.sh +0 -268
- package/src/hooks/session-stop 2.sh +0 -140
- package/src/kg_populate 2.py +0 -290
- package/src/knowledge_graph 2.py +0 -257
- package/src/maintenance 2.py +0 -59
- package/src/migrate_embeddings 2.py +0 -122
- package/src/plugin_loader 2.py +0 -202
- package/src/plugins/__init__ 2.py +0 -0
- package/src/plugins/__pycache__/__init__ 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode 2.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 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/backup 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/core_rules 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cortex 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/entities 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/evolution 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/guard 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/preferences 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/schedule.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/schedule.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/skills.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/skills.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/update 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
- package/src/plugins/adaptive_mode 2.py +0 -805
- package/src/plugins/agents 2.py +0 -52
- package/src/plugins/artifact_registry 2.py +0 -450
- package/src/plugins/backup 2.py +0 -104
- package/src/plugins/cognitive_memory 2.py +0 -564
- package/src/plugins/core_rules 2.py +0 -252
- package/src/plugins/cortex 2.py +0 -299
- package/src/plugins/entities 2.py +0 -67
- package/src/plugins/episodic_memory 2.py +0 -533
- package/src/plugins/evolution 2.py +0 -115
- package/src/plugins/guard 2.py +0 -746
- package/src/plugins/knowledge_graph_tools 2.py +0 -105
- package/src/plugins/preferences 2.py +0 -47
- package/src/plugins/update 2.py +0 -256
- package/src/requirements 2.txt +0 -12
- package/src/rules/__init__ 2.py +0 -0
- package/src/rules/core-rules 2.json +0 -331
- package/src/rules/migrate 2.py +0 -207
- 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-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/check-context 2.py +0 -264
- package/src/scripts/nexo-auto-update 2.py +0 -6
- package/src/scripts/nexo-backup 2.sh +0 -25
- package/src/scripts/nexo-brain-activation 2.sh +0 -140
- package/src/scripts/nexo-catchup 2.py +0 -242
- package/src/scripts/nexo-cognitive-decay 2.py +0 -182
- package/src/scripts/nexo-daily-self-audit 2.py +0 -552
- package/src/scripts/nexo-deep-sleep 2.sh +0 -97
- package/src/scripts/nexo-evolution-run 2.py +0 -597
- package/src/scripts/nexo-followup-hygiene 2.py +0 -112
- package/src/scripts/nexo-github-monitor 2.py +0 -256
- package/src/scripts/nexo-immune 2.py +0 -927
- package/src/scripts/nexo-inbox-hook 2.sh +0 -74
- package/src/scripts/nexo-install 2.py +0 -6
- package/src/scripts/nexo-learning-housekeep 2.py +0 -245
- package/src/scripts/nexo-learning-validator 2.py +0 -207
- package/src/scripts/nexo-migrate 2.py +0 -232
- package/src/scripts/nexo-postmortem-consolidator 2.py +0 -421
- package/src/scripts/nexo-pre-commit 2.py +0 -120
- package/src/scripts/nexo-prevent-sleep 2.sh +0 -29
- package/src/scripts/nexo-proactive-dashboard 2.py +0 -345
- package/src/scripts/nexo-reflection 2.py +0 -253
- package/src/scripts/nexo-runtime-preflight 2.py +0 -274
- package/src/scripts/nexo-send-email 2.py +0 -25
- package/src/scripts/nexo-send-email.py +0 -25
- package/src/scripts/nexo-send-reply 2.py +0 -178
- package/src/scripts/nexo-send-reply.py +0 -178
- package/src/scripts/nexo-sleep 2.py +0 -592
- package/src/scripts/nexo-snapshot-restore 2.sh +0 -35
- package/src/scripts/nexo-synthesis 2.py +0 -253
- package/src/scripts/nexo-tcc-approve 2.sh +0 -79
- package/src/scripts/nexo-update 2.sh +0 -161
- package/src/scripts/nexo-watchdog 2.sh +0 -878
- package/src/scripts/nexo-watchdog-smoke 2.py +0 -119
- package/src/server 2.py +0 -733
- package/src/storage_router 2.py +0 -32
- package/src/tools_coordination 2.py +0 -102
- package/src/tools_credentials 2.py +0 -68
- package/src/tools_learnings 2.py +0 -220
- package/src/tools_menu 2.py +0 -227
- package/src/tools_reminders 2.py +0 -86
- package/src/tools_reminders_crud 2.py +0 -159
- package/src/tools_sessions 2.py +0 -476
- package/src/tools_task_history 2.py +0 -57
- package/templates/CLAUDE.md 2.template +0 -63
- package/templates/openclaw 2.json +0 -13
- package/tests/__init__ 2.py +0 -0
- package/tests/__init__.py +0 -0
- package/tests/conftest 2.py +0 -71
- package/tests/conftest.py +0 -71
- package/tests/test_cognitive 2.py +0 -205
- package/tests/test_cognitive.py +0 -205
- package/tests/test_knowledge_graph 2.py +0 -140
- package/tests/test_knowledge_graph.py +0 -140
- package/tests/test_migrations 2.py +0 -137
- package/tests/test_migrations.py +0 -137
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
NEXO Reflection Engine — Processes session_buffer.jsonl entries.
|
|
4
|
-
|
|
5
|
-
Triggered by the stop hook when >=3 sessions have accumulated and
|
|
6
|
-
the last reflection was >4 hours ago.
|
|
7
|
-
|
|
8
|
-
What it does:
|
|
9
|
-
1. Reads all entries from session_buffer.jsonl
|
|
10
|
-
2. Extracts patterns: recurring tasks, common errors, user mood trends
|
|
11
|
-
3. Updates user_model.json with observed patterns
|
|
12
|
-
4. Writes a reflection summary to reflection-log.json
|
|
13
|
-
5. Clears processed entries from the buffer
|
|
14
|
-
|
|
15
|
-
Runs as a standalone Python script (no LLM needed).
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
import json
|
|
19
|
-
import os
|
|
20
|
-
import sys
|
|
21
|
-
from datetime import datetime
|
|
22
|
-
from collections import Counter
|
|
23
|
-
|
|
24
|
-
NEXO_HOME = os.environ.get("NEXO_HOME", os.path.expanduser("~/.nexo"))
|
|
25
|
-
BUFFER_PATH = os.path.join(NEXO_HOME, "brain", "session_buffer.jsonl")
|
|
26
|
-
USER_MODEL_PATH = os.path.join(NEXO_HOME, "brain", "user_model.json")
|
|
27
|
-
REFLECTION_LOG_PATH = os.path.join(NEXO_HOME, "coordination", "reflection-log.json")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def load_buffer():
|
|
31
|
-
"""Load all entries from session buffer."""
|
|
32
|
-
entries = []
|
|
33
|
-
if not os.path.exists(BUFFER_PATH):
|
|
34
|
-
return entries
|
|
35
|
-
with open(BUFFER_PATH, "r") as f:
|
|
36
|
-
for line in f:
|
|
37
|
-
line = line.strip()
|
|
38
|
-
if not line:
|
|
39
|
-
continue
|
|
40
|
-
try:
|
|
41
|
-
entries.append(json.loads(line))
|
|
42
|
-
except json.JSONDecodeError:
|
|
43
|
-
continue
|
|
44
|
-
return entries
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def load_user_model():
|
|
48
|
-
"""Load existing user model or create empty one."""
|
|
49
|
-
if os.path.exists(USER_MODEL_PATH):
|
|
50
|
-
try:
|
|
51
|
-
return json.load(open(USER_MODEL_PATH))
|
|
52
|
-
except (json.JSONDecodeError, IOError):
|
|
53
|
-
pass
|
|
54
|
-
return {
|
|
55
|
-
"created": datetime.now().strftime("%Y-%m-%d"),
|
|
56
|
-
"traits": {},
|
|
57
|
-
"work_patterns": {},
|
|
58
|
-
"mood_history": [],
|
|
59
|
-
"common_tasks": [],
|
|
60
|
-
"error_patterns": [],
|
|
61
|
-
"reflections_count": 0,
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def load_reflection_log():
|
|
66
|
-
"""Load existing reflection log."""
|
|
67
|
-
if os.path.exists(REFLECTION_LOG_PATH):
|
|
68
|
-
try:
|
|
69
|
-
return json.load(open(REFLECTION_LOG_PATH))
|
|
70
|
-
except (json.JSONDecodeError, IOError):
|
|
71
|
-
pass
|
|
72
|
-
return []
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def analyze_entries(entries):
|
|
76
|
-
"""Extract patterns from buffer entries."""
|
|
77
|
-
tasks = []
|
|
78
|
-
decisions = []
|
|
79
|
-
errors = []
|
|
80
|
-
moods = Counter()
|
|
81
|
-
user_patterns = []
|
|
82
|
-
files_modified = []
|
|
83
|
-
self_critiques = []
|
|
84
|
-
|
|
85
|
-
for entry in entries:
|
|
86
|
-
source = entry.get("source", "")
|
|
87
|
-
# Skip hook-fallback entries (they have no real data)
|
|
88
|
-
if source == "hook-fallback":
|
|
89
|
-
continue
|
|
90
|
-
|
|
91
|
-
# Collect tasks
|
|
92
|
-
for t in entry.get("tasks", []):
|
|
93
|
-
if t and t != "session ended":
|
|
94
|
-
tasks.append(t)
|
|
95
|
-
|
|
96
|
-
# Collect decisions
|
|
97
|
-
for d in entry.get("decisions", []):
|
|
98
|
-
if d:
|
|
99
|
-
decisions.append(d)
|
|
100
|
-
|
|
101
|
-
# Collect errors
|
|
102
|
-
for e in entry.get("errors_resolved", []):
|
|
103
|
-
if e:
|
|
104
|
-
errors.append(e)
|
|
105
|
-
|
|
106
|
-
# Count moods
|
|
107
|
-
mood = entry.get("mood", "unknown")
|
|
108
|
-
if mood and mood != "unknown":
|
|
109
|
-
moods[mood] += 1
|
|
110
|
-
|
|
111
|
-
# User patterns
|
|
112
|
-
for p in entry.get("user_patterns", []):
|
|
113
|
-
if p:
|
|
114
|
-
user_patterns.append(p)
|
|
115
|
-
|
|
116
|
-
# Files
|
|
117
|
-
for f in entry.get("files_modified", []):
|
|
118
|
-
if f:
|
|
119
|
-
files_modified.append(f)
|
|
120
|
-
|
|
121
|
-
# Self-critiques
|
|
122
|
-
critique = entry.get("self_critique", "")
|
|
123
|
-
if critique and "hook-fallback" not in critique:
|
|
124
|
-
self_critiques.append(critique)
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
"tasks": tasks,
|
|
128
|
-
"decisions": decisions,
|
|
129
|
-
"errors": errors,
|
|
130
|
-
"moods": dict(moods),
|
|
131
|
-
"user_patterns": user_patterns,
|
|
132
|
-
"files_modified": files_modified,
|
|
133
|
-
"self_critiques": self_critiques,
|
|
134
|
-
"entry_count": len(entries),
|
|
135
|
-
"claude_entries": sum(1 for e in entries if e.get("source") == "claude"),
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def update_user_model(model, analysis):
|
|
140
|
-
"""Update user model with new patterns."""
|
|
141
|
-
# Update mood history (keep last 50)
|
|
142
|
-
if analysis["moods"]:
|
|
143
|
-
model["mood_history"].append({
|
|
144
|
-
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M"),
|
|
145
|
-
"moods": analysis["moods"],
|
|
146
|
-
})
|
|
147
|
-
model["mood_history"] = model["mood_history"][-50:]
|
|
148
|
-
|
|
149
|
-
# Update common tasks (top 20)
|
|
150
|
-
task_counter = Counter(model.get("common_tasks", []))
|
|
151
|
-
task_counter.update(analysis["tasks"])
|
|
152
|
-
model["common_tasks"] = [t for t, _ in task_counter.most_common(20)]
|
|
153
|
-
|
|
154
|
-
# Update error patterns (keep last 30)
|
|
155
|
-
existing_errors = model.get("error_patterns", [])
|
|
156
|
-
existing_errors.extend(analysis["errors"])
|
|
157
|
-
model["error_patterns"] = existing_errors[-30:]
|
|
158
|
-
|
|
159
|
-
# Update work patterns
|
|
160
|
-
if analysis["files_modified"]:
|
|
161
|
-
file_counter = Counter(model.get("work_patterns", {}).get("frequent_files", []))
|
|
162
|
-
file_counter.update(analysis["files_modified"])
|
|
163
|
-
if "work_patterns" not in model:
|
|
164
|
-
model["work_patterns"] = {}
|
|
165
|
-
model["work_patterns"]["frequent_files"] = [
|
|
166
|
-
f for f, _ in file_counter.most_common(20)
|
|
167
|
-
]
|
|
168
|
-
|
|
169
|
-
# Derive traits from mood patterns
|
|
170
|
-
total_moods = sum(analysis["moods"].values())
|
|
171
|
-
if total_moods >= 3:
|
|
172
|
-
dominant_mood = max(analysis["moods"], key=analysis["moods"].get)
|
|
173
|
-
model["traits"]["recent_dominant_mood"] = dominant_mood
|
|
174
|
-
model["traits"]["mood_updated"] = datetime.now().strftime("%Y-%m-%d")
|
|
175
|
-
|
|
176
|
-
model["reflections_count"] = model.get("reflections_count", 0) + 1
|
|
177
|
-
model["last_reflection"] = datetime.now().strftime("%Y-%m-%d %H:%M")
|
|
178
|
-
|
|
179
|
-
return model
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
def main():
|
|
183
|
-
entries = load_buffer()
|
|
184
|
-
if not entries:
|
|
185
|
-
print("No entries in buffer.")
|
|
186
|
-
return
|
|
187
|
-
|
|
188
|
-
# Filter out pure hook entries (tool captures)
|
|
189
|
-
session_entries = [
|
|
190
|
-
e for e in entries
|
|
191
|
-
if e.get("source") in ("claude", "hook-fallback")
|
|
192
|
-
or "tasks" in e
|
|
193
|
-
]
|
|
194
|
-
|
|
195
|
-
if not session_entries:
|
|
196
|
-
print("No session entries to process.")
|
|
197
|
-
return
|
|
198
|
-
|
|
199
|
-
analysis = analyze_entries(session_entries)
|
|
200
|
-
model = load_user_model()
|
|
201
|
-
model = update_user_model(model, analysis)
|
|
202
|
-
|
|
203
|
-
# Save updated model
|
|
204
|
-
os.makedirs(os.path.dirname(USER_MODEL_PATH), exist_ok=True)
|
|
205
|
-
with open(USER_MODEL_PATH, "w") as f:
|
|
206
|
-
json.dump(model, f, indent=2)
|
|
207
|
-
|
|
208
|
-
# Append to reflection log
|
|
209
|
-
log = load_reflection_log()
|
|
210
|
-
reflection = {
|
|
211
|
-
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M"),
|
|
212
|
-
"entries_processed": analysis["entry_count"],
|
|
213
|
-
"claude_entries": analysis["claude_entries"],
|
|
214
|
-
"tasks_seen": len(analysis["tasks"]),
|
|
215
|
-
"decisions_made": len(analysis["decisions"]),
|
|
216
|
-
"errors_resolved": len(analysis["errors"]),
|
|
217
|
-
"mood_distribution": analysis["moods"],
|
|
218
|
-
"self_critiques": analysis["self_critiques"][:5],
|
|
219
|
-
"user_patterns_observed": analysis["user_patterns"][:5],
|
|
220
|
-
}
|
|
221
|
-
log.append(reflection)
|
|
222
|
-
# Keep last 100 reflections
|
|
223
|
-
log = log[-100:]
|
|
224
|
-
|
|
225
|
-
os.makedirs(os.path.dirname(REFLECTION_LOG_PATH), exist_ok=True)
|
|
226
|
-
with open(REFLECTION_LOG_PATH, "w") as f:
|
|
227
|
-
json.dump(log, f, indent=2)
|
|
228
|
-
|
|
229
|
-
# Clear buffer (processed)
|
|
230
|
-
with open(BUFFER_PATH, "w") as f:
|
|
231
|
-
f.write("")
|
|
232
|
-
|
|
233
|
-
# Adaptive personality: decay inter-session tension
|
|
234
|
-
try:
|
|
235
|
-
import sys
|
|
236
|
-
sys.path.insert(0, os.path.join(NEXO_HOME))
|
|
237
|
-
from plugins.adaptive_mode import decay_tension
|
|
238
|
-
decay_result = decay_tension()
|
|
239
|
-
if decay_result:
|
|
240
|
-
reflection["adaptive_decay"] = decay_result
|
|
241
|
-
print(f"Adaptive decay: tension {decay_result['old_tension']} → {decay_result['new_tension']} "
|
|
242
|
-
f"({decay_result['hours_elapsed']}h elapsed)")
|
|
243
|
-
except Exception as e:
|
|
244
|
-
print(f"Adaptive decay skipped: {e}")
|
|
245
|
-
|
|
246
|
-
print(f"Reflection complete: {analysis['entry_count']} entries processed, "
|
|
247
|
-
f"{analysis['claude_entries']} from Claude, "
|
|
248
|
-
f"{len(analysis['errors'])} errors, "
|
|
249
|
-
f"{len(analysis['self_critiques'])} critiques.")
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if __name__ == "__main__":
|
|
253
|
-
main()
|
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
NEXO Runtime Preflight
|
|
4
|
-
|
|
5
|
-
Runs safe end-to-end smoke tests for Cortex and Evolution using a temporary
|
|
6
|
-
workspace and a copied SQLite database. No external API calls are performed.
|
|
7
|
-
Results are written to NEXO_HOME/logs/runtime-preflight-summary.json.
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
from __future__ import annotations
|
|
11
|
-
|
|
12
|
-
import importlib.util
|
|
13
|
-
import json
|
|
14
|
-
import os
|
|
15
|
-
import shutil
|
|
16
|
-
import sqlite3
|
|
17
|
-
import sys
|
|
18
|
-
import tempfile
|
|
19
|
-
import traceback
|
|
20
|
-
from datetime import datetime
|
|
21
|
-
from pathlib import Path
|
|
22
|
-
|
|
23
|
-
HOME = Path.home()
|
|
24
|
-
NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(HOME / ".nexo")))
|
|
25
|
-
# Auto-detect: if running from repo (src/scripts/), use src/ as NEXO_CODE
|
|
26
|
-
_script_dir = Path(__file__).resolve().parent
|
|
27
|
-
_repo_src = _script_dir.parent # src/scripts/ -> src/
|
|
28
|
-
NEXO_CODE = Path(os.environ.get("NEXO_CODE", str(_repo_src) if (_repo_src / "server.py").exists() else str(NEXO_HOME)))
|
|
29
|
-
|
|
30
|
-
LOG_DIR = NEXO_HOME / "logs"
|
|
31
|
-
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
32
|
-
SUMMARY_FILE = LOG_DIR / "runtime-preflight-summary.json"
|
|
33
|
-
DB_FILE = NEXO_HOME / "data" / "nexo.db"
|
|
34
|
-
# Evolution config: NEXO_HOME/brain/ (canonical), NEXO_HOME/cortex/ (legacy fallback), NEXO_CODE (dev fallback)
|
|
35
|
-
def _find_evolution_file(name: str) -> Path:
|
|
36
|
-
for candidate in [NEXO_HOME / "brain" / name, NEXO_HOME / "cortex" / name, NEXO_CODE / name]:
|
|
37
|
-
if candidate.exists():
|
|
38
|
-
return candidate
|
|
39
|
-
return NEXO_HOME / "brain" / name # default canonical path
|
|
40
|
-
|
|
41
|
-
CORTEX_OBJECTIVE = _find_evolution_file("evolution-objective.json")
|
|
42
|
-
CORTEX_PROMPT = _find_evolution_file("evolution-prompt.md")
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def _load_module(name: str, path: Path):
|
|
46
|
-
spec = importlib.util.spec_from_file_location(name, str(path))
|
|
47
|
-
module = importlib.util.module_from_spec(spec)
|
|
48
|
-
sys.modules[name] = module
|
|
49
|
-
assert spec.loader is not None
|
|
50
|
-
spec.loader.exec_module(module)
|
|
51
|
-
return module
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _write_summary(summary: dict):
|
|
55
|
-
SUMMARY_FILE.write_text(json.dumps(summary, indent=2, ensure_ascii=False))
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def _fake_cortex_response(model: str) -> str:
|
|
59
|
-
if "opus" in model:
|
|
60
|
-
payload = {
|
|
61
|
-
"analysis": "Smoke evolution run over temp workspace",
|
|
62
|
-
"proposals": [
|
|
63
|
-
{
|
|
64
|
-
"dimension": "self_improvement",
|
|
65
|
-
"classification": "propose",
|
|
66
|
-
"action": "Add a regression smoke before live cycles",
|
|
67
|
-
"reasoning": "Smoke run should stay side-effect free",
|
|
68
|
-
}
|
|
69
|
-
],
|
|
70
|
-
"dimension_scores": {
|
|
71
|
-
"episodic_memory": 86,
|
|
72
|
-
"autonomy": 53,
|
|
73
|
-
"proactivity": 34,
|
|
74
|
-
"self_improvement": 30,
|
|
75
|
-
"agi": 6,
|
|
76
|
-
},
|
|
77
|
-
"score_evidence": {
|
|
78
|
-
"self_improvement": "Temp evolution smoke completed successfully",
|
|
79
|
-
},
|
|
80
|
-
}
|
|
81
|
-
else:
|
|
82
|
-
payload = {
|
|
83
|
-
"actions_taken": ["preflight perception completed"],
|
|
84
|
-
"signals_detected": 1,
|
|
85
|
-
"pending_questions": [],
|
|
86
|
-
"execute": [],
|
|
87
|
-
"next_interval_suggestion": 600,
|
|
88
|
-
"reflection_done": False,
|
|
89
|
-
"dmn_done": False,
|
|
90
|
-
"briefing_update": {
|
|
91
|
-
"actions_taken": ["perception smoke ok"],
|
|
92
|
-
"signals_active": [{"summary": "Smoke signal", "urgency": "INFO", "score": 10}],
|
|
93
|
-
"recommendations": ["keep runtime checks green"],
|
|
94
|
-
"pending_questions_unanswered": [],
|
|
95
|
-
"dmn_summary": "",
|
|
96
|
-
},
|
|
97
|
-
"working_memory": {
|
|
98
|
-
"current_threads": ["runtime preflight"],
|
|
99
|
-
"attention_focus": "runtime integrity",
|
|
100
|
-
"last_reasoning": "API mocked successfully",
|
|
101
|
-
"watching": ["state writes", "briefing writes"],
|
|
102
|
-
"momentum": "stable",
|
|
103
|
-
},
|
|
104
|
-
"error": None,
|
|
105
|
-
}
|
|
106
|
-
return json.dumps(payload, ensure_ascii=False)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def _fake_runner_response() -> tuple[str, dict]:
|
|
110
|
-
payload = {
|
|
111
|
-
"analysis": "Standalone evolution smoke over temp DB",
|
|
112
|
-
"patterns": [
|
|
113
|
-
{"type": "smoke", "description": "runner executed over temp workspace", "frequency": "once"}
|
|
114
|
-
],
|
|
115
|
-
"proposals": [
|
|
116
|
-
{
|
|
117
|
-
"dimension": "self_improvement",
|
|
118
|
-
"classification": "propose",
|
|
119
|
-
"action": "Keep standalone evolution covered by preflight",
|
|
120
|
-
"reasoning": "Regression prevention",
|
|
121
|
-
}
|
|
122
|
-
],
|
|
123
|
-
"dimension_scores": {
|
|
124
|
-
"episodic_memory": 87,
|
|
125
|
-
"autonomy": 54,
|
|
126
|
-
"proactivity": 35,
|
|
127
|
-
"self_improvement": 31,
|
|
128
|
-
"agi": 6,
|
|
129
|
-
},
|
|
130
|
-
"score_evidence": {
|
|
131
|
-
"self_improvement": "standalone runner smoke ok",
|
|
132
|
-
},
|
|
133
|
-
}
|
|
134
|
-
usage = {"input_tokens": 1234, "output_tokens": 432}
|
|
135
|
-
return json.dumps(payload, ensure_ascii=False), usage
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def main() -> int:
|
|
139
|
-
started = datetime.now().isoformat()
|
|
140
|
-
summary = {
|
|
141
|
-
"timestamp": started,
|
|
142
|
-
"ok": False,
|
|
143
|
-
"checks": {},
|
|
144
|
-
"errors": [],
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if not DB_FILE.exists():
|
|
148
|
-
summary["errors"].append("nexo.db missing")
|
|
149
|
-
_write_summary(summary)
|
|
150
|
-
return 1
|
|
151
|
-
|
|
152
|
-
preflight_root = HOME / ".codex" / "memories"
|
|
153
|
-
preflight_root.mkdir(parents=True, exist_ok=True)
|
|
154
|
-
temp_root = Path(tempfile.mkdtemp(prefix="nexo-runtime-preflight-", dir=str(preflight_root)))
|
|
155
|
-
try:
|
|
156
|
-
temp_db = temp_root / "nexo.db"
|
|
157
|
-
shutil.copy2(DB_FILE, temp_db)
|
|
158
|
-
|
|
159
|
-
temp_cortex_dir = temp_root / "brain"
|
|
160
|
-
temp_logs_dir = temp_root / "logs"
|
|
161
|
-
temp_coord_dir = temp_root / "coordination"
|
|
162
|
-
temp_daily_dir = temp_root / "daily_summaries"
|
|
163
|
-
temp_dmn_dir = temp_root / "dmn_insights"
|
|
164
|
-
temp_snapshots_dir = temp_root / "snapshots"
|
|
165
|
-
temp_sandbox_dir = temp_root / "sandbox" / "workspace"
|
|
166
|
-
temp_scripts_dir = temp_root / "scripts"
|
|
167
|
-
for directory in [
|
|
168
|
-
temp_cortex_dir, temp_logs_dir, temp_coord_dir, temp_daily_dir,
|
|
169
|
-
temp_dmn_dir, temp_snapshots_dir, temp_sandbox_dir, temp_scripts_dir,
|
|
170
|
-
]:
|
|
171
|
-
directory.mkdir(parents=True, exist_ok=True)
|
|
172
|
-
|
|
173
|
-
temp_objective = temp_cortex_dir / "evolution-objective.json"
|
|
174
|
-
temp_prompt = temp_cortex_dir / "evolution-prompt.md"
|
|
175
|
-
if CORTEX_OBJECTIVE.exists():
|
|
176
|
-
shutil.copy2(CORTEX_OBJECTIVE, temp_objective)
|
|
177
|
-
else:
|
|
178
|
-
# Create a minimal objective so evolution smoke tests can proceed
|
|
179
|
-
temp_objective.write_text(json.dumps({"evolution_enabled": True, "dimensions": {}}, indent=2))
|
|
180
|
-
if CORTEX_PROMPT.exists():
|
|
181
|
-
shutil.copy2(CORTEX_PROMPT, temp_prompt)
|
|
182
|
-
snapshot_restore = NEXO_CODE / "scripts" / "nexo-snapshot-restore.sh"
|
|
183
|
-
if snapshot_restore.exists():
|
|
184
|
-
shutil.copy2(snapshot_restore, temp_scripts_dir / "nexo-snapshot-restore.sh")
|
|
185
|
-
|
|
186
|
-
temp_api_key = temp_root / "anthropic-api-key.txt"
|
|
187
|
-
temp_api_key.write_text("smoke-test-key")
|
|
188
|
-
|
|
189
|
-
evolution_cycle = _load_module("evolution_cycle", NEXO_CODE / "evolution_cycle.py")
|
|
190
|
-
evolution_cycle.NEXO_DB = temp_db
|
|
191
|
-
evolution_cycle.NEXO_HOME = temp_root
|
|
192
|
-
evolution_cycle.SANDBOX_DIR = temp_sandbox_dir
|
|
193
|
-
evolution_cycle.SNAPSHOTS_DIR = temp_snapshots_dir
|
|
194
|
-
evolution_cycle.OBJECTIVE_FILE = temp_objective
|
|
195
|
-
evolution_cycle.PROMPT_FILE = temp_prompt
|
|
196
|
-
evolution_cycle.RESTORE_LOG = temp_logs_dir / "snapshot-restores.log"
|
|
197
|
-
|
|
198
|
-
week_data = evolution_cycle.get_week_data(str(temp_db))
|
|
199
|
-
prompt = evolution_cycle.build_evolution_prompt(week_data, evolution_cycle.load_objective())
|
|
200
|
-
restore_ok = evolution_cycle.dry_run_restore_test()
|
|
201
|
-
if not restore_ok:
|
|
202
|
-
raise RuntimeError("dry_run_restore_test failed")
|
|
203
|
-
summary["checks"]["evolution_cycle"] = {
|
|
204
|
-
"learnings": len(week_data.get("learnings", [])),
|
|
205
|
-
"decisions": len(week_data.get("decisions", [])),
|
|
206
|
-
"prompt_chars": len(prompt),
|
|
207
|
-
"restore_ok": restore_ok,
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
cortex = _load_module("cortex_plugin", NEXO_CODE / "plugins" / "cortex.py")
|
|
211
|
-
cortex.NEXO_DB = temp_db
|
|
212
|
-
cortex.DRY_RUN = True
|
|
213
|
-
cortex.BRIEFING_FILE = temp_cortex_dir / "briefing.json"
|
|
214
|
-
cortex.HEALTH_FILE = temp_cortex_dir / "health.json"
|
|
215
|
-
cortex.STATE_FILE = temp_cortex_dir / "state.json"
|
|
216
|
-
cortex.STATE_TMP = temp_cortex_dir / "state.tmp"
|
|
217
|
-
cortex.LOG_DIR = temp_logs_dir
|
|
218
|
-
cortex.COORD_DIR = temp_coord_dir
|
|
219
|
-
cortex.SIGNALS_FILE = temp_coord_dir / "pending-signals.json"
|
|
220
|
-
cortex.DAILY_SUMMARIES_DIR = temp_daily_dir
|
|
221
|
-
cortex.DMN_INSIGHTS_DIR = temp_dmn_dir
|
|
222
|
-
cortex.FAILURE_COUNT_FILE = temp_cortex_dir / ".failure-count"
|
|
223
|
-
cortex.PID_FILE = temp_cortex_dir / "cortex.pid"
|
|
224
|
-
cortex.API_KEY_FILE = temp_api_key
|
|
225
|
-
cortex.wa_notify = lambda *args, **kwargs: None
|
|
226
|
-
cortex.poll_wa_inbox = lambda state: None
|
|
227
|
-
cortex._check_health_endpoints = lambda: []
|
|
228
|
-
cortex._call_anthropic = lambda prompt, model=None, max_tokens=4096: _fake_cortex_response(model or "")
|
|
229
|
-
|
|
230
|
-
# Smoke test: verify cortex plugin loads and has expected tools
|
|
231
|
-
cortex_path = NEXO_CODE / "plugins" / "cortex.py"
|
|
232
|
-
if cortex_path.exists():
|
|
233
|
-
import importlib.util
|
|
234
|
-
spec = importlib.util.spec_from_file_location("cortex_plugin", str(cortex_path))
|
|
235
|
-
cortex_mod = importlib.util.module_from_spec(spec)
|
|
236
|
-
spec.loader.exec_module(cortex_mod)
|
|
237
|
-
assert hasattr(cortex_mod, 'TOOLS'), "cortex plugin missing TOOLS"
|
|
238
|
-
tool_names = [t[1] for t in cortex_mod.TOOLS]
|
|
239
|
-
assert "nexo_cortex_check" in tool_names, "cortex plugin missing nexo_cortex_check tool"
|
|
240
|
-
else:
|
|
241
|
-
tool_names = ["cortex.py not found"]
|
|
242
|
-
summary["checks"]["cortex_plugin"] = {
|
|
243
|
-
"status": "pass",
|
|
244
|
-
"tools_found": tool_names,
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
# Evolution runner: component verification (not full execution — that needs CLI)
|
|
248
|
-
runner_path = NEXO_CODE / "scripts" / "nexo-evolution-run.py"
|
|
249
|
-
if runner_path.exists():
|
|
250
|
-
runner = _load_module("nexo_evolution_run", runner_path)
|
|
251
|
-
checks = {
|
|
252
|
-
"module_loads": True,
|
|
253
|
-
"has_run": hasattr(runner, 'run'),
|
|
254
|
-
"has_load_objective": hasattr(runner, 'load_objective'),
|
|
255
|
-
"has_get_week_data": hasattr(runner, 'get_week_data'),
|
|
256
|
-
}
|
|
257
|
-
summary["checks"]["standalone_runner"] = checks
|
|
258
|
-
else:
|
|
259
|
-
summary["checks"]["standalone_runner"] = {"status": "skip", "reason": "runner not found"}
|
|
260
|
-
|
|
261
|
-
summary["ok"] = True
|
|
262
|
-
_write_summary(summary)
|
|
263
|
-
return 0
|
|
264
|
-
except Exception as exc:
|
|
265
|
-
summary["errors"].append(f"{type(exc).__name__}: {exc}")
|
|
266
|
-
summary["errors"].append(traceback.format_exc())
|
|
267
|
-
_write_summary(summary)
|
|
268
|
-
return 1
|
|
269
|
-
finally:
|
|
270
|
-
shutil.rmtree(temp_root, ignore_errors=True)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if __name__ == "__main__":
|
|
274
|
-
sys.exit(main())
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Quick email sender for NEXO progress updates."""
|
|
3
|
-
import smtplib, sys
|
|
4
|
-
from email.mime.text import MIMEText
|
|
5
|
-
from email.utils import formataddr
|
|
6
|
-
|
|
7
|
-
def send(subject, body, to="user@example.com", cc="user@example.com"):
|
|
8
|
-
msg = MIMEText(body, 'plain', 'utf-8')
|
|
9
|
-
msg['From'] = formataddr(('NEXO', 'nexo@example.com'))
|
|
10
|
-
msg['To'] = to
|
|
11
|
-
msg['Cc'] = cc
|
|
12
|
-
msg['Subject'] = subject
|
|
13
|
-
smtp = smtplib.SMTP_SSL(os.environ.get('NEXO_SMTP_HOST', 'smtp.example.com'), int(os.environ.get('NEXO_SMTP_PORT', '465')))
|
|
14
|
-
smtp.login(FROM_EMAIL, os.environ.get('NEXO_SMTP_PASSWORD', ''))
|
|
15
|
-
recipients = [to]
|
|
16
|
-
if cc:
|
|
17
|
-
recipients.append(cc)
|
|
18
|
-
smtp.send_message(msg)
|
|
19
|
-
smtp.quit()
|
|
20
|
-
print(f"OK — sent to {to}")
|
|
21
|
-
|
|
22
|
-
if __name__ == "__main__":
|
|
23
|
-
subject = sys.argv[1] if len(sys.argv) > 1 else "NEXO Update"
|
|
24
|
-
body = sys.argv[2] if len(sys.argv) > 2 else "No body"
|
|
25
|
-
send(subject, body)
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Quick email sender for NEXO progress updates."""
|
|
3
|
-
import smtplib, sys
|
|
4
|
-
from email.mime.text import MIMEText
|
|
5
|
-
from email.utils import formataddr
|
|
6
|
-
|
|
7
|
-
def send(subject, body, to="user@example.com", cc="user@example.com"):
|
|
8
|
-
msg = MIMEText(body, 'plain', 'utf-8')
|
|
9
|
-
msg['From'] = formataddr(('NEXO', 'nexo@example.com'))
|
|
10
|
-
msg['To'] = to
|
|
11
|
-
msg['Cc'] = cc
|
|
12
|
-
msg['Subject'] = subject
|
|
13
|
-
smtp = smtplib.SMTP_SSL(os.environ.get('NEXO_SMTP_HOST', 'smtp.example.com'), int(os.environ.get('NEXO_SMTP_PORT', '465')))
|
|
14
|
-
smtp.login(FROM_EMAIL, os.environ.get('NEXO_SMTP_PASSWORD', ''))
|
|
15
|
-
recipients = [to]
|
|
16
|
-
if cc:
|
|
17
|
-
recipients.append(cc)
|
|
18
|
-
smtp.send_message(msg)
|
|
19
|
-
smtp.quit()
|
|
20
|
-
print(f"OK — sent to {to}")
|
|
21
|
-
|
|
22
|
-
if __name__ == "__main__":
|
|
23
|
-
subject = sys.argv[1] if len(sys.argv) > 1 else "NEXO Update"
|
|
24
|
-
body = sys.argv[2] if len(sys.argv) > 2 else "No body"
|
|
25
|
-
send(subject, body)
|