nexo-brain 2.0.0 → 2.2.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 +143 -44
- package/bin/nexo-brain.js +53 -26
- package/package.json +15 -3
- package/scripts/migrate-to-unified 2.sh +813 -0
- package/scripts/migrate-v1.5-to-v1.6 2.py +778 -0
- package/scripts/migrate-v1.7-to-v1.8 2.py +214 -0
- package/scripts/pre-commit-check 2.sh +55 -0
- package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
- package/src/__pycache__/hnsw_index.cpython-310.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 2.py +159 -0
- package/src/auto_update 2.py +634 -0
- package/src/claim_graph 2.py +323 -0
- package/src/cognitive/__init__ 2.py +62 -0
- package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
- package/src/cognitive/_core 2.py +567 -0
- package/src/cognitive/_decay 2.py +382 -0
- package/src/cognitive/_ingest 2.py +892 -0
- package/src/cognitive/_memory 2.py +912 -0
- package/src/cognitive/_search 2.py +949 -0
- package/src/cognitive/_trust 2.py +464 -0
- package/src/cognitive/_trust.py +10 -36
- package/src/crons/manifest 2.json +106 -0
- package/src/crons/manifest.json +106 -0
- package/src/crons/sync 2.py +217 -0
- package/src/crons/sync.py +217 -0
- 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 +789 -0
- package/src/dashboard/app.py +16 -2
- package/src/dashboard/templates/dashboard.html +3 -2
- package/src/db/__init__ 2.py +89 -0
- 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 2.py +417 -0
- package/src/db/_credentials 2.py +124 -0
- package/src/db/_entities 2.py +178 -0
- package/src/db/_episodic 2.py +738 -0
- package/src/db/_episodic.py +1 -1
- package/src/db/_evolution 2.py +54 -0
- package/src/db/_fts 2.py +406 -0
- package/src/db/_learnings 2.py +168 -0
- package/src/db/_reminders 2.py +338 -0
- package/src/db/_reminders.py +9 -5
- package/src/db/_schema 2.py +364 -0
- package/src/db/_sessions 2.py +300 -0
- package/src/db/_tasks 2.py +91 -0
- package/src/evolution_cycle 2.py +266 -0
- package/src/hnsw_index 2.py +254 -0
- package/src/hooks/auto_capture 2.py +208 -0
- package/src/hooks/caffeinate-guard 2.sh +8 -0
- package/src/hooks/capture-session 2.sh +21 -0
- package/src/hooks/capture-session.sh +2 -0
- package/src/hooks/capture-tool-logs 2.sh +127 -0
- package/src/hooks/capture-tool-logs.sh +3 -2
- package/src/hooks/daily-briefing-check 2.sh +33 -0
- package/src/hooks/inbox-hook 2.sh +76 -0
- package/src/hooks/inbox-hook.sh +3 -2
- package/src/hooks/post-compact 2.sh +148 -0
- package/src/hooks/post-compact.sh +1 -1
- package/src/hooks/pre-compact 2.sh +151 -0
- package/src/hooks/pre-compact.sh +1 -1
- package/src/hooks/session-start 2.sh +268 -0
- package/src/hooks/session-start.sh +6 -3
- package/src/hooks/session-stop 2.sh +140 -0
- package/src/hooks/session-stop.sh +3 -2
- package/src/kg_populate 2.py +290 -0
- package/src/knowledge_graph 2.py +257 -0
- package/src/maintenance 2.py +59 -0
- package/src/migrate_embeddings 2.py +122 -0
- package/src/plugin_loader 2.py +202 -0
- 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__/adaptive_mode 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-310.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__/update 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
- package/src/plugins/adaptive_mode 2.py +805 -0
- package/src/plugins/agents 2.py +52 -0
- package/src/plugins/artifact_registry 2.py +450 -0
- package/src/plugins/backup 2.py +104 -0
- package/src/plugins/cognitive_memory 2.py +564 -0
- package/src/plugins/core_rules 2.py +252 -0
- package/src/plugins/core_rules.py +34 -17
- package/src/plugins/cortex 2.py +299 -0
- package/src/plugins/entities 2.py +67 -0
- package/src/plugins/episodic_memory 2.py +533 -0
- package/src/plugins/evolution 2.py +115 -0
- package/src/plugins/guard 2.py +746 -0
- package/src/plugins/knowledge_graph_tools 2.py +105 -0
- package/src/plugins/preferences 2.py +47 -0
- package/src/plugins/update 2.py +256 -0
- package/src/plugins/update.py +18 -0
- package/src/requirements 2.txt +12 -0
- package/src/rules/__init__ 2.py +0 -0
- package/src/rules/core-rules 2.json +331 -0
- package/src/rules/migrate 2.py +207 -0
- package/src/scripts/check-context 2.py +264 -0
- package/src/scripts/check-context.py +4 -7
- package/src/scripts/deep-sleep/apply_findings.py +570 -167
- 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 +197 -0
- package/src/scripts/deep-sleep/synthesize.py +191 -0
- package/src/scripts/nexo-auto-update 2.py +6 -0
- package/src/scripts/nexo-backup 2.sh +25 -0
- package/src/scripts/nexo-brain-activation 2.sh +140 -0
- package/src/scripts/nexo-catchup 2.py +242 -0
- package/src/scripts/nexo-catchup.py +5 -8
- package/src/scripts/nexo-cognitive-decay 2.py +182 -0
- package/src/scripts/nexo-daily-self-audit 2.py +552 -0
- package/src/scripts/nexo-daily-self-audit.py +28 -19
- package/src/scripts/nexo-deep-sleep 2.sh +97 -0
- package/src/scripts/nexo-deep-sleep.sh +31 -16
- package/src/scripts/nexo-evolution-run 2.py +597 -0
- package/src/scripts/nexo-evolution-run.py +5 -20
- package/src/scripts/nexo-followup-hygiene 2.py +112 -0
- package/src/scripts/nexo-followup-hygiene.py +4 -2
- package/src/scripts/nexo-github-monitor 2.py +256 -0
- package/src/scripts/nexo-github-monitor.py +6 -9
- package/src/scripts/nexo-immune 2.py +927 -0
- package/src/scripts/nexo-immune.py +4 -17
- package/src/scripts/nexo-inbox-hook 2.sh +74 -0
- package/src/scripts/nexo-install 2.py +6 -0
- package/src/scripts/nexo-learning-housekeep 2.py +245 -0
- package/src/scripts/nexo-learning-validator 2.py +207 -0
- package/src/scripts/nexo-learning-validator.py +0 -29
- package/src/scripts/nexo-migrate 2.py +232 -0
- package/src/scripts/nexo-postmortem-consolidator 2.py +421 -0
- package/src/scripts/nexo-postmortem-consolidator.py +9 -20
- package/src/scripts/nexo-pre-commit 2.py +120 -0
- package/src/scripts/nexo-prevent-sleep 2.sh +29 -0
- package/src/scripts/nexo-proactive-dashboard 2.py +345 -0
- package/src/scripts/nexo-proactive-dashboard.py +1 -0
- package/src/scripts/nexo-reflection 2.py +253 -0
- package/src/scripts/nexo-runtime-preflight 2.py +274 -0
- package/src/scripts/nexo-send-email 2.py +25 -0
- package/src/scripts/nexo-send-reply 2.py +178 -0
- package/src/scripts/nexo-sleep 2.py +592 -0
- package/src/scripts/nexo-sleep.py +8 -18
- package/src/scripts/nexo-snapshot-restore 2.sh +35 -0
- package/src/scripts/nexo-synthesis 2.py +253 -0
- package/src/scripts/nexo-synthesis.py +8 -19
- package/src/scripts/nexo-tcc-approve 2.sh +79 -0
- package/src/scripts/nexo-update 2.sh +161 -0
- package/src/scripts/nexo-watchdog 2.sh +878 -0
- package/src/scripts/nexo-watchdog-smoke 2.py +119 -0
- package/src/server 2.py +733 -0
- package/src/server.py +6 -1
- package/src/storage_router 2.py +32 -0
- package/src/tools_coordination 2.py +102 -0
- package/src/tools_credentials 2.py +68 -0
- package/src/tools_learnings 2.py +220 -0
- package/src/tools_menu 2.py +227 -0
- package/src/tools_menu.py +1 -1
- package/src/tools_reminders 2.py +86 -0
- package/src/tools_reminders_crud 2.py +159 -0
- package/src/tools_reminders_crud.py +7 -0
- package/src/tools_sessions 2.py +476 -0
- package/src/tools_sessions.py +67 -0
- package/src/tools_task_history 2.py +57 -0
- package/templates/CLAUDE.md 2.template +63 -0
- package/templates/openclaw 2.json +13 -0
- package/tests/__init__ 2.py +0 -0
- package/tests/conftest 2.py +71 -0
- package/tests/test_cognitive 2.py +205 -0
- package/tests/test_knowledge_graph 2.py +140 -0
- package/tests/test_migrations 2.py +137 -0
- package/src/__pycache__/auto_close_sessions.cpython-310.pyc +0 -0
- package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/auto_update.cpython-314.pyc +0 -0
- package/src/__pycache__/claim_graph.cpython-310.pyc +0 -0
- package/src/__pycache__/claim_graph.cpython-314.pyc +0 -0
- package/src/__pycache__/evolution_cycle.cpython-310.pyc +0 -0
- package/src/__pycache__/evolution_cycle.cpython-314.pyc +0 -0
- package/src/__pycache__/hnsw_index.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-310.pyc +0 -0
- package/src/__pycache__/maintenance.cpython-314.pyc +0 -0
- package/src/__pycache__/migrate_embeddings.cpython-310.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-310.pyc +0 -0
- package/src/__pycache__/server.cpython-314.pyc +0 -0
- package/src/__pycache__/storage_router.cpython-310.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/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.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-310.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__/adaptive_mode.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-310.pyc +0 -0
- package/src/rules/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/rules/__pycache__/migrate.cpython-310.pyc +0 -0
- package/src/rules/__pycache__/migrate.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/check-context.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/check-context.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-auto-update.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-catchup.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-evolution-run.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-github-monitor.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-github-monitor.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-immune.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-install.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-install.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-validator.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-migrate.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-migrate.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-pre-commit.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-reflection.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-email.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-reply.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-sleep.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-synthesis.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/analyze_session.cpython-310.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-310.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-310.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-310.pyc +0 -0
- package/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tests/__pycache__/conftest.cpython-310-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/conftest.cpython-310.pyc +0 -0
- package/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_cognitive.cpython-310-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_cognitive.cpython-310.pyc +0 -0
- package/tests/__pycache__/test_cognitive.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_knowledge_graph.cpython-310-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_knowledge_graph.cpython-310.pyc +0 -0
- package/tests/__pycache__/test_knowledge_graph.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_migrations.cpython-310-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_migrations.cpython-310.pyc +0 -0
- package/tests/__pycache__/test_migrations.cpython-314-pytest-9.0.2.pyc +0 -0
|
@@ -1,219 +1,622 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Deep Sleep
|
|
4
|
-
|
|
3
|
+
Deep Sleep v2 -- Phase 4: Apply synthesized findings.
|
|
4
|
+
|
|
5
|
+
Reads $DATE-synthesis.json and executes actions:
|
|
6
|
+
- learning_add: inserts learnings into nexo.db
|
|
7
|
+
- followup_create: inserts followups into nexo.db
|
|
8
|
+
- morning_briefing_item: writes to morning briefing file
|
|
9
|
+
|
|
10
|
+
All actions are idempotent (dedupe_key checked against last 7 days),
|
|
11
|
+
backed up before mutation, and logged to $DATE-applied.json.
|
|
12
|
+
|
|
13
|
+
Environment variables:
|
|
14
|
+
NEXO_HOME -- root of the NEXO installation (default: ~/.nexo)
|
|
5
15
|
"""
|
|
16
|
+
import hashlib
|
|
6
17
|
import json
|
|
7
18
|
import os
|
|
8
19
|
import sqlite3
|
|
9
20
|
import sys
|
|
10
|
-
from datetime import datetime
|
|
21
|
+
from datetime import datetime, timedelta
|
|
11
22
|
from pathlib import Path
|
|
12
23
|
|
|
13
24
|
NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
|
|
14
|
-
|
|
15
25
|
DEEP_SLEEP_DIR = NEXO_HOME / "operations" / "deep-sleep"
|
|
16
26
|
NEXO_DB = NEXO_HOME / "data" / "nexo.db"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if not index_file.exists() or not new_entries:
|
|
54
|
-
return
|
|
55
|
-
|
|
56
|
-
current = index_file.read_text()
|
|
57
|
-
lines_to_add = []
|
|
58
|
-
for entry in new_entries:
|
|
59
|
-
line = f"- **{entry['title']}:** `{entry['filename']}` --- {entry['summary']}"
|
|
60
|
-
if line not in current:
|
|
61
|
-
lines_to_add.append(line)
|
|
62
|
-
|
|
63
|
-
if lines_to_add:
|
|
64
|
-
current += "\n" + "\n".join(lines_to_add) + "\n"
|
|
65
|
-
index_file.write_text(current)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def adjust_trust(points: int, context: str):
|
|
69
|
-
"""Record trust adjustment in cognitive.db if available."""
|
|
70
|
-
cog_db = NEXO_HOME / "data" / "cognitive.db"
|
|
71
|
-
if not cog_db.exists():
|
|
72
|
-
return
|
|
27
|
+
COGNITIVE_DB = NEXO_HOME / "data" / "cognitive.db"
|
|
28
|
+
OPERATIONS_DIR = NEXO_HOME / "operations"
|
|
29
|
+
BACKUP_DIR = DEEP_SLEEP_DIR # backups stored alongside outputs
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def generate_run_id(target_date: str) -> str:
|
|
33
|
+
"""Generate a unique run ID for this execution."""
|
|
34
|
+
ts = datetime.now().strftime("%H%M%S")
|
|
35
|
+
return f"{target_date}-{ts}"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_recent_dedupe_keys(target_date: str, days: int = 7) -> set[str]:
|
|
39
|
+
"""Load dedupe_keys from applied files in the last N days."""
|
|
40
|
+
keys = set()
|
|
41
|
+
base_date = datetime.strptime(target_date, "%Y-%m-%d")
|
|
42
|
+
for i in range(days):
|
|
43
|
+
d = (base_date - timedelta(days=i)).strftime("%Y-%m-%d")
|
|
44
|
+
applied_file = DEEP_SLEEP_DIR / f"{d}-applied.json"
|
|
45
|
+
if applied_file.exists():
|
|
46
|
+
try:
|
|
47
|
+
with open(applied_file) as f:
|
|
48
|
+
data = json.load(f)
|
|
49
|
+
for action in data.get("applied_actions", []):
|
|
50
|
+
dk = action.get("dedupe_key", "")
|
|
51
|
+
if dk:
|
|
52
|
+
keys.add(dk)
|
|
53
|
+
except (json.JSONDecodeError, KeyError):
|
|
54
|
+
continue
|
|
55
|
+
return keys
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def backup_db(db_path: Path, run_id: str) -> Path | None:
|
|
59
|
+
"""Create a backup of a database before mutations."""
|
|
60
|
+
if not db_path.exists():
|
|
61
|
+
return None
|
|
62
|
+
backup_path = BACKUP_DIR / f"{run_id}-backup-{db_path.name}"
|
|
73
63
|
try:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
conn.close()
|
|
81
|
-
except Exception:
|
|
82
|
-
pass
|
|
64
|
+
import shutil
|
|
65
|
+
shutil.copy2(str(db_path), str(backup_path))
|
|
66
|
+
return backup_path
|
|
67
|
+
except Exception as e:
|
|
68
|
+
print(f" [apply] Warning: backup failed for {db_path.name}: {e}", file=sys.stderr)
|
|
69
|
+
return None
|
|
83
70
|
|
|
84
71
|
|
|
85
|
-
def add_learning(category: str, title: str, content: str) ->
|
|
86
|
-
"""Add a learning to nexo.db
|
|
72
|
+
def add_learning(category: str, title: str, content: str) -> dict:
|
|
73
|
+
"""Add a learning to nexo.db. Returns result dict."""
|
|
87
74
|
if not NEXO_DB.exists():
|
|
88
|
-
return False
|
|
75
|
+
return {"success": False, "error": "nexo.db not found"}
|
|
89
76
|
try:
|
|
90
77
|
now = datetime.now().timestamp()
|
|
91
78
|
conn = sqlite3.connect(str(NEXO_DB))
|
|
92
|
-
conn.execute(
|
|
79
|
+
cursor = conn.execute(
|
|
93
80
|
"INSERT INTO learnings (category, title, content, created_at, updated_at, reasoning) VALUES (?, ?, ?, ?, ?, ?)",
|
|
94
|
-
(category, title, content, now, now, "Deep Sleep overnight analysis")
|
|
81
|
+
(category, title, content, now, now, "Deep Sleep v2 overnight analysis")
|
|
95
82
|
)
|
|
83
|
+
learning_id = cursor.lastrowid
|
|
96
84
|
conn.commit()
|
|
97
85
|
conn.close()
|
|
98
|
-
return True
|
|
86
|
+
return {"success": True, "id": learning_id}
|
|
99
87
|
except Exception as e:
|
|
100
|
-
|
|
101
|
-
return False
|
|
88
|
+
return {"success": False, "error": str(e)}
|
|
102
89
|
|
|
103
90
|
|
|
104
|
-
def
|
|
105
|
-
"""
|
|
91
|
+
def create_followup(description: str, date: str = "") -> dict:
|
|
92
|
+
"""Create a followup in nexo.db. Returns result dict."""
|
|
106
93
|
if not NEXO_DB.exists():
|
|
107
|
-
return False
|
|
94
|
+
return {"success": False, "error": "nexo.db not found"}
|
|
108
95
|
try:
|
|
109
96
|
now = datetime.now().timestamp()
|
|
97
|
+
# Generate a deterministic ID
|
|
98
|
+
fid = "NF-DS-" + hashlib.md5(description.encode()).hexdigest()[:8].upper()
|
|
110
99
|
conn = sqlite3.connect(str(NEXO_DB))
|
|
111
100
|
conn.execute(
|
|
112
101
|
"INSERT OR IGNORE INTO followups (id, description, date, status, created_at, updated_at, reasoning) VALUES (?, ?, ?, 'PENDING', ?, ?, ?)",
|
|
113
|
-
(
|
|
102
|
+
(fid, description, date, now, now, "Deep Sleep v2 overnight analysis")
|
|
114
103
|
)
|
|
115
104
|
conn.commit()
|
|
116
105
|
conn.close()
|
|
117
|
-
return True
|
|
106
|
+
return {"success": True, "id": fid}
|
|
118
107
|
except Exception as e:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
"
|
|
157
|
-
"
|
|
158
|
-
"summary": f"Deep sleep {date}, severity {severity}"
|
|
108
|
+
return {"success": False, "error": str(e)}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def update_calibration_mood(synthesis: dict) -> dict:
|
|
112
|
+
"""Update mood in calibration.json based on emotional analysis."""
|
|
113
|
+
calibration_file = NEXO_HOME / "brain" / "calibration.json"
|
|
114
|
+
if not calibration_file.exists():
|
|
115
|
+
return {"success": False, "error": "calibration.json not found"}
|
|
116
|
+
|
|
117
|
+
emotional_day = synthesis.get("emotional_day", {})
|
|
118
|
+
if not emotional_day:
|
|
119
|
+
return {"success": False, "error": "no emotional_day data"}
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
cal = json.loads(calibration_file.read_text())
|
|
123
|
+
|
|
124
|
+
# Add/update mood history
|
|
125
|
+
if "mood_history" not in cal:
|
|
126
|
+
cal["mood_history"] = []
|
|
127
|
+
|
|
128
|
+
cal["mood_history"].append({
|
|
129
|
+
"date": synthesis.get("date", ""),
|
|
130
|
+
"score": emotional_day.get("mood_score", 0.5),
|
|
131
|
+
"arc": emotional_day.get("mood_arc", ""),
|
|
132
|
+
"triggers": emotional_day.get("recurring_triggers", {}),
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
# Keep last 30 days
|
|
136
|
+
cal["mood_history"] = cal["mood_history"][-30:]
|
|
137
|
+
|
|
138
|
+
# Apply calibration recommendation if any
|
|
139
|
+
rec = emotional_day.get("calibration_recommendation")
|
|
140
|
+
if rec and rec != "null":
|
|
141
|
+
if "calibration_notes" not in cal:
|
|
142
|
+
cal["calibration_notes"] = []
|
|
143
|
+
cal["calibration_notes"].append({
|
|
144
|
+
"date": synthesis.get("date", ""),
|
|
145
|
+
"recommendation": rec,
|
|
146
|
+
"applied": False,
|
|
159
147
|
})
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
"
|
|
187
|
-
"
|
|
148
|
+
# Keep last 10
|
|
149
|
+
cal["calibration_notes"] = cal["calibration_notes"][-10:]
|
|
150
|
+
|
|
151
|
+
calibration_file.write_text(json.dumps(cal, indent=2, ensure_ascii=False))
|
|
152
|
+
return {"success": True, "mood_score": emotional_day.get("mood_score")}
|
|
153
|
+
except Exception as e:
|
|
154
|
+
return {"success": False, "error": str(e)}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def calibrate_trust_score(synthesis: dict, target_date: str) -> dict:
|
|
158
|
+
"""Set the daily trust score from Deep Sleep analysis.
|
|
159
|
+
|
|
160
|
+
This is the authoritative score for the day — replaces incremental
|
|
161
|
+
adjustments with a holistic evaluation of the entire day.
|
|
162
|
+
"""
|
|
163
|
+
trust_cal = synthesis.get("trust_calibration")
|
|
164
|
+
if not trust_cal or "score" not in trust_cal:
|
|
165
|
+
return {"success": False, "error": "no trust_calibration in synthesis"}
|
|
166
|
+
|
|
167
|
+
score = max(0, min(100, trust_cal["score"]))
|
|
168
|
+
reasoning = trust_cal.get("reasoning", "Deep Sleep calibration")
|
|
169
|
+
trend = trust_cal.get("trend", "stable")
|
|
170
|
+
highlights = trust_cal.get("highlights", [])
|
|
171
|
+
lowlights = trust_cal.get("lowlights", [])
|
|
172
|
+
|
|
173
|
+
context = (
|
|
174
|
+
f"Deep Sleep {target_date} | trend: {trend} | "
|
|
175
|
+
f"highlights: {', '.join(highlights[:3])} | "
|
|
176
|
+
f"lowlights: {', '.join(lowlights[:3])}"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
# Get current score for delta calculation
|
|
181
|
+
db = sqlite3.connect(str(COGNITIVE_DB))
|
|
182
|
+
row = db.execute(
|
|
183
|
+
"SELECT score FROM trust_score ORDER BY id DESC LIMIT 1"
|
|
184
|
+
).fetchone()
|
|
185
|
+
old_score = row[0] if row else 50.0
|
|
186
|
+
delta = score - old_score
|
|
187
|
+
|
|
188
|
+
db.execute(
|
|
189
|
+
"INSERT INTO trust_score (score, event, delta, context) VALUES (?, ?, ?, ?)",
|
|
190
|
+
(score, f"deep_sleep_calibration: {reasoning[:200]}", delta, context[:500])
|
|
191
|
+
)
|
|
192
|
+
db.commit()
|
|
193
|
+
db.close()
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
"success": True,
|
|
197
|
+
"old_score": old_score,
|
|
198
|
+
"new_score": score,
|
|
199
|
+
"delta": delta,
|
|
200
|
+
"trend": trend,
|
|
201
|
+
}
|
|
202
|
+
except Exception as e:
|
|
203
|
+
return {"success": False, "error": str(e)}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def create_abandoned_followups(synthesis: dict) -> list[dict]:
|
|
207
|
+
"""Create followups for truly abandoned projects."""
|
|
208
|
+
results = []
|
|
209
|
+
abandoned = synthesis.get("abandoned_projects", [])
|
|
210
|
+
for proj in abandoned:
|
|
211
|
+
if proj.get("has_followup"):
|
|
212
|
+
continue
|
|
213
|
+
rec = proj.get("recommendation", "")
|
|
214
|
+
if "ignore" in rec.lower():
|
|
215
|
+
continue
|
|
216
|
+
result = create_followup(
|
|
217
|
+
description=f"[Abandoned] {proj.get('description', '')}",
|
|
218
|
+
date="" # No date — it's a discovered gap
|
|
219
|
+
)
|
|
220
|
+
results.append(result)
|
|
221
|
+
return results
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def generate_session_tone(synthesis: dict, target_date: str) -> dict:
|
|
225
|
+
"""Generate emotional tone guidance for next session startup.
|
|
226
|
+
|
|
227
|
+
This is the 'psychology' layer — tells NEXO how to behave emotionally
|
|
228
|
+
based on yesterday's analysis. Read by startup hook to adapt greeting.
|
|
229
|
+
"""
|
|
230
|
+
emotional = synthesis.get("emotional_day", {})
|
|
231
|
+
productivity = synthesis.get("productivity_day", {})
|
|
232
|
+
patterns = synthesis.get("cross_session_patterns", [])
|
|
233
|
+
abandoned = synthesis.get("abandoned_projects", [])
|
|
234
|
+
mood_score = emotional.get("mood_score", 0.5)
|
|
235
|
+
corrections = productivity.get("total_corrections", 0)
|
|
236
|
+
proactivity = productivity.get("overall_proactivity", "mixed")
|
|
237
|
+
|
|
238
|
+
tone = {
|
|
239
|
+
"date": target_date,
|
|
240
|
+
"mood_yesterday": mood_score,
|
|
241
|
+
"approach": "neutral",
|
|
242
|
+
"opening_style": "normal",
|
|
243
|
+
"acknowledge_mistakes": False,
|
|
244
|
+
"mistakes_to_own": [],
|
|
245
|
+
"motivational": False,
|
|
246
|
+
"reduce_load": False,
|
|
247
|
+
"suggested_greeting_context": "",
|
|
188
248
|
}
|
|
189
249
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
250
|
+
# Agent made many mistakes yesterday → own it, apologize, show learning
|
|
251
|
+
if corrections > 5:
|
|
252
|
+
tone["acknowledge_mistakes"] = True
|
|
253
|
+
tone["opening_style"] = "humble"
|
|
254
|
+
# Collect what went wrong
|
|
255
|
+
high_patterns = [p["pattern"] for p in patterns if p.get("severity") == "high"]
|
|
256
|
+
tone["mistakes_to_own"] = high_patterns[:3]
|
|
257
|
+
tone["suggested_greeting_context"] = (
|
|
258
|
+
f"Yesterday the agent needed {corrections} corrections. "
|
|
259
|
+
f"Acknowledge specific mistakes, show what was learned, "
|
|
260
|
+
f"and demonstrate improvement from the first interaction."
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# User had a bad day → supportive, less pressure
|
|
264
|
+
if mood_score < 0.4:
|
|
265
|
+
tone["approach"] = "supportive"
|
|
266
|
+
tone["motivational"] = True
|
|
267
|
+
tone["reduce_load"] = True
|
|
268
|
+
frustration_triggers = emotional.get("recurring_triggers", {}).get("frustration", [])
|
|
269
|
+
tone["suggested_greeting_context"] += (
|
|
270
|
+
f" User had a tough day (mood {mood_score:.0%}). "
|
|
271
|
+
f"Be supportive, acknowledge the difficulty, and propose a lighter start. "
|
|
272
|
+
f"Avoid these frustration triggers: {', '.join(frustration_triggers[:3])}."
|
|
273
|
+
)
|
|
193
274
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
275
|
+
# User had a great day → reinforce, push momentum
|
|
276
|
+
elif mood_score > 0.7:
|
|
277
|
+
tone["approach"] = "energetic"
|
|
278
|
+
tone["motivational"] = True
|
|
279
|
+
flow_triggers = emotional.get("recurring_triggers", {}).get("flow", [])
|
|
280
|
+
tone["suggested_greeting_context"] += (
|
|
281
|
+
f" User had a great day (mood {mood_score:.0%}). "
|
|
282
|
+
f"Reinforce the momentum. Reference yesterday's wins. "
|
|
283
|
+
f"Propose ambitious next steps. Flow triggers: {', '.join(flow_triggers[:3])}."
|
|
284
|
+
)
|
|
197
285
|
|
|
198
|
-
|
|
286
|
+
# Agent was too reactive → be proactive today
|
|
287
|
+
if proactivity == "reactive":
|
|
288
|
+
tone["approach"] = "proactive"
|
|
289
|
+
tone["suggested_greeting_context"] += (
|
|
290
|
+
" Agent was too reactive yesterday — today lead with proposals, "
|
|
291
|
+
"don't wait for instructions."
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# There are abandoned projects → gently bring up
|
|
295
|
+
if abandoned:
|
|
296
|
+
truly_abandoned = [a for a in abandoned if not a.get("has_followup")]
|
|
297
|
+
if truly_abandoned:
|
|
298
|
+
tone["suggested_greeting_context"] += (
|
|
299
|
+
f" {len(truly_abandoned)} project(s) were started but not finished. "
|
|
300
|
+
f"Offer to pick them up today without pressure."
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
return tone
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def write_morning_briefing(target_date: str, synthesis: dict) -> Path:
|
|
307
|
+
"""Write the morning briefing file from synthesis data."""
|
|
308
|
+
briefing_dir = OPERATIONS_DIR
|
|
309
|
+
briefing_dir.mkdir(parents=True, exist_ok=True)
|
|
310
|
+
briefing_file = briefing_dir / "morning-briefing.md"
|
|
311
|
+
|
|
312
|
+
# Generate session tone for startup
|
|
313
|
+
tone = generate_session_tone(synthesis, target_date)
|
|
314
|
+
tone_file = briefing_dir / "session-tone.json"
|
|
315
|
+
tone_file.write_text(json.dumps(tone, indent=2, ensure_ascii=False))
|
|
316
|
+
|
|
317
|
+
lines = [
|
|
318
|
+
f"# Morning Briefing -- {target_date}",
|
|
319
|
+
f"_Generated by Deep Sleep at {datetime.now().strftime('%H:%M')}_",
|
|
320
|
+
""
|
|
321
|
+
]
|
|
322
|
+
|
|
323
|
+
# Summary
|
|
324
|
+
summary = synthesis.get("summary", "")
|
|
325
|
+
if summary:
|
|
326
|
+
lines.append(f"> {summary}")
|
|
327
|
+
lines.append("")
|
|
328
|
+
|
|
329
|
+
# Morning agenda
|
|
330
|
+
agenda = synthesis.get("morning_agenda", [])
|
|
331
|
+
if agenda:
|
|
332
|
+
lines.append("## Agenda")
|
|
333
|
+
lines.append("")
|
|
334
|
+
for item in agenda:
|
|
335
|
+
priority = item.get("priority", "?")
|
|
336
|
+
title = item.get("title", "")
|
|
337
|
+
desc = item.get("description", "")
|
|
338
|
+
item_type = item.get("type", "")
|
|
339
|
+
lines.append(f"### {priority}. {title}")
|
|
340
|
+
if item_type:
|
|
341
|
+
lines.append(f"_Type: {item_type}_")
|
|
342
|
+
lines.append(desc)
|
|
343
|
+
if item.get("context"):
|
|
344
|
+
lines.append(f"\n> {item['context']}")
|
|
345
|
+
lines.append("")
|
|
346
|
+
|
|
347
|
+
# Emotional day
|
|
348
|
+
emotional = synthesis.get("emotional_day", {})
|
|
349
|
+
if emotional:
|
|
350
|
+
mood_score = emotional.get("mood_score", 0.5)
|
|
351
|
+
mood_bar = "🟢" if mood_score >= 0.7 else "🟡" if mood_score >= 0.4 else "🔴"
|
|
352
|
+
lines.append(f"## Mood {mood_bar} {mood_score:.0%}")
|
|
353
|
+
lines.append("")
|
|
354
|
+
if emotional.get("mood_arc"):
|
|
355
|
+
lines.append(emotional["mood_arc"])
|
|
356
|
+
triggers = emotional.get("recurring_triggers", {})
|
|
357
|
+
if triggers.get("frustration"):
|
|
358
|
+
lines.append(f"**Frustration triggers:** {', '.join(triggers['frustration'])}")
|
|
359
|
+
if triggers.get("flow"):
|
|
360
|
+
lines.append(f"**Flow triggers:** {', '.join(triggers['flow'])}")
|
|
361
|
+
if emotional.get("calibration_recommendation"):
|
|
362
|
+
lines.append(f"\n💡 **Recommendation:** {emotional['calibration_recommendation']}")
|
|
363
|
+
lines.append("")
|
|
364
|
+
|
|
365
|
+
# Productivity
|
|
366
|
+
productivity = synthesis.get("productivity_day", {})
|
|
367
|
+
if productivity:
|
|
368
|
+
lines.append("## Productivity")
|
|
369
|
+
lines.append("")
|
|
370
|
+
lines.append(f"- Corrections needed: {productivity.get('total_corrections', '?')}")
|
|
371
|
+
lines.append(f"- Proactivity: {productivity.get('overall_proactivity', '?')}")
|
|
372
|
+
if productivity.get("tool_insights"):
|
|
373
|
+
lines.append(f"- Tools: {productivity['tool_insights']}")
|
|
374
|
+
inefficiencies = productivity.get("systemic_inefficiencies", [])
|
|
375
|
+
if inefficiencies:
|
|
376
|
+
lines.append(f"- Issues: {', '.join(inefficiencies)}")
|
|
377
|
+
lines.append("")
|
|
378
|
+
|
|
379
|
+
# Abandoned projects
|
|
380
|
+
abandoned = synthesis.get("abandoned_projects", [])
|
|
381
|
+
if abandoned:
|
|
382
|
+
truly_abandoned = [a for a in abandoned if not a.get("has_followup")]
|
|
383
|
+
if truly_abandoned:
|
|
384
|
+
lines.append("## Abandoned Projects")
|
|
385
|
+
lines.append("")
|
|
386
|
+
for a in truly_abandoned:
|
|
387
|
+
lines.append(f"- {a.get('description', '?')}")
|
|
388
|
+
if a.get("recommendation"):
|
|
389
|
+
lines.append(f" → {a['recommendation']}")
|
|
390
|
+
lines.append("")
|
|
391
|
+
|
|
392
|
+
# Cross-session patterns
|
|
393
|
+
patterns = synthesis.get("cross_session_patterns", [])
|
|
394
|
+
if patterns:
|
|
395
|
+
lines.append("## Patterns Detected")
|
|
396
|
+
lines.append("")
|
|
397
|
+
for p in patterns:
|
|
398
|
+
severity = p.get("severity", "")
|
|
399
|
+
lines.append(f"- **[{severity}]** {p.get('pattern', '')}")
|
|
400
|
+
sessions = p.get("sessions", [])
|
|
401
|
+
if sessions:
|
|
402
|
+
lines.append(f" Sessions: {', '.join(sessions)}")
|
|
403
|
+
lines.append("")
|
|
404
|
+
|
|
405
|
+
# Draft actions (things that need user decision)
|
|
406
|
+
draft_actions = [
|
|
407
|
+
a for a in synthesis.get("actions", [])
|
|
408
|
+
if a.get("action_class") == "draft_for_morning"
|
|
409
|
+
]
|
|
410
|
+
if draft_actions:
|
|
411
|
+
lines.append("## Items for Review")
|
|
412
|
+
lines.append("")
|
|
413
|
+
for a in draft_actions:
|
|
414
|
+
confidence = a.get("confidence", 0)
|
|
415
|
+
lines.append(f"- **{a.get('action_type', '')}** (confidence: {confidence:.0%})")
|
|
416
|
+
content = a.get("content", {})
|
|
417
|
+
if isinstance(content, dict):
|
|
418
|
+
title = content.get("title", content.get("description", ""))
|
|
419
|
+
lines.append(f" {title}")
|
|
420
|
+
evidence = a.get("evidence", [])
|
|
421
|
+
if evidence and isinstance(evidence, list):
|
|
422
|
+
for ev in evidence[:2]:
|
|
423
|
+
quote = ev.get("quote", "")
|
|
424
|
+
if quote:
|
|
425
|
+
lines.append(f' > "{quote}"')
|
|
426
|
+
lines.append("")
|
|
427
|
+
|
|
428
|
+
# Context packets
|
|
429
|
+
packets = synthesis.get("context_packets", [])
|
|
430
|
+
if packets:
|
|
431
|
+
lines.append("## Context for Today's Work")
|
|
432
|
+
lines.append("")
|
|
433
|
+
for p in packets:
|
|
434
|
+
lines.append(f"### {p.get('topic', 'Unknown')}")
|
|
435
|
+
lines.append(f"**Last state:** {p.get('last_state', 'N/A')}")
|
|
436
|
+
files = p.get("key_files", [])
|
|
437
|
+
if files:
|
|
438
|
+
lines.append(f"**Files:** {', '.join(files)}")
|
|
439
|
+
questions = p.get("open_questions", [])
|
|
440
|
+
if questions:
|
|
441
|
+
lines.append("**Open questions:**")
|
|
442
|
+
for q in questions:
|
|
443
|
+
lines.append(f" - {q}")
|
|
444
|
+
lines.append("")
|
|
445
|
+
|
|
446
|
+
briefing_file.write_text("\n".join(lines), encoding="utf-8")
|
|
447
|
+
return briefing_file
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def apply_action(action: dict, run_id: str) -> dict:
|
|
451
|
+
"""Apply a single action and return the result log."""
|
|
452
|
+
action_type = action.get("action_type", "")
|
|
453
|
+
action_class = action.get("action_class", "")
|
|
454
|
+
content = action.get("content", {})
|
|
455
|
+
dedupe_key = action.get("dedupe_key", "")
|
|
456
|
+
|
|
457
|
+
applied_id = f"{run_id}-{hashlib.md5(dedupe_key.encode()).hexdigest()[:8]}"
|
|
458
|
+
|
|
459
|
+
log_entry = {
|
|
460
|
+
"applied_action_id": applied_id,
|
|
461
|
+
"action_type": action_type,
|
|
462
|
+
"action_class": action_class,
|
|
463
|
+
"dedupe_key": dedupe_key,
|
|
464
|
+
"timestamp": datetime.now().isoformat(),
|
|
465
|
+
"status": "skipped",
|
|
466
|
+
"details": {}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
# Only auto_apply actions get executed
|
|
470
|
+
if action_class != "auto_apply":
|
|
471
|
+
log_entry["status"] = "deferred_to_morning"
|
|
472
|
+
log_entry["details"] = {"reason": "action_class is not auto_apply"}
|
|
473
|
+
return log_entry
|
|
474
|
+
|
|
475
|
+
if not isinstance(content, dict):
|
|
476
|
+
log_entry["status"] = "error"
|
|
477
|
+
log_entry["details"] = {"error": "content is not a dict"}
|
|
478
|
+
return log_entry
|
|
479
|
+
|
|
480
|
+
if action_type == "learning_add":
|
|
481
|
+
result = add_learning(
|
|
482
|
+
category=content.get("category", "process"),
|
|
483
|
+
title=content.get("title", "Deep Sleep finding"),
|
|
484
|
+
content=content.get("content", content.get("description", ""))
|
|
485
|
+
)
|
|
486
|
+
log_entry["status"] = "applied" if result.get("success") else "error"
|
|
487
|
+
log_entry["details"] = result
|
|
488
|
+
|
|
489
|
+
elif action_type == "followup_create":
|
|
490
|
+
result = create_followup(
|
|
491
|
+
description=content.get("description", content.get("title", "")),
|
|
492
|
+
date=content.get("date", "")
|
|
493
|
+
)
|
|
494
|
+
log_entry["status"] = "applied" if result.get("success") else "error"
|
|
495
|
+
log_entry["details"] = result
|
|
496
|
+
|
|
497
|
+
elif action_type == "morning_briefing_item":
|
|
498
|
+
# These are included in the briefing file, not applied separately
|
|
499
|
+
log_entry["status"] = "included_in_briefing"
|
|
500
|
+
|
|
501
|
+
else:
|
|
502
|
+
log_entry["status"] = "unknown_type"
|
|
503
|
+
log_entry["details"] = {"error": f"Unknown action_type: {action_type}"}
|
|
504
|
+
|
|
505
|
+
return log_entry
|
|
199
506
|
|
|
200
507
|
|
|
201
508
|
def main():
|
|
202
|
-
|
|
509
|
+
target_date = sys.argv[1] if len(sys.argv) > 1 else datetime.now().strftime("%Y-%m-%d")
|
|
203
510
|
|
|
204
|
-
|
|
205
|
-
if not
|
|
206
|
-
print(f"No
|
|
511
|
+
synthesis_file = DEEP_SLEEP_DIR / f"{target_date}-synthesis.json"
|
|
512
|
+
if not synthesis_file.exists():
|
|
513
|
+
print(f"[apply] No synthesis file for {target_date}. Run synthesize.py first.")
|
|
207
514
|
sys.exit(1)
|
|
208
515
|
|
|
209
|
-
with open(
|
|
210
|
-
|
|
516
|
+
with open(synthesis_file) as f:
|
|
517
|
+
synthesis = json.load(f)
|
|
518
|
+
|
|
519
|
+
run_id = generate_run_id(target_date)
|
|
520
|
+
actions = synthesis.get("actions", [])
|
|
521
|
+
print(f"[apply] Phase 4: Applying findings for {target_date} (run: {run_id})")
|
|
522
|
+
print(f"[apply] Actions to process: {len(actions)}")
|
|
523
|
+
|
|
524
|
+
# Load recent dedupe keys for idempotency
|
|
525
|
+
existing_keys = load_recent_dedupe_keys(target_date)
|
|
526
|
+
print(f"[apply] Existing dedupe keys (7d): {len(existing_keys)}")
|
|
527
|
+
|
|
528
|
+
# Backup databases before mutations
|
|
529
|
+
auto_apply_count = sum(1 for a in actions if a.get("action_class") == "auto_apply")
|
|
530
|
+
if auto_apply_count > 0:
|
|
531
|
+
print("[apply] Creating database backups...")
|
|
532
|
+
nexo_backup = backup_db(NEXO_DB, run_id)
|
|
533
|
+
cog_backup = backup_db(COGNITIVE_DB, run_id)
|
|
534
|
+
if nexo_backup:
|
|
535
|
+
print(f" Backup: {nexo_backup}")
|
|
536
|
+
if cog_backup:
|
|
537
|
+
print(f" Backup: {cog_backup}")
|
|
538
|
+
|
|
539
|
+
# Process actions
|
|
540
|
+
applied_actions = []
|
|
541
|
+
stats = {"applied": 0, "deferred": 0, "skipped_dedupe": 0, "errors": 0}
|
|
542
|
+
|
|
543
|
+
for action in actions:
|
|
544
|
+
dedupe_key = action.get("dedupe_key", "")
|
|
545
|
+
|
|
546
|
+
# Idempotency check
|
|
547
|
+
if dedupe_key and dedupe_key in existing_keys:
|
|
548
|
+
applied_actions.append({
|
|
549
|
+
"applied_action_id": f"{run_id}-deduped",
|
|
550
|
+
"action_type": action.get("action_type"),
|
|
551
|
+
"dedupe_key": dedupe_key,
|
|
552
|
+
"status": "skipped_dedupe",
|
|
553
|
+
"timestamp": datetime.now().isoformat()
|
|
554
|
+
})
|
|
555
|
+
stats["skipped_dedupe"] += 1
|
|
556
|
+
continue
|
|
557
|
+
|
|
558
|
+
result = apply_action(action, run_id)
|
|
559
|
+
applied_actions.append(result)
|
|
560
|
+
|
|
561
|
+
if result["status"] == "applied":
|
|
562
|
+
stats["applied"] += 1
|
|
563
|
+
print(f" Applied: {action.get('action_type')} -- {action.get('content', {}).get('title', '')[:50]}")
|
|
564
|
+
elif result["status"] == "deferred_to_morning":
|
|
565
|
+
stats["deferred"] += 1
|
|
566
|
+
elif result["status"] == "error":
|
|
567
|
+
stats["errors"] += 1
|
|
568
|
+
print(f" Error: {result.get('details', {}).get('error', 'unknown')}", file=sys.stderr)
|
|
569
|
+
|
|
570
|
+
# Update mood in calibration.json
|
|
571
|
+
print("[apply] Updating mood/calibration...")
|
|
572
|
+
mood_result = update_calibration_mood(synthesis)
|
|
573
|
+
if mood_result.get("success"):
|
|
574
|
+
stats["applied"] += 1
|
|
575
|
+
print(f" Mood score: {mood_result.get('mood_score', '?')}")
|
|
576
|
+
else:
|
|
577
|
+
print(f" Mood skip: {mood_result.get('error', '?')}")
|
|
578
|
+
|
|
579
|
+
# Calibrate trust score (authoritative daily score from Deep Sleep)
|
|
580
|
+
print("[apply] Calibrating trust score...")
|
|
581
|
+
trust_result = calibrate_trust_score(synthesis, target_date)
|
|
582
|
+
if trust_result.get("success"):
|
|
583
|
+
stats["applied"] += 1
|
|
584
|
+
print(f" Trust: {trust_result['old_score']:.0f} → {trust_result['new_score']:.0f} (Δ{trust_result['delta']:+.0f}, {trust_result['trend']})")
|
|
585
|
+
else:
|
|
586
|
+
print(f" Trust skip: {trust_result.get('error', '?')}")
|
|
587
|
+
|
|
588
|
+
# Create followups for abandoned projects
|
|
589
|
+
abandoned_results = create_abandoned_followups(synthesis)
|
|
590
|
+
for r in abandoned_results:
|
|
591
|
+
if r.get("success"):
|
|
592
|
+
stats["applied"] += 1
|
|
593
|
+
print(f" Abandoned project followup: {r.get('id')}")
|
|
594
|
+
|
|
595
|
+
# Write morning briefing
|
|
596
|
+
print("[apply] Writing morning briefing...")
|
|
597
|
+
briefing_path = write_morning_briefing(target_date, synthesis)
|
|
598
|
+
print(f" Briefing: {briefing_path}")
|
|
599
|
+
|
|
600
|
+
# Write applied log
|
|
601
|
+
applied_log = {
|
|
602
|
+
"date": target_date,
|
|
603
|
+
"run_id": run_id,
|
|
604
|
+
"applied_at": datetime.now().isoformat(),
|
|
605
|
+
"stats": stats,
|
|
606
|
+
"applied_actions": applied_actions,
|
|
607
|
+
"summary": synthesis.get("summary", ""),
|
|
608
|
+
}
|
|
211
609
|
|
|
212
|
-
|
|
610
|
+
applied_file = DEEP_SLEEP_DIR / f"{target_date}-applied.json"
|
|
611
|
+
with open(applied_file, "w") as f:
|
|
612
|
+
json.dump(applied_log, f, indent=2, ensure_ascii=False)
|
|
213
613
|
|
|
214
|
-
|
|
215
|
-
print(f"
|
|
216
|
-
|
|
614
|
+
print(f"\n[apply] Done.")
|
|
615
|
+
print(f" Applied: {stats['applied']}")
|
|
616
|
+
print(f" Deferred to morning: {stats['deferred']}")
|
|
617
|
+
print(f" Skipped (dedupe): {stats['skipped_dedupe']}")
|
|
618
|
+
print(f" Errors: {stats['errors']}")
|
|
619
|
+
print(f"[apply] Log: {applied_file}")
|
|
217
620
|
|
|
218
621
|
|
|
219
622
|
if __name__ == "__main__":
|