nexo-brain 1.4.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +63 -12
  2. package/package.json +7 -3
  3. package/src/claim_graph.py +323 -0
  4. package/src/cognitive/__init__.py +62 -0
  5. package/src/cognitive/_core.py +563 -0
  6. package/src/cognitive/_decay.py +382 -0
  7. package/src/cognitive/_ingest.py +892 -0
  8. package/src/cognitive/_memory.py +907 -0
  9. package/src/cognitive/_search.py +947 -0
  10. package/src/cognitive/_trust.py +464 -0
  11. package/src/db/__init__.py +88 -0
  12. package/src/db/_core.py +413 -0
  13. package/src/db/_credentials.py +124 -0
  14. package/src/db/_entities.py +178 -0
  15. package/src/db/_episodic.py +670 -0
  16. package/src/db/_evolution.py +54 -0
  17. package/src/db/_fts.py +406 -0
  18. package/src/db/_learnings.py +168 -0
  19. package/src/db/_reminders.py +260 -0
  20. package/src/db/_schema.py +323 -0
  21. package/src/db/_sessions.py +300 -0
  22. package/src/db/_tasks.py +91 -0
  23. package/src/hnsw_index.py +254 -0
  24. package/src/plugins/agents.py +11 -11
  25. package/src/plugins/backup.py +3 -3
  26. package/src/plugins/cognitive_memory.py +9 -9
  27. package/src/plugins/episodic_memory.py +70 -70
  28. package/src/plugins/evolution.py +4 -4
  29. package/src/plugins/guard.py +2 -2
  30. package/src/scripts/deep-sleep/analyze_session.py +215 -0
  31. package/src/scripts/deep-sleep/apply_findings.py +167 -0
  32. package/src/scripts/deep-sleep/collect_transcripts.py +143 -0
  33. package/src/scripts/deep-sleep/prompt.md +109 -0
  34. package/src/scripts/nexo-deep-sleep.sh +76 -0
  35. package/src/scripts/nexo-inbox-hook.sh +73 -0
  36. package/src/scripts/nexo-watchdog.sh +83 -3
  37. package/src/server.py +13 -8
  38. package/src/tools_coordination.py +3 -3
  39. package/src/tools_credentials.py +13 -13
  40. package/src/tools_menu.py +55 -37
  41. package/src/tools_reminders.py +1 -1
  42. package/src/tools_reminders_crud.py +21 -21
  43. package/src/tools_sessions.py +39 -31
  44. package/src/tools_task_history.py +11 -11
  45. package/tests/__init__.py +0 -0
  46. package/tests/conftest.py +71 -0
  47. package/tests/test_cognitive.py +204 -0
  48. package/tests/test_knowledge_graph.py +140 -0
  49. package/tests/test_migrations.py +111 -0
  50. package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
  51. package/src/__pycache__/cognitive.cpython-314.pyc +0 -0
  52. package/src/__pycache__/db.cpython-314.pyc +0 -0
  53. package/src/__pycache__/evolution_cycle.cpython-314.pyc +0 -0
  54. package/src/__pycache__/kg_populate.cpython-314.pyc +0 -0
  55. package/src/__pycache__/knowledge_graph.cpython-314.pyc +0 -0
  56. package/src/__pycache__/maintenance.cpython-314.pyc +0 -0
  57. package/src/__pycache__/migrate_embeddings.cpython-314.pyc +0 -0
  58. package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
  59. package/src/__pycache__/server.cpython-314.pyc +0 -0
  60. package/src/__pycache__/storage_router.cpython-314.pyc +0 -0
  61. package/src/__pycache__/tools_coordination.cpython-314.pyc +0 -0
  62. package/src/__pycache__/tools_credentials.cpython-314.pyc +0 -0
  63. package/src/__pycache__/tools_learnings.cpython-314.pyc +0 -0
  64. package/src/__pycache__/tools_menu.cpython-314.pyc +0 -0
  65. package/src/__pycache__/tools_reminders.cpython-314.pyc +0 -0
  66. package/src/__pycache__/tools_reminders_crud.cpython-314.pyc +0 -0
  67. package/src/__pycache__/tools_sessions.cpython-314.pyc +0 -0
  68. package/src/__pycache__/tools_task_history.cpython-314.pyc +0 -0
  69. package/src/cognitive.py +0 -3803
  70. package/src/dashboard/__pycache__/__init__.cpython-314.pyc +0 -0
  71. package/src/dashboard/__pycache__/app.cpython-314.pyc +0 -0
  72. package/src/db.py +0 -2925
  73. package/src/hooks/__pycache__/auto_capture.cpython-314.pyc +0 -0
  74. package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
  75. package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
  76. package/src/plugins/__pycache__/agents.cpython-314.pyc +0 -0
  77. package/src/plugins/__pycache__/artifact_registry.cpython-314.pyc +0 -0
  78. package/src/plugins/__pycache__/backup.cpython-314.pyc +0 -0
  79. package/src/plugins/__pycache__/cognitive_memory.cpython-314.pyc +0 -0
  80. package/src/plugins/__pycache__/core_rules.cpython-314.pyc +0 -0
  81. package/src/plugins/__pycache__/cortex.cpython-314.pyc +0 -0
  82. package/src/plugins/__pycache__/entities.cpython-314.pyc +0 -0
  83. package/src/plugins/__pycache__/episodic_memory.cpython-314.pyc +0 -0
  84. package/src/plugins/__pycache__/evolution.cpython-314.pyc +0 -0
  85. package/src/plugins/__pycache__/guard.cpython-314.pyc +0 -0
  86. package/src/plugins/__pycache__/knowledge_graph_tools.cpython-314.pyc +0 -0
  87. package/src/plugins/__pycache__/preferences.cpython-314.pyc +0 -0
  88. package/src/rules/__pycache__/__init__.cpython-314.pyc +0 -0
  89. package/src/rules/__pycache__/migrate.cpython-314.pyc +0 -0
  90. package/src/rules/core-rules 2.json +0 -329
  91. package/src/scripts/__pycache__/check-context.cpython-314.pyc +0 -0
  92. package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
  93. package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
  94. package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
  95. package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
  96. package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
  97. package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
  98. package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
  99. package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
  100. package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
  101. package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
  102. package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
  103. package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
  104. package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
  105. package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
  106. package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
  107. package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
  108. package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
  109. package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
package/README.md CHANGED
@@ -198,7 +198,7 @@ This means long sessions (8+ hours) feel like one continuous conversation instea
198
198
 
199
199
  ## Cognitive Features
200
200
 
201
- NEXO Brain provides 29 cognitive tools on top of the 78 base tools, totaling **115+ MCP tools**. These features implement cognitive science concepts that go beyond basic memory:
201
+ NEXO Brain provides **100+ MCP tools** implementing cognitive science concepts that go beyond basic memory:
202
202
 
203
203
  ### Input Pipeline
204
204
 
@@ -226,11 +226,13 @@ NEXO Brain provides 29 cognitive tools on top of the 78 base tools, totaling **1
226
226
  |---------|-------------|
227
227
  | **HyDE Query Expansion** | Generates hypothetical answer embeddings for richer semantic search. Instead of searching for "deploy error", it imagines what a helpful memory about deploy errors would look like, then searches for that. |
228
228
  | **Hybrid Search (FTS5+BM25+RRF)** | Combines dense vector search with BM25 keyword search via Reciprocal Rank Fusion. Outperforms pure semantic search on precise terminology and code identifiers. |
229
+ | **KG Boost** | Knowledge Graph connection count influences retrieval ranking. Memories linked to well-connected entities (many edges) receive a logarithmic score bonus, surfacing contextually important facts higher. |
230
+ | **HNSW Vector Index** | Optional approximate nearest neighbor index (hnswlib). Activates automatically when memory count exceeds 10,000. Falls back to exact brute-force below that threshold — no configuration needed. |
229
231
  | **Cross-Encoder Reranking** | After initial vector retrieval, a cross-encoder model rescores candidates for precision. The top-k results are reordered by true semantic relevance before being returned to the agent. |
230
232
  | **Multi-Query Decomposition** | Complex questions are automatically split into sub-queries. Each component is retrieved independently, then fused for a higher-quality answer — improves recall on multi-faceted prompts. |
231
233
  | **Temporal Indexing** | Memories are indexed by time in addition to semantics. Time-sensitive queries ("what did we decide last Tuesday?") use temporal proximity scoring alongside semantic similarity. |
232
234
  | **Spreading Activation** | Graph-based co-activation network. Memories retrieved together reinforce each other's connections, building an associative web that improves over time. |
233
- | **Recall Explanations** | Transparent score breakdown for every retrieval result. Shows exactly why a memory was returned: semantic similarity, recency, access frequency, and co-activation bonuses. |
235
+ | **Recall Explanations** | Transparent score breakdown for every retrieval result. Shows exactly why a memory was returned: semantic similarity, recency, access frequency, KG boost, and co-activation bonuses. |
234
236
 
235
237
  ### Proactive
236
238
 
@@ -412,7 +414,7 @@ That's it. No need to run `claude` manually. Your operator will greet you immedi
412
414
  | Component | What | Where |
413
415
  |-----------|------|-------|
414
416
  | Cognitive engine | Python: fastembed, numpy, vector search | pip packages |
415
- | MCP server | 111+ tools for memory, cognition, learning, guard | ~/.nexo/ |
417
+ | MCP server | 100+ tools for memory, cognition, learning, guard | ~/.nexo/ |
416
418
  | Plugins | Guard, episodic memory, cognitive memory, entities, preferences | ~/.nexo/plugins/ |
417
419
  | Hooks (6) | SessionStart briefing, Stop post-mortem, PostToolUse capture, PreCompact checkpoint, PostCompact recovery, Caffeinate | ~/.nexo/hooks/ |
418
420
  | Reflection engine | Processes session buffer, extracts patterns, updates user model | ~/.nexo/scripts/ |
@@ -425,12 +427,23 @@ That's it. No need to run `claude` manually. Your operator will greet you immedi
425
427
 
426
428
  - **macOS or Linux** (Windows via [WSL](https://learn.microsoft.com/en-us/windows/wsl/install))
427
429
  - **Node.js 18+** (for the installer)
428
- - **Claude Opus (latest version) strongly recommended.** NEXO Brain provides 111+ MCP tools across 20 categories. This cognitive load requires a top-tier model with large context window. Smaller models (Haiku, Sonnet) may struggle with tool selection and produce inconsistent results. Opus handles all 111+ tools without hesitation.
430
+ - **Claude Opus (latest version) strongly recommended.** NEXO Brain provides 100+ MCP tools across 20 categories. This cognitive load requires a top-tier model with large context window. Smaller models (Haiku, Sonnet) may struggle with tool selection and produce inconsistent results. Opus handles all 100+ tools without hesitation.
429
431
  - Python 3, Homebrew, and Claude Code are installed automatically if missing.
430
432
 
431
433
  ## Architecture
432
434
 
433
- ### 111+ MCP Tools across 20 Categories
435
+ ### Modular Package Structure (v1.5.0)
436
+
437
+ The core is organized into two Python packages:
438
+
439
+ | Package | Modules | Responsibility |
440
+ |---------|---------|----------------|
441
+ | `db/` | 11 modules (`_core`, `_schema`, `_sessions`, `_learnings`, `_episodic`, `_credentials`, `_entities`, `_evolution`, `_fts`, `_reminders`, `_tasks`) | All SQLite persistence: schema migrations, CRUD, FTS indexing |
442
+ | `cognitive/` | 6 modules (`_core`, `_memory`, `_ingest`, `_search`, `_decay`, `_trust`) | Cognitive engine: embeddings, RAG, decay, trust scoring |
443
+
444
+ The rest of the server (`server.py`, `tools_*.py`, `plugins/`) stays flat for clarity.
445
+
446
+ ### 100+ MCP Tools across 20 Categories
434
447
 
435
448
  | Category | Count | Tools | Purpose |
436
449
  |----------|-------|-------|---------|
@@ -439,7 +452,7 @@ That's it. No need to run `claude` manually. Your operator will greet you immedi
439
452
  | Cognitive Advanced | 8 | hyde_search, spread_activate, explain_recall, dream, prospect, hook_capture, pin, archive | Advanced retrieval, proactive, lifecycle |
440
453
  | Guard | 3 | check, stats, log_repetition | Metacognitive error prevention |
441
454
  | Episodic | 10 | change_log/search/commit, decision_log/outcome/search, review_queue, diary_write/read, recall | What happened and why |
442
- | Sessions | 4 | startup, heartbeat, stop, status | Session lifecycle + context shift detection |
455
+ | Sessions | 4 | startup, heartbeat, stop, status | Session lifecycle + context shift detection + inter-terminal auto-inbox |
443
456
  | Coordination | 7 | track, untrack, files, send, ask, answer, check_answer | Multi-session file coordination + messaging |
444
457
  | Reminders | 5 | list, create, update, complete, delete | User's tasks and deadlines |
445
458
  | Followups | 4 | create, update, complete, delete | System's autonomous verification tasks |
@@ -455,6 +468,7 @@ That's it. No need to run `claude` manually. Your operator will greet you immedi
455
468
  | Adaptive & Somatic | 4 | adaptive_weights, adaptive_override, somatic_check, somatic_stats | Learned signal weights + pain memory per file |
456
469
  | Knowledge Graph | 4 | kg_query, kg_path, kg_neighbors, kg_stats | Bi-temporal entity-relationship graph |
457
470
  | Context Continuity | 2 | checkpoint_save, checkpoint_read | Auto-compaction session preservation |
471
+ | Claim Graph | — | (internal) | Atomic facts with provenance and contradiction detection |
458
472
 
459
473
  ### Plugin System
460
474
 
@@ -512,7 +526,7 @@ NEXO Brain is designed as an MCP server. Claude Code is the primary supported cl
512
526
  npx nexo-brain
513
527
  ```
514
528
 
515
- All 111+ tools are available immediately after installation. The installer configures Claude Code's `~/.claude/settings.json` automatically.
529
+ All 100+ tools are available immediately after installation. The installer configures Claude Code's `~/.claude/settings.json` automatically.
516
530
 
517
531
  ### OpenClaw
518
532
 
@@ -591,11 +605,11 @@ NEXO Brain builds on ideas from several open-source projects. We're grateful for
591
605
 
592
606
  | Project | Inspired Features |
593
607
  |---------|------------------|
594
- | [Vestige](https://github.com/pchaganti/gx-vestige) | HyDE query expansion, spreading activation, prediction error gating, memory dreaming, prospective memory |
595
- | [ShieldCortex](https://github.com/PShieldCortex/ShieldCortex) | Security pipeline (4-layer memory poisoning defense) |
596
- | [Bicameral](https://github.com/nicobailey/Bicameral) | Quarantine queue (trust promotion policy for new facts) |
597
- | [claude-mem](https://github.com/nicobailey/claude-mem) | Hook auto-capture (extracting decisions and facts from conversations) |
598
- | [ClawMem](https://github.com/nicobailey/ClawMem) | Co-activation reinforcement (memories retrieved together strengthen connections) |
608
+ | Vestige | HyDE query expansion, spreading activation, prediction error gating, memory dreaming, prospective memory |
609
+ | ShieldCortex | Security pipeline (4-layer memory poisoning defense) |
610
+ | Bicameral | Quarantine queue (trust promotion policy for new facts) |
611
+ | claude-mem | Hook auto-capture (extracting decisions and facts from conversations) |
612
+ | ClawMem | Co-activation reinforcement (memories retrieved together strengthen connections) |
599
613
 
600
614
  ## Support the Project
601
615
 
@@ -610,6 +624,43 @@ If NEXO Brain is useful to you, consider:
610
624
 
611
625
  ## Changelog
612
626
 
627
+ ### v1.5.0 — Modular Core + Knowledge Graph Search (2026-03-29)
628
+ - **Architecture**: `db.py` refactored into `db/` package (11 modules: core, schema, sessions, learnings, episodic, credentials, entities, evolution, fts, reminders, tasks)
629
+ - **Architecture**: `cognitive.py` refactored into `cognitive/` package (6 modules: core, memory, ingest, search, decay, trust)
630
+ - **KG Boost**: Knowledge Graph connection count now influences search result ranking — well-connected entities surface higher in retrieval
631
+ - **HNSW Vector Index**: Optional approximate nearest neighbor acceleration (activates automatically above 10,000 memories, falls back to brute-force otherwise)
632
+ - **Claim Graph**: Decomposes blob memories into atomic verifiable facts with provenance, confidence scores, and contradiction detection
633
+ - **Inter-terminal Auto-inbox (D+)**: `nexo_startup` now accepts `claude_session_id` (Claude Code session UUID) — enables automatic inbox delivery between parallel terminals via PostToolUse hook + migration v13
634
+ - **Tests**: 24 pytest tests across 3 suites (cognitive, knowledge graph, migrations)
635
+
636
+ ### v1.4.1 — Multi-AI Code Review (2026-03-29)
637
+ - **Fix**: 3 bugs found by GPT-5.4 (Codex CLI) + Gemini 2.5 (Gemini CLI) reviewing full codebase
638
+ - `session_diaries` → `session_diary` table name (smart startup silently failed)
639
+ - Quarantine contradiction logic distinguished confirmation from opposition
640
+ - Knowledge Graph timezone import crash
641
+ - **Security**: Memory sanitization prevents prompt injection via stored content
642
+ - **Migration #13**: Normalizes legacy status values (PENDIENTE→PENDING) on upgrade
643
+
644
+ ### v1.4.0 — The Brain Dreams (2026-03-29)
645
+ - **Major**: All 9 nightly scripts migrated from Python word-overlap to CLI wrapper pattern
646
+ - Postmortem consolidator: opus understands patterns by meaning, not word overlap
647
+ - Sleep system: detects real duplicates via semantic understanding
648
+ - Daily synthesis: opus prioritizes what matters for tomorrow
649
+ - Self-audit: interprets findings for root cause analysis
650
+ - Evolution: prompt reduced 95% (45K → 2.2K chars) — CLI investigates using tools
651
+ - **Stop Hook v8**: Session-scoped tool counting (not day-wide), buffer fallback removed
652
+ - **Guard**: Behavioral rules section surfaces most-violated rules at session start
653
+ - **Followup hygiene**: Weekly cleanup script normalizes statuses, flags stale items
654
+ - 8 missing core scripts + 1 plugin added to repository
655
+
656
+ ### v1.3.0 — Evolution System (2026-03-28)
657
+ - **New**: Self-improvement cycle — NEXO proposes and applies improvements weekly
658
+ - Dual-mode: auto (low-risk changes) and review (owner approval required)
659
+ - Circuit breaker, snapshot/rollback, immutable file protection
660
+
661
+ ### v1.2.3 — AGPL-3.0 License (2026-03-27)
662
+ - License changed from MIT to AGPL-3.0
663
+
613
664
  ### v1.2.1 — Stop Hook Hotfix (2026-03-27)
614
665
  - **Fix**: v1.2.0 deleted the flag on approve, causing infinite block loops if session didn't close immediately
615
666
  - **Fix**: Removed TTL on flag — it persists until SessionStart cleans it up next session
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "1.4.1",
3
+ "version": "1.5.1",
4
4
  "mcpName": "io.github.wazionapps/nexo",
5
- "description": "NEXO — Cognitive co-operator for Claude Code. Atkinson-Shiffrin memory, semantic RAG, trust scoring, and metacognitive error prevention.",
5
+ "description": "NEXO — Cognitive co-operator for Claude Code. Atkinson-Shiffrin memory, semantic RAG, knowledge graph, HNSW vector indexing, trust scoring, and metacognitive error prevention.",
6
6
  "bin": {
7
7
  "nexo-brain": "./bin/nexo-brain.js"
8
8
  },
@@ -14,6 +14,9 @@
14
14
  "ai-assistant",
15
15
  "vector-search",
16
16
  "atkinson-shiffrin",
17
+ "knowledge-graph",
18
+ "hnsw",
19
+ "claim-graph",
17
20
  "openclaw",
18
21
  "openclaw-plugin"
19
22
  ],
@@ -30,6 +33,7 @@
30
33
  "bin/",
31
34
  "src/",
32
35
  "templates/",
33
- "scripts/"
36
+ "scripts/",
37
+ "tests/"
34
38
  ]
35
39
  }
@@ -0,0 +1,323 @@
1
+ """NEXO Claim Graph — Atomic claims with provenance and contradiction detection.
2
+
3
+ Decomposes blob memories into individual verifiable facts. Each claim has a
4
+ source memory, confidence score, and can be linked to contradicting claims.
5
+
6
+ Tables (created in cognitive.db alongside KG):
7
+ - claims: atomic facts with provenance
8
+ - claim_links: relationships between claims (supports, contradicts, refines)
9
+ """
10
+
11
+ import json
12
+ import sqlite3
13
+ import numpy as np
14
+ from datetime import datetime, timezone
15
+ from typing import Optional
16
+
17
+
18
+ def _get_db():
19
+ """Get cognitive.db connection."""
20
+ import cognitive
21
+ return cognitive._get_db()
22
+
23
+
24
+ def _embed(text: str) -> np.ndarray:
25
+ import cognitive
26
+ return cognitive.embed(text)
27
+
28
+
29
+ def _cosine_similarity(a, b) -> float:
30
+ import cognitive
31
+ return cognitive.cosine_similarity(a, b)
32
+
33
+
34
+ def _array_to_blob(arr: np.ndarray) -> bytes:
35
+ return arr.astype(np.float32).tobytes()
36
+
37
+
38
+ def _blob_to_array(blob: bytes) -> np.ndarray:
39
+ return np.frombuffer(blob, dtype=np.float32)
40
+
41
+
42
+ def init_tables():
43
+ """Create claim graph tables if they don't exist."""
44
+ db = _get_db()
45
+ db.executescript("""
46
+ CREATE TABLE IF NOT EXISTS claims (
47
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
48
+ text TEXT NOT NULL,
49
+ embedding BLOB,
50
+ source_type TEXT NOT NULL DEFAULT '',
51
+ source_id TEXT NOT NULL DEFAULT '',
52
+ source_memory_store TEXT DEFAULT '',
53
+ source_memory_id INTEGER DEFAULT 0,
54
+ confidence REAL DEFAULT 1.0,
55
+ verification_status TEXT DEFAULT 'unverified',
56
+ verified_at TEXT,
57
+ domain TEXT DEFAULT '',
58
+ created_at TEXT DEFAULT (datetime('now')),
59
+ updated_at TEXT DEFAULT (datetime('now'))
60
+ );
61
+
62
+ CREATE TABLE IF NOT EXISTS claim_links (
63
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
64
+ source_claim_id INTEGER NOT NULL REFERENCES claims(id),
65
+ target_claim_id INTEGER NOT NULL REFERENCES claims(id),
66
+ relation TEXT NOT NULL,
67
+ confidence REAL DEFAULT 1.0,
68
+ created_at TEXT DEFAULT (datetime('now')),
69
+ UNIQUE(source_claim_id, target_claim_id, relation)
70
+ );
71
+
72
+ CREATE INDEX IF NOT EXISTS idx_claims_source ON claims(source_type, source_id);
73
+ CREATE INDEX IF NOT EXISTS idx_claims_domain ON claims(domain);
74
+ CREATE INDEX IF NOT EXISTS idx_claims_status ON claims(verification_status);
75
+ CREATE INDEX IF NOT EXISTS idx_claim_links_source ON claim_links(source_claim_id);
76
+ CREATE INDEX IF NOT EXISTS idx_claim_links_target ON claim_links(target_claim_id);
77
+ """)
78
+ db.commit()
79
+
80
+
81
+ def add_claim(text: str, source_type: str = "", source_id: str = "",
82
+ source_memory_store: str = "", source_memory_id: int = 0,
83
+ confidence: float = 1.0, domain: str = "") -> dict:
84
+ """Add an atomic claim to the graph.
85
+
86
+ Returns the claim dict with id, or existing claim if duplicate detected.
87
+ """
88
+ db = _get_db()
89
+ init_tables()
90
+
91
+ # Embed the claim
92
+ vec = _embed(text)
93
+ blob = _array_to_blob(vec)
94
+
95
+ # Check for near-duplicate claims (similarity > 0.92)
96
+ existing = find_similar_claims(text, threshold=0.92, limit=1)
97
+ if existing:
98
+ # Update confidence if new source provides additional evidence
99
+ dup = existing[0]
100
+ new_conf = min(1.0, dup["confidence"] + 0.1)
101
+ db.execute("UPDATE claims SET confidence = ?, updated_at = datetime('now') WHERE id = ?",
102
+ (new_conf, dup["id"]))
103
+ db.commit()
104
+ return {"id": dup["id"], "action": "merged", "confidence": new_conf}
105
+
106
+ cursor = db.execute(
107
+ """INSERT INTO claims (text, embedding, source_type, source_id,
108
+ source_memory_store, source_memory_id, confidence, domain)
109
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
110
+ (text, blob, source_type, source_id, source_memory_store,
111
+ source_memory_id, confidence, domain)
112
+ )
113
+ db.commit()
114
+ return {"id": cursor.lastrowid, "action": "added", "confidence": confidence}
115
+
116
+
117
+ def find_similar_claims(text: str, threshold: float = 0.8, limit: int = 10) -> list[dict]:
118
+ """Find claims similar to the given text using cosine similarity."""
119
+ db = _get_db()
120
+ init_tables()
121
+
122
+ query_vec = _embed(text)
123
+ if np.linalg.norm(query_vec) == 0:
124
+ return []
125
+
126
+ rows = db.execute("SELECT * FROM claims").fetchall()
127
+ results = []
128
+ for row in rows:
129
+ if not row["embedding"]:
130
+ continue
131
+ vec = _blob_to_array(row["embedding"])
132
+ score = _cosine_similarity(query_vec, vec)
133
+ if score >= threshold:
134
+ d = dict(row)
135
+ d.pop("embedding", None)
136
+ d["similarity"] = round(score, 4)
137
+ results.append(d)
138
+
139
+ results.sort(key=lambda x: x["similarity"], reverse=True)
140
+ return results[:limit]
141
+
142
+
143
+ def link_claims(source_id: int, target_id: int, relation: str,
144
+ confidence: float = 1.0) -> dict:
145
+ """Create a relationship between two claims.
146
+
147
+ Relations:
148
+ - 'supports': source provides evidence for target
149
+ - 'contradicts': source contradicts target
150
+ - 'refines': source is a more specific version of target
151
+ - 'supersedes': source replaces target (target is outdated)
152
+ """
153
+ db = _get_db()
154
+ init_tables()
155
+
156
+ valid_relations = {"supports", "contradicts", "refines", "supersedes"}
157
+ if relation not in valid_relations:
158
+ return {"error": f"Invalid relation. Use: {valid_relations}"}
159
+
160
+ try:
161
+ db.execute(
162
+ "INSERT OR IGNORE INTO claim_links (source_claim_id, target_claim_id, relation, confidence) "
163
+ "VALUES (?, ?, ?, ?)",
164
+ (source_id, target_id, relation, confidence)
165
+ )
166
+ db.commit()
167
+
168
+ # If contradiction, update verification status
169
+ if relation == "contradicts":
170
+ db.execute(
171
+ "UPDATE claims SET verification_status = 'contradicted', updated_at = datetime('now') WHERE id = ?",
172
+ (target_id,)
173
+ )
174
+ db.commit()
175
+
176
+ return {"source": source_id, "target": target_id, "relation": relation}
177
+ except Exception as e:
178
+ return {"error": str(e)}
179
+
180
+
181
+ def detect_contradictions(claim_id: int) -> list[dict]:
182
+ """Find claims that potentially contradict the given claim."""
183
+ db = _get_db()
184
+ init_tables()
185
+
186
+ claim = db.execute("SELECT * FROM claims WHERE id = ?", (claim_id,)).fetchone()
187
+ if not claim:
188
+ return []
189
+
190
+ claim_vec = _blob_to_array(claim["embedding"])
191
+
192
+ # Find similar claims from different sources
193
+ rows = db.execute(
194
+ "SELECT * FROM claims WHERE id != ? AND domain = ?",
195
+ (claim_id, claim["domain"])
196
+ ).fetchall()
197
+
198
+ contradictions = []
199
+ for row in rows:
200
+ if not row["embedding"]:
201
+ continue
202
+ vec = _blob_to_array(row["embedding"])
203
+ sim = _cosine_similarity(claim_vec, vec)
204
+ # High similarity but different source = potential contradiction
205
+ if 0.6 <= sim <= 0.9 and row["source_id"] != claim["source_id"]:
206
+ d = dict(row)
207
+ d.pop("embedding", None)
208
+ d["similarity"] = round(sim, 4)
209
+ contradictions.append(d)
210
+
211
+ return contradictions
212
+
213
+
214
+ def verify_claim(claim_id: int, status: str = "confirmed") -> dict:
215
+ """Update verification status of a claim.
216
+
217
+ Status: 'confirmed', 'contradicted', 'outdated', 'unverified'
218
+ """
219
+ db = _get_db()
220
+ init_tables()
221
+
222
+ valid = {"confirmed", "contradicted", "outdated", "unverified"}
223
+ if status not in valid:
224
+ return {"error": f"Invalid status. Use: {valid}"}
225
+
226
+ db.execute(
227
+ "UPDATE claims SET verification_status = ?, verified_at = datetime('now'), "
228
+ "updated_at = datetime('now') WHERE id = ?",
229
+ (status, claim_id)
230
+ )
231
+ db.commit()
232
+ row = db.execute("SELECT * FROM claims WHERE id = ?", (claim_id,)).fetchone()
233
+ if row:
234
+ d = dict(row)
235
+ d.pop("embedding", None)
236
+ return d
237
+ return {"error": f"Claim {claim_id} not found"}
238
+
239
+
240
+ def get_claim(claim_id: int) -> Optional[dict]:
241
+ """Get a single claim with its links."""
242
+ db = _get_db()
243
+ init_tables()
244
+
245
+ row = db.execute("SELECT * FROM claims WHERE id = ?", (claim_id,)).fetchone()
246
+ if not row:
247
+ return None
248
+
249
+ d = dict(row)
250
+ d.pop("embedding", None)
251
+
252
+ # Get links
253
+ outgoing = db.execute(
254
+ "SELECT cl.*, c.text as target_text FROM claim_links cl "
255
+ "JOIN claims c ON c.id = cl.target_claim_id "
256
+ "WHERE cl.source_claim_id = ?", (claim_id,)
257
+ ).fetchall()
258
+ incoming = db.execute(
259
+ "SELECT cl.*, c.text as source_text FROM claim_links cl "
260
+ "JOIN claims c ON c.id = cl.source_claim_id "
261
+ "WHERE cl.target_claim_id = ?", (claim_id,)
262
+ ).fetchall()
263
+
264
+ d["links_out"] = [dict(r) for r in outgoing]
265
+ d["links_in"] = [dict(r) for r in incoming]
266
+ return d
267
+
268
+
269
+ def search_claims(query: str = "", domain: str = "", status: str = "",
270
+ limit: int = 20) -> list[dict]:
271
+ """Search claims by text, domain, and/or status."""
272
+ db = _get_db()
273
+ init_tables()
274
+
275
+ if query:
276
+ return find_similar_claims(query, threshold=0.5, limit=limit)
277
+
278
+ conditions = []
279
+ params = []
280
+ if domain:
281
+ conditions.append("domain = ?")
282
+ params.append(domain)
283
+ if status:
284
+ conditions.append("verification_status = ?")
285
+ params.append(status)
286
+
287
+ where = " AND ".join(conditions) if conditions else "1=1"
288
+ rows = db.execute(
289
+ f"SELECT id, text, source_type, source_id, confidence, verification_status, "
290
+ f"domain, created_at FROM claims WHERE {where} ORDER BY created_at DESC LIMIT ?",
291
+ params + [limit]
292
+ ).fetchall()
293
+ return [dict(r) for r in rows]
294
+
295
+
296
+ def stats() -> dict:
297
+ """Claim graph statistics."""
298
+ db = _get_db()
299
+ init_tables()
300
+
301
+ total = db.execute("SELECT COUNT(*) FROM claims").fetchone()[0]
302
+ by_status = {}
303
+ for row in db.execute(
304
+ "SELECT verification_status, COUNT(*) as cnt FROM claims GROUP BY verification_status"
305
+ ).fetchall():
306
+ by_status[row["verification_status"]] = row["cnt"]
307
+ by_domain = {}
308
+ for row in db.execute(
309
+ "SELECT domain, COUNT(*) as cnt FROM claims WHERE domain != '' GROUP BY domain ORDER BY cnt DESC LIMIT 10"
310
+ ).fetchall():
311
+ by_domain[row["domain"]] = row["cnt"]
312
+ links = db.execute("SELECT COUNT(*) FROM claim_links").fetchone()[0]
313
+ contradictions = db.execute(
314
+ "SELECT COUNT(*) FROM claim_links WHERE relation = 'contradicts'"
315
+ ).fetchone()[0]
316
+
317
+ return {
318
+ "total_claims": total,
319
+ "by_status": by_status,
320
+ "by_domain": by_domain,
321
+ "total_links": links,
322
+ "contradictions": contradictions,
323
+ }
@@ -0,0 +1,62 @@
1
+ """NEXO Cognitive Engine — Modular vector memory with Atkinson-Shiffrin model.
2
+
3
+ This package replaces the monolithic cognitive.py. All public functions and
4
+ constants are re-exported here for full backwards compatibility:
5
+ import cognitive
6
+ cognitive.search("query")
7
+ cognitive.embed("text")
8
+ """
9
+
10
+ # Core: DB, embedding, cosine, constants, tables, redaction
11
+ from cognitive._core import (
12
+ COGNITIVE_DB, EMBEDDING_DIM, LAMBDA_STM, LAMBDA_LTM,
13
+ PE_GATE_REJECT, PE_GATE_REFINE, _gate_stats,
14
+ DISCRIMINATING_ENTITIES,
15
+ POSITIVE_SIGNALS, NEGATIVE_SIGNALS, URGENCY_SIGNALS,
16
+ _get_db, _init_tables, _migrate_lifecycle, _migrate_co_activation,
17
+ _auto_migrate_embeddings,
18
+ _get_model, _get_reranker, rerank_results,
19
+ embed, cosine_similarity, _array_to_blob, _blob_to_array,
20
+ extract_temporal_date, redact_secrets,
21
+ )
22
+
23
+ # Search
24
+ from cognitive._search import (
25
+ search, bm25_search, hyde_expand_query,
26
+ record_co_activation,
27
+ _kg_boost_results, _apply_temporal_boost,
28
+ create_trigger, check_triggers, list_triggers, delete_trigger, rearm_trigger,
29
+ # Constants
30
+ CO_ACTIVATION_DECAY, CO_ACTIVATION_BOOST, CO_ACTIVATION_MIN_STRENGTH,
31
+ )
32
+
33
+ # Ingest
34
+ from cognitive._ingest import (
35
+ ingest, ingest_session, ingest_to_ltm, ingest_sensory,
36
+ prediction_error_gate, get_gate_stats, detect_patterns,
37
+ process_quarantine, quarantine_list, quarantine_promote, quarantine_reject, quarantine_stats,
38
+ security_scan,
39
+ )
40
+
41
+ # Decay and maintenance
42
+ from cognitive._decay import (
43
+ apply_decay, promote_stm_to_ltm, gc_stm, gc_test_memories,
44
+ gc_sensory, gc_ltm_dormant, dream_cycle,
45
+ )
46
+
47
+ # Trust and sentiment
48
+ from cognitive._trust import (
49
+ get_trust_events, auto_detect_trust_events,
50
+ detect_sentiment, log_sentiment,
51
+ get_trust_score, adjust_trust, get_trust_history,
52
+ detect_dissonance, resolve_dissonance, check_correction_fatigue,
53
+ )
54
+
55
+ # Memory operations
56
+ from cognitive._memory import (
57
+ format_results, get_metrics, check_repeat_errors, rehearse_by_content,
58
+ consolidate_semantic, get_siblings,
59
+ get_stats, set_lifecycle, auto_merge_duplicates,
60
+ somatic_accumulate, somatic_guard_decay, somatic_nightly_decay,
61
+ somatic_project_events, somatic_get_risk, somatic_top_risks,
62
+ )