nexo-brain 0.10.0-beta.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # NEXO Brain — Your AI Gets a Brain
2
2
 
3
- [![npm v0.10.0-beta.8](https://img.shields.io/npm/v/nexo-brain?label=npm&color=purple)](https://www.npmjs.com/package/nexo-brain)
3
+ [![npm v1.1.0](https://img.shields.io/npm/v/nexo-brain?label=npm&color=purple)](https://www.npmjs.com/package/nexo-brain)
4
4
  [![F1 0.588 on LoCoMo](https://img.shields.io/badge/LoCoMo_F1-0.588-brightgreen)](https://github.com/wazionapps/nexo/blob/main/benchmarks/locomo/results/)
5
5
  [![+55% vs GPT-4](https://img.shields.io/badge/vs_GPT--4-%2B55%25-blue)](https://github.com/snap-research/locomo/issues/33)
6
6
  [![GitHub stars](https://img.shields.io/github/stars/wazionapps/nexo?style=social)](https://github.com/wazionapps/nexo/stargazers)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
 
9
- > **v0.10.0-beta.8** — 30 Core System Rules (versioned, with migration). Battle-tested behavioral rules from 6 months production use, validated via multi-AI debate (Claude + GPT-4o). 6 categories: Integrity, Execution, Memory, Delegation, Communication, Proactivity. 25 BLOCKING + 5 ADVISORY. Plus: Smart Startup, Context Packets, Auto-Prime, and zero-decay pinning.
9
+ > **v1.1.0** — Context Continuity via auto-compaction hooks. PreCompact saves a full session checkpoint; PostCompact re-injects it so long sessions (8+ hours) feel like one continuous conversation. Plus: Cognitive Cortex, 30 Core Rules as DNA, Smart Startup, Context Packets, Auto-Prime. The first AI memory system with architectural inhibitory control — the agent reasons about whether to act before acting. Battle-tested from 6 months of production use, validated via multi-AI debate (Claude Opus + GPT-5.4 + Gemini 3.1 Pro).
10
10
 
11
11
  **NEXO Brain transforms any MCP-compatible AI agent from a stateless assistant into a cognitive partner that remembers, learns, forgets, adapts, and builds a relationship with you over time.**
12
12
 
@@ -136,9 +136,69 @@ Like a human brain, NEXO Brain has automated processes that run while you're not
136
136
 
137
137
  If your Mac was asleep during any scheduled process, NEXO Brain catches up in order when it wakes.
138
138
 
139
+ ## Cognitive Cortex (v1.0.0)
140
+
141
+ The Cortex is a middleware cognitive layer that makes the agent **think before acting**. It implements architectural inhibitory control — the agent cannot bypass reasoning.
142
+
143
+ ```
144
+ User message → Fast Path check → Simple chat? → Respond directly
145
+ → Action needed? → Cortex activates
146
+
147
+ Generate cognitive state
148
+ (goal, plan, unknowns, evidence)
149
+
150
+ Middleware validates
151
+ ├─ Unknowns? → ASK mode (tools blocked)
152
+ ├─ No plan? → PROPOSE mode (read-only)
153
+ └─ Plan + evidence → ACT mode (full access)
154
+ ```
155
+
156
+ | Feature | What It Does |
157
+ |---------|-------------|
158
+ | **Inhibitory Control** | Physically restricts tools based on reasoning quality. Unknowns → can only ask. No plan → can only propose. Evidence + verification → can act. |
159
+ | **Event-Driven Activation** | Only activates on tool intent, ambiguity, destructive actions, or retries. Simple chat has zero overhead. |
160
+ | **Trust-Gated Escalation** | Low trust score → requires more evidence before allowing "act" mode. Trust builds through successful execution. |
161
+ | **Core Rules Injection** | Automatically surfaces relevant behavioral rules based on task type. |
162
+ | **Activation Metrics** | Tracks modes, inhibition rates, and task types for continuous improvement. |
163
+
164
+ The Cortex was designed through a 3-way AI debate (Claude Opus 4.6 + GPT-5.4 + Gemini 3.1 Pro) and validated against 6 months of real production failures.
165
+
166
+ ## Context Continuity (Auto-Compaction) (v1.1.0)
167
+
168
+ NEXO Brain automatically preserves session context when Claude Code compacts conversations. Using PreCompact and PostCompact hooks:
169
+
170
+ - **PreCompact**: Saves a complete session checkpoint to SQLite (task, files, decisions, errors, reasoning thread, next step)
171
+ - **PostCompact**: Re-injects a structured Core Memory Block into the conversation, so the session continues seamlessly
172
+
173
+ This means long sessions (8+ hours) feel like one continuous conversation instead of restarting after each compaction.
174
+
175
+ **How it works:**
176
+ 1. Configure the hooks in your Claude Code `settings.json`
177
+ 2. NEXO Brain's heartbeat automatically maintains the checkpoint
178
+ 3. When compaction happens, the PreCompact hook reads the checkpoint and injects a recovery block
179
+ 4. The session continues from exactly where it left off
180
+
181
+ **Setup:**
182
+ ```json
183
+ {
184
+ "hooks": {
185
+ "PreCompact": [{
186
+ "matcher": "*",
187
+ "hooks": [{"type": "command", "command": "bash path/to/nexo/src/hooks/pre-compact.sh", "timeout": 10}]
188
+ }],
189
+ "PostCompact": [{
190
+ "matcher": "*",
191
+ "hooks": [{"type": "command", "command": "bash path/to/nexo/src/hooks/post-compact.sh", "timeout": 10}]
192
+ }]
193
+ }
194
+ }
195
+ ```
196
+
197
+ 2 new MCP tools: `nexo_checkpoint_save` (manual or hook-triggered checkpoint), `nexo_checkpoint_read` (retrieves the latest checkpoint for context injection).
198
+
139
199
  ## Cognitive Features
140
200
 
141
- NEXO Brain provides 27 cognitive tools on top of the 76 base tools, totaling **111+ MCP tools**. These features implement cognitive science concepts that go beyond basic memory:
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:
142
202
 
143
203
  ### Input Pipeline
144
204
 
@@ -211,7 +271,7 @@ Full results in [`benchmarks/locomo/results/`](benchmarks/locomo/results/).
211
271
 
212
272
  Memory alone doesn't make a co-operator. What makes the difference is the **behavioral loop** — the automated discipline that ensures every session starts informed, runs with guardrails, and ends with self-reflection.
213
273
 
214
- ### 5 Automated Hooks
274
+ ### 6 Automated Hooks
215
275
 
216
276
  These fire automatically at key moments in every Claude Code session:
217
277
 
@@ -220,7 +280,8 @@ These fire automatically at key moments in every Claude Code session:
220
280
  | **SessionStart** | Session opens | Generates a briefing from SQLite: overdue reminders, today's tasks, pending followups, active sessions |
221
281
  | **Stop** | Session ends | Mandatory post-mortem: self-critique (5 questions), session buffer entry, followup creation, proactive seeds for next session |
222
282
  | **PostToolUse** | After each tool call | Captures meaningful mutations to the Sensory Register |
223
- | **PreCompact** | Before context compression | Saves checkpoint, reminds operator to write diary prevents losing the thread |
283
+ | **PreCompact** | Before context compression | Saves full session checkpoint to SQLitetask, files, decisions, errors, reasoning thread |
284
+ | **PostCompact** | After context compression | Re-injects Core Memory Block so the session continues seamlessly from where it left off |
224
285
  | **Caffeinate** | Always (optional) | Keeps Mac awake for nocturnal cognitive processes |
225
286
 
226
287
  ### The Session Lifecycle
@@ -236,7 +297,9 @@ Heartbeat on every interaction (sentiment, context shifts)
236
297
 
237
298
  Guard check before every code edit
238
299
 
239
- PreCompact hook saves context if conversation is compressed
300
+ PreCompact hook saves full checkpoint if conversation is compressed
301
+
302
+ PostCompact hook re-injects Core Memory Block → session continues seamlessly
240
303
 
241
304
  Stop hook triggers mandatory post-mortem:
242
305
  - Self-critique: 5 questions about what could be better
@@ -319,7 +382,7 @@ The installer handles everything:
319
382
  - Node.js project detected
320
383
  Configuring MCP server...
321
384
  Setting up automated processes...
322
- 5 automated processes configured.
385
+ 6 automated processes configured.
323
386
  Caffeinate enabled.
324
387
  Generating operator instructions...
325
388
 
@@ -343,25 +406,25 @@ That's it. No need to run `claude` manually. Atlas will greet you immediately
343
406
  | Component | What | Where |
344
407
  |-----------|------|-------|
345
408
  | Cognitive engine | Python: fastembed, numpy, vector search | pip packages |
346
- | MCP server | 109+ tools for memory, cognition, learning, guard | ~/.nexo/ |
409
+ | MCP server | 111+ tools for memory, cognition, learning, guard | ~/.nexo/ |
347
410
  | Plugins | Guard, episodic memory, cognitive memory, entities, preferences | ~/.nexo/plugins/ |
348
- | Hooks (5) | SessionStart briefing, Stop post-mortem, PostToolUse capture, PreCompact checkpoint, Caffeinate | ~/.nexo/hooks/ |
411
+ | Hooks (6) | SessionStart briefing, Stop post-mortem, PostToolUse capture, PreCompact checkpoint, PostCompact recovery, Caffeinate | ~/.nexo/hooks/ |
349
412
  | Reflection engine | Processes session buffer, extracts patterns, updates user model | ~/.nexo/scripts/ |
350
413
  | CLAUDE.md | Complete operator instructions (Codex, hooks, guard, trust, memory) | ~/.claude/CLAUDE.md |
351
414
  | LaunchAgents | Decay, sleep, audit, postmortem, catch-up | ~/Library/LaunchAgents/ |
352
415
  | Auto-update | Checks for new versions at boot | Built into catch-up |
353
- | Claude Code config | MCP server + 5 hooks registered | ~/.claude/settings.json |
416
+ | Claude Code config | MCP server + 6 hooks registered | ~/.claude/settings.json |
354
417
 
355
418
  ### Requirements
356
419
 
357
420
  - **macOS or Linux** (Windows via [WSL](https://learn.microsoft.com/en-us/windows/wsl/install))
358
421
  - **Node.js 18+** (for the installer)
359
- - **Claude Opus (latest version) strongly recommended.** NEXO Brain provides 109+ MCP tools across 19 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 109+ tools without hesitation.
422
+ - **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.
360
423
  - Python 3, Homebrew, and Claude Code are installed automatically if missing.
361
424
 
362
425
  ## Architecture
363
426
 
364
- ### 109+ MCP Tools across 19 Categories
427
+ ### 111+ MCP Tools across 20 Categories
365
428
 
366
429
  | Category | Count | Tools | Purpose |
367
430
  |----------|-------|-------|---------|
@@ -385,6 +448,7 @@ That's it. No need to run `claude` manually. Atlas will greet you immediately
385
448
  | Evolution | 5 | propose, approve, reject, status, history | Self-improvement proposals |
386
449
  | Adaptive & Somatic | 4 | adaptive_weights, adaptive_override, somatic_check, somatic_stats | Learned signal weights + pain memory per file |
387
450
  | Knowledge Graph | 4 | kg_query, kg_path, kg_neighbors, kg_stats | Bi-temporal entity-relationship graph |
451
+ | Context Continuity | 2 | checkpoint_save, checkpoint_read | Auto-compaction session preservation |
388
452
 
389
453
  ### Plugin System
390
454
 
@@ -442,7 +506,7 @@ NEXO Brain is designed as an MCP server. Claude Code is the primary supported cl
442
506
  npx nexo-brain
443
507
  ```
444
508
 
445
- All 109+ tools are available immediately after installation. The installer configures Claude Code's `~/.claude/settings.json` automatically.
509
+ All 111+ tools are available immediately after installation. The installer configures Claude Code's `~/.claude/settings.json` automatically.
446
510
 
447
511
  ### OpenClaw
448
512
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "0.10.0-beta.8",
3
+ "version": "1.1.0",
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 \u2014 Cognitive co-operator for Claude Code. Atkinson-Shiffrin memory, semantic RAG, trust scoring, and metacognitive error prevention.",
6
6
  "bin": {
7
7
  "nexo-brain": "./bin/nexo-brain.js"
8
8
  },
@@ -32,4 +32,4 @@
32
32
  "templates/",
33
33
  "scripts/"
34
34
  ]
35
- }
35
+ }
package/src/db.py CHANGED
@@ -1003,6 +1003,30 @@ def _seed_core_rules(conn):
1003
1003
  conn.commit()
1004
1004
 
1005
1005
 
1006
+ def _m12_session_checkpoints(conn):
1007
+ """Session checkpoints for intelligent auto-compaction.
1008
+
1009
+ PreCompact saves a checkpoint; PostCompact reads it to re-inject a
1010
+ Core Memory Block that preserves continuity after context compression.
1011
+ """
1012
+ conn.execute("""
1013
+ CREATE TABLE IF NOT EXISTS session_checkpoints (
1014
+ sid TEXT PRIMARY KEY,
1015
+ task TEXT DEFAULT '',
1016
+ task_status TEXT DEFAULT 'active',
1017
+ active_files TEXT DEFAULT '[]',
1018
+ current_goal TEXT DEFAULT '',
1019
+ decisions_summary TEXT DEFAULT '',
1020
+ errors_found TEXT DEFAULT '',
1021
+ reasoning_thread TEXT DEFAULT '',
1022
+ next_step TEXT DEFAULT '',
1023
+ compaction_count INTEGER DEFAULT 0,
1024
+ created_at TEXT DEFAULT (datetime('now')),
1025
+ updated_at TEXT DEFAULT (datetime('now'))
1026
+ )
1027
+ """)
1028
+
1029
+
1006
1030
  # Migration registry — APPEND ONLY, never reorder or delete
1007
1031
  MIGRATIONS = [
1008
1032
  (1, "learnings_columns", _m1_learnings_columns),
@@ -1016,6 +1040,7 @@ MIGRATIONS = [
1016
1040
  (9, "maintenance_schedule", _m9_maintenance_schedule),
1017
1041
  (10, "diary_archive", _m10_diary_archive),
1018
1042
  (11, "core_rules", _m11_core_rules),
1043
+ (12, "session_checkpoints", _m12_session_checkpoints),
1019
1044
  ]
1020
1045
 
1021
1046
 
@@ -2814,3 +2839,71 @@ def update_evolution_log_status(log_id: int, status: str, **kwargs):
2814
2839
  vals.append(kwargs[k])
2815
2840
  vals.append(log_id)
2816
2841
  conn.execute(f"UPDATE evolution_log SET {', '.join(sets)} WHERE id = ?", vals)
2842
+
2843
+
2844
+ # ── Session Checkpoint operations ──────────────────────────────────
2845
+
2846
+ def save_checkpoint(sid: str, task: str = '', task_status: str = 'active',
2847
+ active_files: str = '[]', current_goal: str = '',
2848
+ decisions_summary: str = '', errors_found: str = '',
2849
+ reasoning_thread: str = '', next_step: str = '') -> dict:
2850
+ """Save or update a session checkpoint. Called by PreCompact hook."""
2851
+ conn = get_db()
2852
+ # Get current compaction count
2853
+ existing = conn.execute(
2854
+ "SELECT compaction_count FROM session_checkpoints WHERE sid = ?", (sid,)
2855
+ ).fetchone()
2856
+ count = (existing["compaction_count"] + 1) if existing else 0
2857
+
2858
+ conn.execute(
2859
+ """INSERT INTO session_checkpoints
2860
+ (sid, task, task_status, active_files, current_goal,
2861
+ decisions_summary, errors_found, reasoning_thread, next_step,
2862
+ compaction_count, updated_at)
2863
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
2864
+ ON CONFLICT(sid) DO UPDATE SET
2865
+ task = excluded.task,
2866
+ task_status = excluded.task_status,
2867
+ active_files = excluded.active_files,
2868
+ current_goal = excluded.current_goal,
2869
+ decisions_summary = excluded.decisions_summary,
2870
+ errors_found = excluded.errors_found,
2871
+ reasoning_thread = excluded.reasoning_thread,
2872
+ next_step = excluded.next_step,
2873
+ compaction_count = excluded.compaction_count,
2874
+ updated_at = datetime('now')""",
2875
+ (sid, task, task_status, active_files, current_goal,
2876
+ decisions_summary, errors_found, reasoning_thread, next_step, count)
2877
+ )
2878
+ conn.commit()
2879
+ return {"sid": sid, "compaction_count": count}
2880
+
2881
+
2882
+ def read_checkpoint(sid: str = '') -> dict | None:
2883
+ """Read the most recent session checkpoint. If no sid, returns the latest."""
2884
+ conn = get_db()
2885
+ if sid:
2886
+ row = conn.execute(
2887
+ "SELECT * FROM session_checkpoints WHERE sid = ?", (sid,)
2888
+ ).fetchone()
2889
+ else:
2890
+ row = conn.execute(
2891
+ "SELECT * FROM session_checkpoints ORDER BY updated_at DESC LIMIT 1"
2892
+ ).fetchone()
2893
+ return dict(row) if row else None
2894
+
2895
+
2896
+ def increment_compaction_count(sid: str) -> int:
2897
+ """Increment and return the compaction count for a session."""
2898
+ conn = get_db()
2899
+ conn.execute(
2900
+ """UPDATE session_checkpoints
2901
+ SET compaction_count = compaction_count + 1, updated_at = datetime('now')
2902
+ WHERE sid = ?""",
2903
+ (sid,)
2904
+ )
2905
+ conn.commit()
2906
+ row = conn.execute(
2907
+ "SELECT compaction_count FROM session_checkpoints WHERE sid = ?", (sid,)
2908
+ ).fetchone()
2909
+ return row["compaction_count"] if row else 0
@@ -0,0 +1,126 @@
1
+ #!/bin/bash
2
+ # NEXO PostCompact Hook — Re-inject Core Memory Block after compaction
3
+ # Reads the latest session checkpoint from SQLite and generates a structured
4
+ # context block that preserves session continuity.
5
+ set -euo pipefail
6
+
7
+ NEXO_HOME="${NEXO_HOME:-$HOME/.nexo}"
8
+ NEXO_DB="$NEXO_HOME/nexo.db"
9
+ TODAY=$(date +%Y-%m-%d)
10
+ LOG_FILE="$NEXO_HOME/operations/tool-logs/${TODAY}.jsonl"
11
+ LOG_LINES=0
12
+ if [ -f "$LOG_FILE" ]; then
13
+ LOG_LINES=$(wc -l < "$LOG_FILE" | tr -d ' ')
14
+ fi
15
+
16
+ # Read latest checkpoint from SQLite
17
+ if [ -f "$NEXO_DB" ]; then
18
+ CHECKPOINT=$(sqlite3 "$NEXO_DB" "
19
+ SELECT sid, task, task_status, active_files, current_goal,
20
+ decisions_summary, errors_found, reasoning_thread,
21
+ next_step, compaction_count
22
+ FROM session_checkpoints
23
+ ORDER BY updated_at DESC LIMIT 1
24
+ " 2>/dev/null || echo "")
25
+
26
+ if [ -n "$CHECKPOINT" ]; then
27
+ # Parse pipe-separated fields
28
+ SID=$(echo "$CHECKPOINT" | cut -d'|' -f1)
29
+ TASK=$(echo "$CHECKPOINT" | cut -d'|' -f2)
30
+ TASK_STATUS=$(echo "$CHECKPOINT" | cut -d'|' -f3)
31
+ ACTIVE_FILES=$(echo "$CHECKPOINT" | cut -d'|' -f4)
32
+ CURRENT_GOAL=$(echo "$CHECKPOINT" | cut -d'|' -f5)
33
+ DECISIONS=$(echo "$CHECKPOINT" | cut -d'|' -f6)
34
+ ERRORS=$(echo "$CHECKPOINT" | cut -d'|' -f7)
35
+ REASONING=$(echo "$CHECKPOINT" | cut -d'|' -f8)
36
+ NEXT_STEP=$(echo "$CHECKPOINT" | cut -d'|' -f9)
37
+ COMPACT_COUNT=$(echo "$CHECKPOINT" | cut -d'|' -f10)
38
+
39
+ # Increment compaction count
40
+ sqlite3 "$NEXO_DB" "
41
+ UPDATE session_checkpoints
42
+ SET compaction_count = compaction_count + 1, updated_at = datetime('now')
43
+ WHERE sid = '$SID'
44
+ " 2>/dev/null || true
45
+
46
+ # Read diary draft for extra context
47
+ DRAFT=$(sqlite3 "$NEXO_DB" "
48
+ SELECT tasks_seen, last_context_hint
49
+ FROM session_diary_draft
50
+ WHERE sid = '$SID'
51
+ " 2>/dev/null || echo "")
52
+
53
+ TASKS_SEEN=""
54
+ LAST_HINT=""
55
+ if [ -n "$DRAFT" ]; then
56
+ TASKS_SEEN=$(echo "$DRAFT" | cut -d'|' -f1)
57
+ LAST_HINT=$(echo "$DRAFT" | cut -d'|' -f2)
58
+ fi
59
+
60
+ # Build Core Memory Block
61
+ BLOCK="## SESSION CONTINUITY [auto-injected post-compaction #$((COMPACT_COUNT + 1))]"
62
+ BLOCK="$BLOCK\n**Session:** $SID"
63
+ BLOCK="$BLOCK\n**Task:** $TASK (status: $TASK_STATUS)"
64
+
65
+ if [ -n "$CURRENT_GOAL" ] && [ "$CURRENT_GOAL" != "$TASK" ]; then
66
+ BLOCK="$BLOCK\n**Goal:** $CURRENT_GOAL"
67
+ fi
68
+
69
+ if [ -n "$ACTIVE_FILES" ] && [ "$ACTIVE_FILES" != "[]" ]; then
70
+ BLOCK="$BLOCK\n**Files:** $ACTIVE_FILES"
71
+ fi
72
+
73
+ if [ -n "$DECISIONS" ]; then
74
+ BLOCK="$BLOCK\n**Decisions:** $DECISIONS"
75
+ fi
76
+
77
+ if [ -n "$ERRORS" ]; then
78
+ BLOCK="$BLOCK\n**Errors:** $ERRORS"
79
+ fi
80
+
81
+ if [ -n "$NEXT_STEP" ]; then
82
+ BLOCK="$BLOCK\n**Next:** $NEXT_STEP"
83
+ fi
84
+
85
+ if [ -n "$REASONING" ]; then
86
+ BLOCK="$BLOCK\n**Context:** $REASONING"
87
+ fi
88
+
89
+ if [ -n "$LAST_HINT" ]; then
90
+ BLOCK="$BLOCK\n**Last context:** $LAST_HINT"
91
+ fi
92
+
93
+ if [ -n "$TASKS_SEEN" ] && [ "$TASKS_SEEN" != "[]" ]; then
94
+ BLOCK="$BLOCK\n**Session tasks so far:** $TASKS_SEEN"
95
+ fi
96
+
97
+ BLOCK="$BLOCK\n**Tool logs:** ${NEXO_HOME}/operations/tool-logs/${TODAY}.jsonl ($LOG_LINES entries)"
98
+ BLOCK="$BLOCK\n\n**POST-COMPACTION INSTRUCTIONS:**"
99
+ BLOCK="$BLOCK\n1. Call nexo_heartbeat with the SID above to reconnect with the session"
100
+ BLOCK="$BLOCK\n2. If you need specific lost data, query tool logs with jq"
101
+ BLOCK="$BLOCK\n3. Continue the task from where it left off — do NOT start from zero"
102
+ BLOCK="$BLOCK\n4. MCP tools (nexo_*) have all persistent state"
103
+
104
+ # Escape for JSON
105
+ BLOCK_ESCAPED=$(echo -e "$BLOCK" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
106
+
107
+ cat << HOOKEOF
108
+ {
109
+ "systemMessage": $BLOCK_ESCAPED
110
+ }
111
+ HOOKEOF
112
+ else
113
+ # No checkpoint — fallback to basic message
114
+ cat << 'HOOKEOF'
115
+ {
116
+ "systemMessage": "Post-compaction: no prior checkpoint found. Call nexo_heartbeat to reconnect session state."
117
+ }
118
+ HOOKEOF
119
+ fi
120
+ else
121
+ cat << 'HOOKEOF'
122
+ {
123
+ "systemMessage": "Post-compaction: nexo.db not found. Reconnect via nexo_startup."
124
+ }
125
+ HOOKEOF
126
+ fi
@@ -1,65 +1,47 @@
1
1
  #!/bin/bash
2
- # NEXO PreCompact hooksaves context before Claude Code compacts the conversation.
3
- # Compaction loses context silently. This hook ensures the operator writes a checkpoint
4
- # before that happens, and saves the last known state to a recovery file.
2
+ # NEXO PreCompact HookSave checkpoint + inject preservation instructions
3
+ # This runs BEFORE Claude Code compacts. It:
4
+ # 1. Enriches the session checkpoint in SQLite with latest diary draft data
5
+ # 2. Injects a systemMessage telling the operator to save any WIP via MCP tools
5
6
  set -euo pipefail
6
7
 
7
8
  NEXO_HOME="${NEXO_HOME:-$HOME/.nexo}"
8
- NEXO_NAME="${NEXO_NAME:-NEXO}"
9
- CHECKPOINT_FILE="$NEXO_HOME/coordination/pre-compact-checkpoint.json"
9
+ NEXO_DB="$NEXO_HOME/nexo.db"
10
+ TODAY=$(date +%Y-%m-%d)
11
+ LOG_FILE="$NEXO_HOME/operations/tool-logs/${TODAY}.jsonl"
12
+ LOG_LINES=0
13
+ if [ -f "$LOG_FILE" ]; then
14
+ LOG_LINES=$(wc -l < "$LOG_FILE" | tr -d ' ')
15
+ fi
16
+
17
+ # Enrich checkpoint: copy diary draft context into checkpoint if exists
18
+ if [ -f "$NEXO_DB" ]; then
19
+ # Get latest active session's diary draft
20
+ LATEST_SID=$(sqlite3 "$NEXO_DB" "
21
+ SELECT sid FROM sessions ORDER BY last_update_epoch DESC LIMIT 1
22
+ " 2>/dev/null || echo "")
23
+
24
+ if [ -n "$LATEST_SID" ]; then
25
+ # Pull diary draft data into checkpoint
26
+ sqlite3 "$NEXO_DB" "
27
+ INSERT INTO session_checkpoints (sid, task, current_goal, updated_at)
28
+ SELECT s.sid, s.task, COALESCE(d.last_context_hint, s.task), datetime('now')
29
+ FROM sessions s
30
+ LEFT JOIN session_diary_draft d ON d.sid = s.sid
31
+ WHERE s.sid = '$LATEST_SID'
32
+ ON CONFLICT(sid) DO UPDATE SET
33
+ task = excluded.task,
34
+ current_goal = CASE
35
+ WHEN excluded.current_goal != '' THEN excluded.current_goal
36
+ ELSE session_checkpoints.current_goal
37
+ END,
38
+ updated_at = datetime('now')
39
+ " 2>/dev/null || true
40
+ fi
41
+ fi
10
42
 
11
- mkdir -p "$NEXO_HOME/coordination"
12
-
13
- # Save current state to checkpoint file
14
- python3 -c "
15
- import json, os, sys
16
- from datetime import datetime
17
-
18
- nexo_home = os.environ.get('NEXO_HOME', os.path.expanduser('~/.nexo'))
19
- db_path = os.path.join(nexo_home, 'nexo.db')
20
-
21
- checkpoint = {
22
- 'timestamp': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S'),
23
- 'active_sessions': [],
24
- 'last_context_hints': [],
25
- }
26
-
27
- try:
28
- import sqlite3
29
- if os.path.exists(db_path):
30
- db = sqlite3.connect(db_path, timeout=5)
31
- db.row_factory = sqlite3.Row
32
- sessions = db.execute(
33
- 'SELECT sid, task, started FROM sessions WHERE completed=0'
34
- ).fetchall()
35
- checkpoint['active_sessions'] = [
36
- {'sid': s['sid'], 'task': s['task'], 'started': s['started']}
37
- for s in sessions
38
- ]
39
- # Get last diary drafts for context
40
- try:
41
- drafts = db.execute(
42
- 'SELECT sid, last_context_hint, tasks_seen FROM session_diary_draft '
43
- 'ORDER BY updated_at DESC LIMIT 3'
44
- ).fetchall()
45
- checkpoint['last_context_hints'] = [
46
- {'sid': d['sid'], 'hint': d['last_context_hint'], 'tasks': d['tasks_seen']}
47
- for d in drafts
48
- ]
49
- except Exception:
50
- pass
51
- db.close()
52
- except Exception:
53
- pass
54
-
55
- with open('$CHECKPOINT_FILE', 'w') as f:
56
- json.dump(checkpoint, f, indent=2)
57
- " 2>/dev/null || true
58
-
59
- # Emit hook response with systemMessage
60
43
  cat << HOOKEOF
61
44
  {
62
- "decision": "approve",
63
- "systemMessage": "PRE-COMPACT HOOK — Context is about to be compressed. BEFORE continuing:\n\n1. **Write a diary draft NOW** — call nexo_session_diary_write with what you've done so far, decisions made, and current mental state. This is your lifeline after compaction.\n2. **Note your current task** — after compaction you may lose the thread. Write it down in the diary.\n3. **Check pending followups** — if you promised to do something, make sure it's recorded before context is lost.\n4. **Read the checkpoint** after compaction: ${NEXO_HOME}/coordination/pre-compact-checkpoint.json\n\nDo NOT skip this. Compaction without a diary = starting from zero."
45
+ "systemMessage": "CONTEXT IS ABOUT TO BE COMPRESSED.\n\nOBLIGATORY ACTIONS BEFORE COMPACTION:\n1. Save critical state via MCP: nexo_checkpoint_save with current task, active files, decisions, errors, next step, and reasoning thread.\n2. If there is work in progress without a commit, save data via nexo_entity_create, nexo_preference_set, nexo_learning_add, nexo_followup_create.\n3. PERSISTENT TOOL LOGS: ${NEXO_HOME}/operations/tool-logs/${TODAY}.jsonl has ${LOG_LINES} entries.\n4. After compaction, the PostCompact hook will re-inject a Core Memory Block with the checkpoint.\n5. MCP tools (nexo_*) preserve all state — use them to recover context."
64
46
  }
65
47
  HOOKEOF
@@ -0,0 +1,299 @@
1
+ """Cognitive Cortex plugin — middleware cognitive layer for NEXO Brain.
2
+
3
+ Provides structured pre-action reasoning with architectural inhibitory control.
4
+ The Cortex does NOT generate answers — it gates, plans, and validates actions.
5
+
6
+ Activation: event-driven, not on every turn. Only on:
7
+ - Tool intent (edit, execute, delegate)
8
+ - Ambiguity in user request
9
+ - Destructive actions
10
+ - Multi-step tasks
11
+ - Retry after failure
12
+ - Contradictions with known facts
13
+
14
+ v0.1: Single MCP tool + middleware validation.
15
+ """
16
+
17
+ import json
18
+ import time
19
+
20
+
21
+ def _get_db():
22
+ from db import get_db
23
+ return get_db()
24
+
25
+
26
+ def _get_core_rules_for_task(task_type: str) -> list[str]:
27
+ """Get relevant Core Rules for the given task type."""
28
+ conn = _get_db()
29
+ try:
30
+ # Map task type to rule categories
31
+ category_map = {
32
+ "edit": ["integrity", "execution"],
33
+ "execute": ["integrity", "execution", "delegation"],
34
+ "delegate": ["delegation"],
35
+ "analyze": ["execution", "memory"],
36
+ "answer": ["communication"],
37
+ }
38
+ categories = category_map.get(task_type, ["integrity", "execution"])
39
+ placeholders = ",".join("?" * len(categories))
40
+
41
+ rows = conn.execute(
42
+ f"SELECT id, rule FROM core_rules WHERE category IN ({placeholders}) AND is_active = 1 AND type = 'blocking' ORDER BY importance DESC LIMIT 5",
43
+ categories
44
+ ).fetchall()
45
+ return [f"{r['id']}: {r['rule']}" for r in rows]
46
+ except Exception:
47
+ return []
48
+
49
+
50
+ def _get_trust_score() -> float:
51
+ """Get current trust score from cognitive.db."""
52
+ try:
53
+ import cognitive
54
+ return cognitive.get_trust_score()
55
+ except Exception:
56
+ return 50.0
57
+
58
+
59
+ def _validate_state(state: dict) -> dict:
60
+ """Validate cognitive state and determine action mode.
61
+
62
+ Returns dict with: mode, warnings, injected_rules, blocked_reason
63
+ """
64
+ warnings = []
65
+ mode = "act" # default: allow action
66
+ blocked_reason = None
67
+
68
+ task_type = state.get("task_type", "answer")
69
+ plan = state.get("plan", [])
70
+ unknowns = state.get("unknowns", [])
71
+ evidence = state.get("evidence_refs", [])
72
+ verification = state.get("verification_step", "")
73
+ constraints = state.get("constraints", [])
74
+ goal = state.get("goal", "")
75
+
76
+ # === INHIBITION RULES (architectural, not advisory) ===
77
+
78
+ # Rule 1: unknowns exist → force ASK mode
79
+ if unknowns:
80
+ mode = "ask"
81
+ blocked_reason = f"Cannot act with {len(unknowns)} unknown(s). Resolve first."
82
+ warnings.append(f"UNKNOWNS: {', '.join(unknowns[:3])}")
83
+
84
+ # Rule 2: edit/execute without plan → force PROPOSE
85
+ if task_type in ("edit", "execute", "delegate") and not plan and mode == "act":
86
+ mode = "propose"
87
+ blocked_reason = "No plan defined for action task. Propose plan first."
88
+ warnings.append("MISSING PLAN: define steps before executing")
89
+
90
+ # Rule 3: edit/execute without verification → force PROPOSE
91
+ if task_type in ("edit", "execute") and not verification and mode == "act":
92
+ mode = "propose"
93
+ blocked_reason = "No verification step. How will you confirm it worked?"
94
+ warnings.append("MISSING VERIFICATION: define how to verify")
95
+
96
+ # Rule 4: execute without evidence → force PROPOSE
97
+ if task_type == "execute" and not evidence and mode == "act":
98
+ mode = "propose"
99
+ blocked_reason = "No evidence supporting this action."
100
+ warnings.append("MISSING EVIDENCE: what supports this action?")
101
+
102
+ # Rule 5: no goal → force ASK
103
+ if not goal:
104
+ mode = "ask"
105
+ blocked_reason = "No goal defined."
106
+ warnings.append("NO GOAL: what are you trying to achieve?")
107
+
108
+ # === TRUST-BASED ADJUSTMENTS ===
109
+ trust = _get_trust_score()
110
+ if trust < 30 and mode == "act" and task_type in ("edit", "execute"):
111
+ mode = "propose"
112
+ blocked_reason = f"Trust score {trust:.0f}/100 — propose before acting."
113
+ warnings.append(f"LOW TRUST ({trust:.0f}): extra verification required")
114
+
115
+ # === INJECT RELEVANT RULES ===
116
+ rules = _get_core_rules_for_task(task_type)
117
+
118
+ return {
119
+ "mode": mode,
120
+ "tools_available": _tools_for_mode(mode),
121
+ "warnings": warnings,
122
+ "blocked_reason": blocked_reason,
123
+ "injected_rules": rules,
124
+ "trust_score": round(trust),
125
+ }
126
+
127
+
128
+ def _tools_for_mode(mode: str) -> list[str]:
129
+ """Define which tool categories are available per mode."""
130
+ if mode == "ask":
131
+ return ["read", "search", "ask_user"]
132
+ elif mode == "propose":
133
+ return ["read", "search", "analyze", "propose_plan"]
134
+ else: # act
135
+ return ["all"]
136
+
137
+
138
+ def handle_cortex_check(
139
+ goal: str,
140
+ task_type: str = "answer",
141
+ plan: str = "[]",
142
+ known_facts: str = "[]",
143
+ unknowns: str = "[]",
144
+ constraints: str = "[]",
145
+ evidence_refs: str = "[]",
146
+ verification_step: str = "",
147
+ ) -> str:
148
+ """Cognitive Cortex pre-action check. Call BEFORE significant actions.
149
+
150
+ Validates your reasoning state and determines if you can act, should propose,
151
+ or need to ask for clarification first. Implements architectural inhibitory control.
152
+
153
+ WHEN TO CALL:
154
+ - Before editing files or running commands
155
+ - Before delegating to subagents
156
+ - When the task has multiple possible approaches
157
+ - After a failed attempt (before retrying)
158
+ - When user instruction seems to conflict with known facts
159
+
160
+ DO NOT CALL for simple chat responses, greetings, or explanations.
161
+
162
+ Args:
163
+ goal: What you are trying to achieve (required)
164
+ task_type: One of: answer, analyze, edit, execute, delegate
165
+ plan: JSON array of planned steps (e.g. '["read file", "edit function", "test"]')
166
+ known_facts: JSON array of facts you have (from user, memory, files)
167
+ unknowns: JSON array of things you don't know yet but need
168
+ constraints: JSON array of rules or limitations that apply
169
+ evidence_refs: JSON array of evidence supporting your plan (learnings, user statements, file contents)
170
+ verification_step: How you will verify the action worked
171
+
172
+ Returns:
173
+ Mode (ask/propose/act), available tools, warnings, and relevant Core Rules
174
+ """
175
+ # Parse JSON arrays safely
176
+ def _parse(s):
177
+ try:
178
+ v = json.loads(s) if isinstance(s, str) else s
179
+ return v if isinstance(v, list) else []
180
+ except (json.JSONDecodeError, TypeError):
181
+ return []
182
+
183
+ state = {
184
+ "goal": goal.strip() if goal else "",
185
+ "task_type": task_type if task_type in ("answer", "analyze", "edit", "execute", "delegate") else "answer",
186
+ "plan": _parse(plan),
187
+ "known_facts": _parse(known_facts),
188
+ "unknowns": _parse(unknowns),
189
+ "constraints": _parse(constraints),
190
+ "evidence_refs": _parse(evidence_refs),
191
+ "verification_step": verification_step.strip() if verification_step else "",
192
+ }
193
+
194
+ result = _validate_state(state)
195
+
196
+ # Format response
197
+ lines = [
198
+ f"CORTEX CHECK — mode: {result['mode'].upper()}",
199
+ f"Trust: {result['trust_score']}/100",
200
+ ]
201
+
202
+ if result["mode"] == "act":
203
+ lines.append("CLEARED: You may proceed with the action.")
204
+ elif result["mode"] == "propose":
205
+ lines.append(f"PROPOSE ONLY: {result['blocked_reason']}")
206
+ lines.append("Show the user your plan and get approval before executing.")
207
+ elif result["mode"] == "ask":
208
+ lines.append(f"ASK FIRST: {result['blocked_reason']}")
209
+ lines.append("Gather the missing information before proceeding.")
210
+
211
+ if result["warnings"]:
212
+ lines.append("")
213
+ lines.append("Warnings:")
214
+ for w in result["warnings"]:
215
+ lines.append(f" - {w}")
216
+
217
+ if result["injected_rules"]:
218
+ lines.append("")
219
+ lines.append("Applicable Core Rules:")
220
+ for r in result["injected_rules"]:
221
+ lines.append(f" - {r}")
222
+
223
+ lines.append("")
224
+ lines.append(f"Tools available: {', '.join(result['tools_available'])}")
225
+
226
+ # Log cortex activation for metrics
227
+ try:
228
+ conn = _get_db()
229
+ conn.execute(
230
+ """CREATE TABLE IF NOT EXISTS cortex_log (
231
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
232
+ goal TEXT,
233
+ task_type TEXT,
234
+ mode TEXT,
235
+ warnings TEXT,
236
+ trust_score INTEGER,
237
+ created_at TEXT DEFAULT (datetime('now'))
238
+ )"""
239
+ )
240
+ conn.execute(
241
+ "INSERT INTO cortex_log (goal, task_type, mode, warnings, trust_score) VALUES (?, ?, ?, ?, ?)",
242
+ (goal[:200], task_type, result["mode"], json.dumps(result["warnings"]), result["trust_score"])
243
+ )
244
+ conn.commit()
245
+ except Exception:
246
+ pass
247
+
248
+ return "\n".join(lines)
249
+
250
+
251
+ def handle_cortex_stats(days: int = 7) -> str:
252
+ """View Cortex activation statistics — how often it activates, modes, warnings.
253
+
254
+ Args:
255
+ days: Period to analyze (default 7)
256
+ """
257
+ conn = _get_db()
258
+ try:
259
+ conn.execute("SELECT 1 FROM cortex_log LIMIT 1")
260
+ except Exception:
261
+ return "No Cortex data yet. The Cortex activates on significant actions."
262
+
263
+ cutoff = f"datetime('now', '-{days} days')"
264
+
265
+ total = conn.execute(f"SELECT COUNT(*) FROM cortex_log WHERE created_at >= {cutoff}").fetchone()[0]
266
+ by_mode = conn.execute(
267
+ f"SELECT mode, COUNT(*) as c FROM cortex_log WHERE created_at >= {cutoff} GROUP BY mode ORDER BY c DESC"
268
+ ).fetchall()
269
+ by_type = conn.execute(
270
+ f"SELECT task_type, COUNT(*) as c FROM cortex_log WHERE created_at >= {cutoff} GROUP BY task_type ORDER BY c DESC"
271
+ ).fetchall()
272
+
273
+ lines = [
274
+ f"CORTEX STATS — last {days} days",
275
+ f"Total activations: {total}",
276
+ "",
277
+ "By mode:",
278
+ ]
279
+ for r in by_mode:
280
+ pct = (r["c"] / total * 100) if total > 0 else 0
281
+ lines.append(f" {r['mode']}: {r['c']} ({pct:.0f}%)")
282
+
283
+ lines.append("")
284
+ lines.append("By task type:")
285
+ for r in by_type:
286
+ lines.append(f" {r['task_type']}: {r['c']}")
287
+
288
+ # Inhibition rate = % of activations that resulted in ask or propose (not act)
289
+ inhibited = sum(r["c"] for r in by_mode if r["mode"] != "act")
290
+ inhibition_rate = (inhibited / total * 100) if total > 0 else 0
291
+ lines.append(f"\nInhibition rate: {inhibition_rate:.0f}% (target: 30-60%)")
292
+
293
+ return "\n".join(lines)
294
+
295
+
296
+ TOOLS = [
297
+ (handle_cortex_check, "nexo_cortex_check", "Cognitive pre-action check. Validates reasoning and determines if you can act, should propose, or need to ask first. Call before significant actions."),
298
+ (handle_cortex_stats, "nexo_cortex_stats", "View Cortex activation statistics — modes, task types, inhibition rate."),
299
+ ]
@@ -6,7 +6,9 @@
6
6
  "source": "Consolidated from 6 months production use + multi-AI debate (Claude Opus + GPT-4o)",
7
7
  "total_rules": 30,
8
8
  "blocking": 25,
9
- "advisory": 5
9
+ "advisory": 5,
10
+ "immutable": true,
11
+ "immutable_note": "Core rules are the DNA of NEXO Brain. They CANNOT be deleted or modified by the user. Only the migration system (version updates from the creators) can add, modify, or remove rules. Users can configure behavioral intensity (autonomy, communication, proactivity) but not the rules themselves."
10
12
  },
11
13
  "categories": {
12
14
  "integrity": {
package/src/server.py CHANGED
@@ -126,6 +126,77 @@ def nexo_smart_startup() -> str:
126
126
  return handle_smart_startup_query()
127
127
 
128
128
 
129
+ # ── Session Checkpoints (auto-compaction continuity) ──────────────
130
+
131
+ @mcp.tool
132
+ def nexo_checkpoint_save(
133
+ sid: str,
134
+ task: str = '',
135
+ task_status: str = 'active',
136
+ active_files: str = '[]',
137
+ current_goal: str = '',
138
+ decisions_summary: str = '',
139
+ errors_found: str = '',
140
+ reasoning_thread: str = '',
141
+ next_step: str = ''
142
+ ) -> str:
143
+ """Save a session checkpoint for auto-compaction continuity.
144
+
145
+ Call this BEFORE context compaction to preserve session state.
146
+ The PostCompact hook reads this checkpoint and re-injects it as a
147
+ Core Memory Block, so the session continues seamlessly.
148
+
149
+ Args:
150
+ sid: Session ID.
151
+ task: Current task description.
152
+ task_status: One of 'active', 'investigating', 'fixing', 'deploying', 'blocked'.
153
+ active_files: JSON array of file paths currently being worked on.
154
+ current_goal: What you're trying to achieve right now (1-2 sentences).
155
+ decisions_summary: Recent decisions with brief reasoning (2-3 lines).
156
+ errors_found: Errors encountered and their status (resolved/open).
157
+ reasoning_thread: Your current chain of thought (1-2 sentences).
158
+ next_step: The concrete next action to take.
159
+ """
160
+ from db import save_checkpoint
161
+ result = save_checkpoint(
162
+ sid=sid, task=task, task_status=task_status,
163
+ active_files=active_files, current_goal=current_goal,
164
+ decisions_summary=decisions_summary, errors_found=errors_found,
165
+ reasoning_thread=reasoning_thread, next_step=next_step,
166
+ )
167
+ return f"Checkpoint saved for {sid}. Compaction #{result['compaction_count']}. PostCompact will re-inject this as Core Memory Block."
168
+
169
+
170
+ @mcp.tool
171
+ def nexo_checkpoint_read(sid: str = '') -> str:
172
+ """Read the latest session checkpoint. Used by PostCompact hook and for manual recovery.
173
+
174
+ Args:
175
+ sid: Session ID. If empty, returns the most recent checkpoint from any session.
176
+ """
177
+ from db import read_checkpoint
178
+ cp = read_checkpoint(sid)
179
+ if not cp:
180
+ return "No checkpoint found."
181
+
182
+ lines = [f"CHECKPOINT for {cp['sid']} (compaction #{cp['compaction_count']})"]
183
+ lines.append(f"Task: {cp['task']} ({cp['task_status']})")
184
+ if cp.get('current_goal'):
185
+ lines.append(f"Goal: {cp['current_goal']}")
186
+ if cp.get('active_files') and cp['active_files'] != '[]':
187
+ lines.append(f"Files: {cp['active_files']}")
188
+ if cp.get('decisions_summary'):
189
+ lines.append(f"Decisions: {cp['decisions_summary']}")
190
+ if cp.get('errors_found'):
191
+ lines.append(f"Errors: {cp['errors_found']}")
192
+ if cp.get('reasoning_thread'):
193
+ lines.append(f"Context: {cp['reasoning_thread']}")
194
+ if cp.get('next_step'):
195
+ lines.append(f"Next: {cp['next_step']}")
196
+ lines.append(f"Updated: {cp['updated_at']}")
197
+ return "\n".join(lines)
198
+
199
+
129
200
  # ── File coordination (3 tools) ───────────────────────────────────
130
201
 
131
202
  @mcp.tool
@@ -8,6 +8,7 @@ from db import (
8
8
  get_active_sessions, clean_stale_sessions, search_sessions,
9
9
  get_inbox, get_pending_questions, now_epoch,
10
10
  SESSION_STALE_SECONDS, check_session_has_diary,
11
+ save_checkpoint, read_checkpoint, increment_compaction_count,
11
12
  )
12
13
 
13
14
  # ── Session Keepalive ────────────────────────────────────────────────
@@ -317,6 +318,16 @@ def handle_heartbeat(sid: str, task: str, context_hint: str = '') -> str:
317
318
  except Exception:
318
319
  pass # Draft accumulation is best-effort, never block heartbeat
319
320
 
321
+ # Update session checkpoint with current goal (lightweight, every heartbeat)
322
+ try:
323
+ save_checkpoint(
324
+ sid=sid,
325
+ task=task,
326
+ current_goal=context_hint[:300] if context_hint else task,
327
+ )
328
+ except Exception:
329
+ pass # Checkpoint update is best-effort
330
+
320
331
  # Diary reminder: after 30 min active with no diary entry
321
332
  conn = get_db()
322
333
  row = conn.execute("SELECT started_epoch FROM sessions WHERE sid = ?", (sid,)).fetchone()
@@ -58,6 +58,28 @@ _Subagents inherit zero session memory. Without context = guaranteed errors._
58
58
 
59
59
  For the full 30 rules across 6 categories (Integrity, Execution, Memory, Delegation, Communication, Proactivity), call `nexo_rules_check`.
60
60
 
61
+ ## Cognitive Cortex (Pre-Action Reasoning)
62
+
63
+ Before significant actions, call `nexo_cortex_check` to validate your reasoning. The Cortex determines if you can **act**, should **propose**, or need to **ask** first.
64
+
65
+ **WHEN to call `nexo_cortex_check`:**
66
+ - Before editing files or running commands
67
+ - Before delegating to subagents
68
+ - When the task has multiple possible approaches
69
+ - After a failed attempt (before retrying differently)
70
+ - When user instruction conflicts with known facts
71
+
72
+ **DO NOT call** for simple chat, greetings, explanations, or read-only queries.
73
+
74
+ **How it works:** You provide your goal, plan, knowns, unknowns, and evidence. The Cortex validates and returns:
75
+ - **ACT** — proceed with execution (all tools available)
76
+ - **PROPOSE** — show plan to user first (no write/execute tools)
77
+ - **ASK** — gather missing information first (read/search only)
78
+
79
+ The Cortex enforces inhibitory control architecturally — it physically restricts available tools based on your reasoning quality. This prevents premature action, hallucinated certainty, and unverified execution.
80
+
81
+ `nexo_cortex_stats` shows activation metrics and inhibition rates.
82
+
61
83
  ## Personality Calibration
62
84
 
63
85
  Read `{{NEXO_HOME}}/brain/calibration.json` at startup to load user preferences. These override defaults: