nexo-brain 1.6.0 → 2.0.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 +102 -79
- package/bin/nexo-brain.js +681 -303
- package/bin/postinstall.js +46 -0
- package/package.json +14 -2
- package/scripts/migrate-to-unified.sh +813 -0
- package/scripts/migrate-v1.7-to-v1.8.py +214 -0
- package/scripts/pre-commit-check.sh +1 -1
- package/src/__pycache__/auto_close_sessions.cpython-310.pyc +0 -0
- package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/auto_update.cpython-310.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-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__/kg_populate.cpython-314.pyc +0 -0
- package/src/__pycache__/knowledge_graph.cpython-310.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-310.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-310.pyc +0 -0
- package/src/__pycache__/tools_coordination.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_credentials.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_learnings.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_menu.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_reminders.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_reminders_crud.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_task_history.cpython-314.pyc +0 -0
- package/src/auto_close_sessions.py +4 -3
- package/src/auto_update.py +634 -0
- package/src/cognitive/__pycache__/__init__.cpython-310.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-314.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-310.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-314.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-310.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-314.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-314.pyc +0 -0
- package/src/cognitive/_core.py +7 -3
- package/src/cognitive/_decay.py +1 -1
- package/src/cognitive/_memory.py +7 -3
- package/src/cognitive/_search.py +12 -10
- package/src/cognitive/_trust.py +3 -3
- package/src/dashboard/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/dashboard/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/dashboard/__pycache__/app.cpython-310.pyc +0 -0
- package/src/dashboard/__pycache__/app.cpython-314.pyc +0 -0
- package/src/dashboard/app.py +9 -3
- package/src/dashboard/templates/dashboard.html +4 -4
- package/src/dashboard/templates/operations.html +6 -6
- package/src/db/__init__.py +2 -1
- package/src/db/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/db/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
- package/src/db/_core.py +7 -3
- package/src/db/_episodic.py +69 -1
- package/src/db/_fts.py +12 -12
- package/src/db/_reminders.py +89 -15
- package/src/db/_schema.py +41 -0
- package/src/evolution_cycle.py +33 -11
- package/src/hooks/__pycache__/auto_capture.cpython-310.pyc +0 -0
- package/src/hooks/__pycache__/auto_capture.cpython-314.pyc +0 -0
- package/src/hooks/auto_capture.py +1 -1
- package/src/hooks/capture-tool-logs.sh +76 -0
- package/src/hooks/inbox-hook.sh +2 -1
- package/src/hooks/post-compact.sh +2 -1
- package/src/hooks/pre-compact.sh +104 -2
- package/src/hooks/session-start.sh +6 -2
- package/src/hooks/session-stop.sh +2 -1
- package/src/kg_populate.py +4 -1
- package/src/migrate_embeddings.py +4 -1
- package/src/plugin_loader.py +100 -34
- package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/agents.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/backup.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/core_rules.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cortex.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/entities.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/evolution.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/guard.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/preferences.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
- package/src/plugins/agents.py +2 -2
- package/src/plugins/backup.py +5 -4
- package/src/plugins/cognitive_memory.py +1 -1
- package/src/plugins/core_rules.py +5 -1
- package/src/plugins/episodic_memory.py +43 -16
- package/src/plugins/evolution.py +7 -2
- package/src/plugins/guard.py +45 -17
- package/src/plugins/update.py +238 -0
- package/src/requirements.txt +12 -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/check-context.py +13 -3
- 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 +3 -1
- package/src/scripts/deep-sleep/apply_findings.py +7 -4
- package/src/scripts/deep-sleep/collect_transcripts.py +3 -1
- package/src/scripts/nexo-auto-update.py +4 -211
- package/src/scripts/nexo-backup.sh +25 -0
- package/src/scripts/nexo-brain-activation.sh +26 -26
- package/src/scripts/nexo-catchup.py +39 -25
- package/src/scripts/nexo-cognitive-decay.py +12 -5
- package/src/scripts/nexo-daily-self-audit.py +36 -14
- package/src/scripts/nexo-deep-sleep.sh +4 -3
- package/src/scripts/nexo-evolution-run.py +40 -12
- package/src/scripts/nexo-followup-hygiene.py +6 -3
- package/src/scripts/nexo-github-monitor.py +11 -4
- package/src/scripts/nexo-immune.py +20 -3
- package/src/scripts/nexo-inbox-hook.sh +2 -1
- package/src/scripts/nexo-install.py +6 -0
- package/src/scripts/nexo-learning-housekeep.py +245 -0
- package/src/scripts/nexo-learning-validator.py +12 -2
- package/src/scripts/nexo-migrate.py +232 -0
- package/src/scripts/nexo-postmortem-consolidator.py +38 -19
- package/src/scripts/nexo-pre-commit.py +3 -1
- package/src/scripts/nexo-prevent-sleep.sh +29 -0
- package/src/scripts/nexo-proactive-dashboard.py +8 -6
- package/src/scripts/nexo-runtime-preflight.py +59 -55
- package/src/scripts/nexo-send-email.py +2 -2
- package/src/scripts/nexo-send-reply.py +3 -1
- package/src/scripts/nexo-sleep.py +21 -5
- package/src/scripts/nexo-snapshot-restore.sh +2 -1
- package/src/scripts/nexo-synthesis.py +19 -4
- package/src/scripts/nexo-tcc-approve.sh +79 -0
- package/src/scripts/nexo-update.sh +161 -0
- package/src/scripts/nexo-watchdog-smoke.py +18 -13
- package/src/scripts/nexo-watchdog.sh +41 -31
- package/src/server.py +107 -44
- package/src/storage_router.py +6 -2
- package/src/tools_coordination.py +14 -14
- package/src/tools_credentials.py +11 -11
- package/src/tools_learnings.py +36 -27
- package/src/tools_menu.py +7 -6
- package/src/tools_reminders.py +11 -5
- package/src/tools_reminders_crud.py +11 -9
- package/src/tools_sessions.py +62 -187
- package/src/tools_task_history.py +7 -7
- package/templates/CLAUDE.md.template +49 -469
- package/templates/launchagents/README.md +7 -7
- package/templates/launchagents/com.nexo.auto-close-sessions.plist +5 -1
- package/templates/launchagents/com.nexo.catchup.plist +4 -0
- package/templates/launchagents/com.nexo.cognitive-decay.plist +7 -0
- package/templates/launchagents/com.nexo.dashboard.plist +5 -1
- package/templates/launchagents/com.nexo.deep-sleep.plist +4 -0
- package/templates/launchagents/com.nexo.evolution.plist +4 -0
- package/templates/launchagents/com.nexo.followup-hygiene.plist +4 -0
- package/templates/launchagents/com.nexo.github-monitor.plist +3 -1
- package/templates/launchagents/com.nexo.immune.plist +4 -0
- package/templates/launchagents/com.nexo.postmortem.plist +4 -0
- package/templates/launchagents/com.nexo.self-audit.plist +4 -0
- package/templates/launchagents/com.nexo.synthesis.plist +4 -0
- package/templates/launchagents/com.nexo.watchdog.plist +4 -0
- package/templates/openclaw.json +1 -1
- package/tests/__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
- package/tests/conftest.py +2 -2
- package/tests/test_cognitive.py +7 -6
- package/tests/test_migrations.py +29 -3
package/src/cognitive/_decay.py
CHANGED
|
@@ -254,7 +254,7 @@ def dream_cycle(max_insights: int = 50) -> dict:
|
|
|
254
254
|
})
|
|
255
255
|
|
|
256
256
|
if len(recent_memories) < 2:
|
|
257
|
-
return {"insights_created": 0, "insights": [], "memories_scanned": len(recent_memories)}
|
|
257
|
+
return {"insights_created": 0, "insights": [], "memories_scanned": len(recent_memories), "candidates_found": 0}
|
|
258
258
|
|
|
259
259
|
# 2. Get already-dreamed pairs to skip
|
|
260
260
|
dreamed = set()
|
package/src/cognitive/_memory.py
CHANGED
|
@@ -398,18 +398,20 @@ def get_stats() -> dict:
|
|
|
398
398
|
"""Return statistics about the cognitive memory system."""
|
|
399
399
|
db = _get_db()
|
|
400
400
|
|
|
401
|
-
stm_active = db.execute("SELECT COUNT(*) FROM stm_memories WHERE promoted_to_ltm = 0").fetchone()[0]
|
|
401
|
+
stm_active = db.execute("SELECT COUNT(*) FROM stm_memories WHERE lifecycle_state = 'active' AND promoted_to_ltm = 0").fetchone()[0]
|
|
402
|
+
stm_promoted = db.execute("SELECT COUNT(*) FROM stm_memories WHERE promoted_to_ltm = 1").fetchone()[0]
|
|
403
|
+
stm_total = db.execute("SELECT COUNT(*) FROM stm_memories WHERE lifecycle_state = 'active'").fetchone()[0]
|
|
402
404
|
ltm_active = db.execute("SELECT COUNT(*) FROM ltm_memories WHERE is_dormant = 0").fetchone()[0]
|
|
403
405
|
ltm_dormant = db.execute("SELECT COUNT(*) FROM ltm_memories WHERE is_dormant = 1").fetchone()[0]
|
|
404
406
|
|
|
405
|
-
avg_stm = db.execute("SELECT AVG(strength) FROM stm_memories WHERE promoted_to_ltm = 0").fetchone()[0] or 0.0
|
|
407
|
+
avg_stm = db.execute("SELECT AVG(strength) FROM stm_memories WHERE lifecycle_state = 'active' AND promoted_to_ltm = 0").fetchone()[0] or 0.0
|
|
406
408
|
avg_ltm = db.execute("SELECT AVG(strength) FROM ltm_memories WHERE is_dormant = 0").fetchone()[0] or 0.0
|
|
407
409
|
|
|
408
410
|
total_retrievals = db.execute("SELECT COUNT(*) FROM retrieval_log").fetchone()[0]
|
|
409
411
|
avg_retrieval_score = db.execute("SELECT AVG(top_score) FROM retrieval_log").fetchone()[0] or 0.0
|
|
410
412
|
|
|
411
413
|
top_domains_stm = db.execute(
|
|
412
|
-
"SELECT domain, COUNT(*) as cnt FROM stm_memories WHERE promoted_to_ltm = 0 AND domain != '' GROUP BY domain ORDER BY cnt DESC LIMIT 5"
|
|
414
|
+
"SELECT domain, COUNT(*) as cnt FROM stm_memories WHERE lifecycle_state = 'active' AND promoted_to_ltm = 0 AND domain != '' GROUP BY domain ORDER BY cnt DESC LIMIT 5"
|
|
413
415
|
).fetchall()
|
|
414
416
|
top_domains_ltm = db.execute(
|
|
415
417
|
"SELECT domain, COUNT(*) as cnt FROM ltm_memories WHERE is_dormant = 0 AND domain != '' GROUP BY domain ORDER BY cnt DESC LIMIT 5"
|
|
@@ -420,6 +422,8 @@ def get_stats() -> dict:
|
|
|
420
422
|
|
|
421
423
|
return {
|
|
422
424
|
"stm_active": stm_active,
|
|
425
|
+
"stm_promoted": stm_promoted,
|
|
426
|
+
"stm_total": stm_total,
|
|
423
427
|
"ltm_active": ltm_active,
|
|
424
428
|
"ltm_dormant": ltm_dormant,
|
|
425
429
|
"avg_stm_strength": round(avg_stm, 3),
|
package/src/cognitive/_search.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""NEXO Cognitive — Search, retrieval, ranking."""
|
|
2
2
|
import math
|
|
3
|
+
import sqlite3
|
|
3
4
|
import numpy as np
|
|
4
5
|
from datetime import datetime
|
|
5
6
|
from cognitive._core import _get_db, embed, cosine_similarity, _blob_to_array, _array_to_blob, _get_model, _get_reranker, rerank_results, EMBEDDING_DIM
|
|
@@ -112,11 +113,12 @@ def _rrf_fuse(vector_results: list[dict], bm25_results: list[dict],
|
|
|
112
113
|
|
|
113
114
|
# If we have the original cosine score, blend it in to preserve semantic confidence
|
|
114
115
|
if vec_result and "score" in vec_result:
|
|
115
|
-
# Weighted blend:
|
|
116
|
-
|
|
116
|
+
# Weighted blend: cosine for confidence + RRF for ranking boost
|
|
117
|
+
rrf_normalized = min(1.0, rrf_score * k) # normalize to 0-1 range
|
|
118
|
+
result["score"] = 0.7 * vec_result["score"] + 0.3 * rrf_normalized
|
|
117
119
|
else:
|
|
118
|
-
# BM25-only result: use RRF score scaled to ~0.
|
|
119
|
-
result["score"] = min(0.
|
|
120
|
+
# BM25-only result: use RRF score scaled to ~0.3-0.7 range
|
|
121
|
+
result["score"] = min(0.75, rrf_score * k)
|
|
120
122
|
|
|
121
123
|
result["bm25_boosted"] = key in bm25_lookup
|
|
122
124
|
result["bm25_only"] = key not in vec_lookup
|
|
@@ -199,8 +201,8 @@ def _apply_temporal_boost(results: list[dict], query_text: str) -> list[dict]:
|
|
|
199
201
|
# Bounded exponential decay boost
|
|
200
202
|
boost = alpha * math.exp(-ln2 * age_days / half_life_days)
|
|
201
203
|
|
|
202
|
-
# Apply boost (capped at 1.0)
|
|
203
|
-
r["score"] = min(
|
|
204
|
+
# Apply boost (capped at 0.95 — reserve 1.0 for exact matches only)
|
|
205
|
+
r["score"] = min(0.95, r["score"] + boost)
|
|
204
206
|
if boost > 0.001:
|
|
205
207
|
r["temporal_boost"] = round(boost, 4)
|
|
206
208
|
|
|
@@ -282,7 +284,7 @@ def _kg_boost_results(results: list[dict], max_boost: float = 0.08) -> list[dict
|
|
|
282
284
|
for idx in ref_map.get(node_ref, []):
|
|
283
285
|
r = results[idx]
|
|
284
286
|
if r.get("score", 0) >= 0.45: # Same relevance gate as temporal
|
|
285
|
-
r["score"] = min(
|
|
287
|
+
r["score"] = min(0.95, r["score"] + boost)
|
|
286
288
|
r["kg_boost"] = round(boost, 4)
|
|
287
289
|
r["kg_connections"] = connections
|
|
288
290
|
|
|
@@ -613,7 +615,7 @@ def _rehearse_results(results: list[dict], skip_ids: set = None):
|
|
|
613
615
|
continue
|
|
614
616
|
table = "stm_memories" if r["store"] == "stm" else "ltm_memories"
|
|
615
617
|
db.execute(
|
|
616
|
-
f"UPDATE {table} SET strength = 1.0, access_count = access_count + 1, last_accessed = ? WHERE id = ?",
|
|
618
|
+
f"UPDATE {table} SET strength = MIN(1.0, strength + 0.08), access_count = access_count + 1, last_accessed = ? WHERE id = ?",
|
|
617
619
|
(now, r["id"])
|
|
618
620
|
)
|
|
619
621
|
db.commit()
|
|
@@ -834,7 +836,7 @@ def search(
|
|
|
834
836
|
if is_temporal_query:
|
|
835
837
|
for r in results:
|
|
836
838
|
if r.get("temporal_date"):
|
|
837
|
-
r["score"] = min(
|
|
839
|
+
r["score"] = min(0.95, r["score"] + 0.05)
|
|
838
840
|
|
|
839
841
|
# Recency temporal boost: recent memories get additive bonus (query-adaptive)
|
|
840
842
|
results = _apply_temporal_boost(results, query_text)
|
|
@@ -866,7 +868,7 @@ def search(
|
|
|
866
868
|
existing_hashes.add(co_hash)
|
|
867
869
|
if co_hash in neighbor_boosts:
|
|
868
870
|
boost = neighbor_boosts[co_hash]
|
|
869
|
-
r["score"] = min(
|
|
871
|
+
r["score"] = min(0.95, r["score"] + boost)
|
|
870
872
|
r["co_activation_boost"] = boost
|
|
871
873
|
|
|
872
874
|
# Add neighbor memories not already in results
|
package/src/cognitive/_trust.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""NEXO Cognitive — Trust scoring, sentiment, dissonance."""
|
|
2
2
|
import re
|
|
3
3
|
import numpy as np
|
|
4
|
-
from datetime import datetime
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
5
|
from cognitive._core import _get_db, embed, cosine_similarity, _blob_to_array
|
|
6
6
|
from cognitive._core import POSITIVE_SIGNALS, NEGATIVE_SIGNALS, URGENCY_SIGNALS
|
|
7
7
|
|
|
@@ -314,13 +314,13 @@ def detect_sentiment(text: str) -> dict:
|
|
|
314
314
|
sentiment = "negative"
|
|
315
315
|
intensity = min(1.0, 0.3 + neg_score * 0.15)
|
|
316
316
|
if intensity > 0.7:
|
|
317
|
-
guidance = "MODE: Ultra-
|
|
317
|
+
guidance = "MODE: Ultra-concise. Zero explanations. Solve and show result."
|
|
318
318
|
else:
|
|
319
319
|
guidance = "MODE: Concise. Less context, more direct action."
|
|
320
320
|
elif pos_score > neg_score and pos_score >= 1:
|
|
321
321
|
sentiment = "positive"
|
|
322
322
|
intensity = min(1.0, 0.3 + pos_score * 0.15)
|
|
323
|
-
guidance = "MODE: Normal.
|
|
323
|
+
guidance = "MODE: Normal. Good time to suggest backlog ideas or improvements."
|
|
324
324
|
elif urgency_hits:
|
|
325
325
|
sentiment = "urgent"
|
|
326
326
|
intensity = 0.8
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/dashboard/app.py
CHANGED
|
@@ -10,6 +10,7 @@ Usage:
|
|
|
10
10
|
import argparse
|
|
11
11
|
import json
|
|
12
12
|
import os
|
|
13
|
+
import platform
|
|
13
14
|
import subprocess
|
|
14
15
|
import sys
|
|
15
16
|
import time
|
|
@@ -22,7 +23,7 @@ from fastapi.responses import HTMLResponse, JSONResponse
|
|
|
22
23
|
from fastapi.staticfiles import StaticFiles
|
|
23
24
|
from pydantic import BaseModel
|
|
24
25
|
|
|
25
|
-
# Add parent dir to path so we can import
|
|
26
|
+
# Add parent dir to path so we can import NEXO modules
|
|
26
27
|
_PARENT = str(Path(__file__).resolve().parent.parent)
|
|
27
28
|
if _PARENT not in sys.path:
|
|
28
29
|
sys.path.insert(0, _PARENT)
|
|
@@ -63,7 +64,7 @@ async def create_tables():
|
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
# ---------------------------------------------------------------------------
|
|
66
|
-
# Lazy imports — modules live in the parent
|
|
67
|
+
# Lazy imports — modules live in the parent source directory
|
|
67
68
|
# ---------------------------------------------------------------------------
|
|
68
69
|
|
|
69
70
|
def _cognitive():
|
|
@@ -602,6 +603,11 @@ async def api_ops_execute(fid: str):
|
|
|
602
603
|
return JSONResponse({"error": f"Followup {fid} not found"}, status_code=404)
|
|
603
604
|
item = dict(row)
|
|
604
605
|
description = item["description"].replace('"', '\\"').replace("'", "\\'")
|
|
606
|
+
if platform.system() != "Darwin":
|
|
607
|
+
return JSONResponse(
|
|
608
|
+
{"error": "This operation requires macOS (uses osascript to open Terminal)"},
|
|
609
|
+
status_code=501,
|
|
610
|
+
)
|
|
605
611
|
script = f'tell application "Terminal" to do script "claude \\"NEXO: execute followup #{fid} — {description}\\""'
|
|
606
612
|
subprocess.Popen(["osascript", "-e", script])
|
|
607
613
|
return {"success": True, "followup_id": fid}
|
|
@@ -728,7 +734,7 @@ async def api_calendar(
|
|
|
728
734
|
@app.get("/api/watchdog")
|
|
729
735
|
async def api_watchdog():
|
|
730
736
|
"""Read watchdog status from file."""
|
|
731
|
-
nexo_home = os.environ.get("NEXO_HOME", str(Path.home() / "
|
|
737
|
+
nexo_home = os.environ.get("NEXO_HOME", str(Path.home() / ".nexo"))
|
|
732
738
|
watchdog_path = Path(nexo_home) / "operations" / "watchdog-status.json"
|
|
733
739
|
if not watchdog_path.exists():
|
|
734
740
|
return JSONResponse(
|
|
@@ -447,10 +447,10 @@
|
|
|
447
447
|
// --- Overdue Items ---
|
|
448
448
|
if (remindersData || followupsData) {
|
|
449
449
|
const reminders = (remindersData?.reminders || []).filter(r =>
|
|
450
|
-
r.status === '
|
|
450
|
+
r.status === 'PENDING' && r.date && r.date < today
|
|
451
451
|
);
|
|
452
452
|
const followups = (followupsData?.followups || []).filter(f =>
|
|
453
|
-
f.status === '
|
|
453
|
+
f.status === 'PENDING' && f.date && f.date < today
|
|
454
454
|
);
|
|
455
455
|
const total = reminders.length + followups.length;
|
|
456
456
|
const el = document.getElementById('overdue-count');
|
|
@@ -530,12 +530,12 @@
|
|
|
530
530
|
if (remindersData || followupsData) {
|
|
531
531
|
const items = [];
|
|
532
532
|
(remindersData?.reminders || []).forEach(r => {
|
|
533
|
-
if (r.status === '
|
|
533
|
+
if (r.status === 'PENDING' && r.date && r.date <= today) {
|
|
534
534
|
items.push({ ...r, _type: 'R', _overdue: r.date < today });
|
|
535
535
|
}
|
|
536
536
|
});
|
|
537
537
|
(followupsData?.followups || []).forEach(f => {
|
|
538
|
-
if (f.status === '
|
|
538
|
+
if (f.status === 'PENDING' && f.date && f.date <= today) {
|
|
539
539
|
items.push({ ...f, _type: 'F', _overdue: f.date < today });
|
|
540
540
|
}
|
|
541
541
|
});
|
|
@@ -154,9 +154,9 @@
|
|
|
154
154
|
onchange="loadOpsData()"
|
|
155
155
|
class="bg-slate-800 border border-slate-700 rounded-lg px-2 py-1 text-xs text-slate-200 focus:outline-none focus:ring-1 focus:ring-violet-500"
|
|
156
156
|
>
|
|
157
|
-
<option value="
|
|
157
|
+
<option value="PENDING">Pending</option>
|
|
158
158
|
<option value="all">All</option>
|
|
159
|
-
<option value="
|
|
159
|
+
<option value="COMPLETED">Completed</option>
|
|
160
160
|
</select>
|
|
161
161
|
</div>
|
|
162
162
|
</header>
|
|
@@ -380,7 +380,7 @@
|
|
|
380
380
|
const today = getToday();
|
|
381
381
|
const weekEnd = getWeekEnd();
|
|
382
382
|
return {
|
|
383
|
-
overdue: items.filter(i => i.date && i.date < today && i.status !== '
|
|
383
|
+
overdue: items.filter(i => i.date && i.date < today && i.status !== 'COMPLETED'),
|
|
384
384
|
today: items.filter(i => i.date === today),
|
|
385
385
|
week: items.filter(i => i.date && i.date > today && i.date <= weekEnd),
|
|
386
386
|
upcoming: items.filter(i => i.date && i.date > weekEnd),
|
|
@@ -393,9 +393,9 @@
|
|
|
393
393
|
// -----------------------------------------------------------------------
|
|
394
394
|
function renderItem(item, type) {
|
|
395
395
|
const today = getToday();
|
|
396
|
-
const isOverdue = item.date && item.date < today && item.status !== '
|
|
396
|
+
const isOverdue = item.date && item.date < today && item.status !== 'COMPLETED';
|
|
397
397
|
const isToday = item.date === today;
|
|
398
|
-
const isCompleted = item.status === '
|
|
398
|
+
const isCompleted = item.status === 'COMPLETED';
|
|
399
399
|
|
|
400
400
|
const dotColor = isCompleted ? 'bg-emerald-400' : isOverdue ? 'bg-red-400' : isToday ? 'bg-amber-400' : 'bg-slate-500';
|
|
401
401
|
const dateLabel = relativeDate(item.date);
|
|
@@ -542,7 +542,7 @@
|
|
|
542
542
|
const res = await fetch(url, {
|
|
543
543
|
method: 'PUT',
|
|
544
544
|
headers: { 'Content-Type': 'application/json' },
|
|
545
|
-
body: JSON.stringify({ status: '
|
|
545
|
+
body: JSON.stringify({ status: 'COMPLETED' })
|
|
546
546
|
});
|
|
547
547
|
const data = await res.json();
|
|
548
548
|
if (data.success) {
|
package/src/db/__init__.py
CHANGED
|
@@ -39,6 +39,7 @@ from db._reminders import (
|
|
|
39
39
|
get_reminders, get_reminder,
|
|
40
40
|
create_followup, update_followup, complete_followup, delete_followup,
|
|
41
41
|
get_followups, get_followup,
|
|
42
|
+
find_similar_followups,
|
|
42
43
|
)
|
|
43
44
|
|
|
44
45
|
# Learnings
|
|
@@ -69,7 +70,7 @@ from db._entities import (
|
|
|
69
70
|
|
|
70
71
|
# Episodic memory
|
|
71
72
|
from db._episodic import (
|
|
72
|
-
cleanup_old_changes, log_change, search_changes, update_change_commit,
|
|
73
|
+
cleanup_old_changes, log_change, search_changes, update_change_commit, auto_resolve_followups,
|
|
73
74
|
cleanup_old_decisions, log_decision, update_decision_outcome,
|
|
74
75
|
get_memory_review_queue, find_decisions_by_context_ref, search_decisions,
|
|
75
76
|
cleanup_old_diaries, write_session_diary,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/db/_core.py
CHANGED
|
@@ -9,11 +9,15 @@ import datetime
|
|
|
9
9
|
import pathlib
|
|
10
10
|
import threading
|
|
11
11
|
|
|
12
|
+
NEXO_HOME = os.environ.get("NEXO_HOME", os.path.expanduser("~/.nexo"))
|
|
13
|
+
_data_dir = os.path.join(NEXO_HOME, "data")
|
|
14
|
+
os.makedirs(_data_dir, exist_ok=True)
|
|
15
|
+
|
|
12
16
|
DB_PATH = os.environ.get(
|
|
13
17
|
"NEXO_TEST_DB",
|
|
14
18
|
os.environ.get(
|
|
15
19
|
"NEXO_DB",
|
|
16
|
-
os.path.join(
|
|
20
|
+
os.path.join(_data_dir, "nexo.db"),
|
|
17
21
|
),
|
|
18
22
|
)
|
|
19
23
|
|
|
@@ -168,7 +172,7 @@ def init_db():
|
|
|
168
172
|
id TEXT PRIMARY KEY,
|
|
169
173
|
date TEXT,
|
|
170
174
|
description TEXT NOT NULL,
|
|
171
|
-
status TEXT NOT NULL DEFAULT '
|
|
175
|
+
status TEXT NOT NULL DEFAULT 'PENDING',
|
|
172
176
|
category TEXT DEFAULT 'general',
|
|
173
177
|
created_at REAL NOT NULL,
|
|
174
178
|
updated_at REAL NOT NULL
|
|
@@ -179,7 +183,7 @@ def init_db():
|
|
|
179
183
|
date TEXT,
|
|
180
184
|
description TEXT NOT NULL,
|
|
181
185
|
verification TEXT DEFAULT '',
|
|
182
|
-
status TEXT NOT NULL DEFAULT '
|
|
186
|
+
status TEXT NOT NULL DEFAULT 'PENDING',
|
|
183
187
|
recurrence TEXT DEFAULT NULL,
|
|
184
188
|
created_at REAL NOT NULL,
|
|
185
189
|
updated_at REAL NOT NULL
|
package/src/db/_episodic.py
CHANGED
|
@@ -69,8 +69,73 @@ def search_changes(query: str = '', files: str = '', days: int = 30) -> list[dic
|
|
|
69
69
|
return [dict(r) for r in rows]
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
def auto_resolve_followups(change: dict) -> list[str]:
|
|
73
|
+
"""Cross-reference a change_log entry with open followups. Auto-completes matches.
|
|
74
|
+
|
|
75
|
+
Matching logic:
|
|
76
|
+
1. File overlap: if change touched files mentioned in followup description
|
|
77
|
+
2. Keyword overlap: Jaccard similarity between change text and followup text
|
|
78
|
+
3. ID reference: if followup ID appears in the change's triggered_by/why fields
|
|
79
|
+
|
|
80
|
+
Returns list of followup IDs that were auto-resolved.
|
|
81
|
+
"""
|
|
82
|
+
conn = get_db()
|
|
83
|
+
open_followups = conn.execute(
|
|
84
|
+
"SELECT * FROM followups WHERE status NOT LIKE 'COMPLETED%' "
|
|
85
|
+
"AND status != 'DELETED'"
|
|
86
|
+
).fetchall()
|
|
87
|
+
|
|
88
|
+
if not open_followups:
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
change_text = " ".join(str(change.get(f, "")) for f in
|
|
92
|
+
["files", "what_changed", "why", "triggered_by", "affects"])
|
|
93
|
+
change_files = set(change.get("files", "").replace(",", " ").split())
|
|
94
|
+
change_tokens = {w.lower() for w in change_text.split() if len(w) > 3}
|
|
95
|
+
|
|
96
|
+
resolved = []
|
|
97
|
+
for f in open_followups:
|
|
98
|
+
fid = f["id"]
|
|
99
|
+
fdesc = f"{fid} {f['description']} {f['verification'] or ''}"
|
|
100
|
+
ftokens = {w.lower() for w in fdesc.split() if len(w) > 3}
|
|
101
|
+
|
|
102
|
+
# Check 1: followup ID explicitly in change trigger/why
|
|
103
|
+
if fid.lower() in change_text.lower():
|
|
104
|
+
resolved.append(fid)
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
# Check 2: file overlap (any changed file mentioned in followup)
|
|
108
|
+
if change_files:
|
|
109
|
+
for cf in change_files:
|
|
110
|
+
basename = cf.rsplit("/", 1)[-1] if "/" in cf else cf
|
|
111
|
+
if basename and len(basename) > 4 and basename.lower() in fdesc.lower():
|
|
112
|
+
resolved.append(fid)
|
|
113
|
+
break
|
|
114
|
+
if fid in resolved:
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
# Check 3: keyword similarity (asymmetric overlap >= 0.35)
|
|
118
|
+
if ftokens and change_tokens:
|
|
119
|
+
intersection = ftokens & change_tokens
|
|
120
|
+
smaller = min(len(ftokens), len(change_tokens))
|
|
121
|
+
score = len(intersection) / smaller if smaller else 0
|
|
122
|
+
if score >= 0.35:
|
|
123
|
+
resolved.append(fid)
|
|
124
|
+
|
|
125
|
+
# Auto-complete matched followups
|
|
126
|
+
from db._reminders import complete_followup
|
|
127
|
+
commit_ref = change.get("commit_ref", "")
|
|
128
|
+
for fid in resolved:
|
|
129
|
+
complete_followup(fid, result=f"Auto-resolved by change #{change.get('id', '?')} (commit {commit_ref[:8] if commit_ref else 'N/A'})")
|
|
130
|
+
|
|
131
|
+
return resolved
|
|
132
|
+
|
|
133
|
+
|
|
72
134
|
def update_change_commit(id: int, commit_ref: str) -> dict:
|
|
73
|
-
"""Link a change log entry to its git commit after commit.
|
|
135
|
+
"""Link a change log entry to its git commit after commit.
|
|
136
|
+
|
|
137
|
+
After linking, auto-resolves any open followups that match the change.
|
|
138
|
+
"""
|
|
74
139
|
conn = get_db()
|
|
75
140
|
row = conn.execute("SELECT * FROM change_log WHERE id = ?", (id,)).fetchone()
|
|
76
141
|
if not row:
|
|
@@ -81,6 +146,9 @@ def update_change_commit(id: int, commit_ref: str) -> dict:
|
|
|
81
146
|
r = dict(row)
|
|
82
147
|
body = f"{r.get('what_changed','')} {r.get('why','')} {r.get('triggered_by','')} {r.get('affects','')} {r.get('risks','')}"
|
|
83
148
|
fts_upsert("change", str(id), r.get("files",""), body, "change_log", commit=False)
|
|
149
|
+
|
|
150
|
+
# Auto-resolve followups that match this change
|
|
151
|
+
r["_auto_resolved"] = auto_resolve_followups(r)
|
|
84
152
|
return r
|
|
85
153
|
|
|
86
154
|
|
package/src/db/_fts.py
CHANGED
|
@@ -2,27 +2,27 @@
|
|
|
2
2
|
import os, pathlib, sqlite3, threading, datetime
|
|
3
3
|
from db._core import get_db, now_epoch, DB_PATH
|
|
4
4
|
|
|
5
|
+
NEXO_HOME = os.environ.get("NEXO_HOME", os.path.expanduser("~/.nexo"))
|
|
6
|
+
|
|
5
7
|
# ── FTS5 Unified Search ──────────────────────────────────────────
|
|
6
8
|
|
|
7
9
|
# Directories to index for unified search
|
|
8
10
|
_FTS_MD_DIRS = [
|
|
9
|
-
os.path.
|
|
10
|
-
os.path.
|
|
11
|
-
os.path.
|
|
12
|
-
os.path.
|
|
13
|
-
os.path.
|
|
14
|
-
os.path.
|
|
15
|
-
os.path.
|
|
16
|
-
os.path.
|
|
11
|
+
os.path.join(NEXO_HOME, "docs"),
|
|
12
|
+
os.path.join(NEXO_HOME, "projects"),
|
|
13
|
+
os.path.join(NEXO_HOME, "memory"),
|
|
14
|
+
os.path.join(NEXO_HOME, "operations"),
|
|
15
|
+
os.path.join(NEXO_HOME, "learnings"),
|
|
16
|
+
os.path.join(NEXO_HOME, "brain"),
|
|
17
|
+
os.path.join(NEXO_HOME, "agents"),
|
|
18
|
+
os.path.join(NEXO_HOME, "skills"),
|
|
17
19
|
]
|
|
18
20
|
# Code repos: index source files (skip vendor, node_modules, etc.)
|
|
19
|
-
_FTS_CODE_DIRS = [
|
|
20
|
-
(os.path.expanduser("~/Documents/_PhpstormProjects"), ["*.php", "*.js", "*.json", "*.py", "*.ts", "*.tsx"]),
|
|
21
|
-
]
|
|
21
|
+
_FTS_CODE_DIRS = [] # Users can add project dirs via nexo_index_add_dir
|
|
22
22
|
_FTS_CODE_SKIP = {
|
|
23
23
|
"vendor", "node_modules", ".git", "cache", "tmp", "logs", "uploads",
|
|
24
24
|
"assets/img", "assets/fonts", ".next", "dist", "build", ".prisma",
|
|
25
|
-
"
|
|
25
|
+
"public/build", ".turbo", "__pycache__",
|
|
26
26
|
"coverage", ".nyc_output", "storage/framework", "bootstrap/cache",
|
|
27
27
|
}
|
|
28
28
|
_FTS_MAX_FILE_SIZE = 50_000 # skip .md files >50KB
|