eagle-mem 4.7.1 → 4.8.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.
package/README.md CHANGED
@@ -1,16 +1,29 @@
1
1
  ```
2
2
  ======================================
3
3
  Eagle Mem
4
- context that survives /compact
4
+ shared memory | guardrails | lanes
5
5
  ======================================
6
6
  ```
7
7
 
8
8
  # Eagle Mem
9
9
 
10
- **Context that survives `/compact`.**
10
+ **Shared memory, release guardrails, and worker lanes for Claude Code and Codex.**
11
11
 
12
- **v4.7.1 hardens first-class Codex support and enforced anti-regression checks.**
13
- Claude Code and Codex can now share the same local Eagle Mem database, while every captured row records which agent created it.
12
+ Eagle Mem turns AI coding sessions into compounding project knowledge. It gives Claude Code and Codex the same local memory, labels which agent created each memory, blocks risky release commands until affected features are verified, and lets broad work split into durable worker lanes.
13
+
14
+ **v4.8.1 ships the communication refresh for the new architecture:** cross-agent orchestration, compact Codex hook recall, enforced anti-regression checks, configurable RTK token guardrails, and a memory-sync fix for large memory files.
15
+
16
+ **Website:** [Product](https://eagleisbatman.github.io/eagle-mem/) |
17
+ [Architecture](https://eagleisbatman.github.io/eagle-mem/architecture.html) |
18
+ [About](https://eagleisbatman.github.io/eagle-mem/about.html)
19
+
20
+ ## Why People Install It
21
+
22
+ - **Start warmer** - every new session can recall project overviews, decisions, gotchas, summaries, hot files, mirrored memories, plans, and tasks.
23
+ - **Ship safer** - feature-mapped changes create pending verification records, and release-boundary commands stay blocked until the current diff is verified or waived.
24
+ - **Waste fewer tokens** - Eagle Mem injects compact context, nudges duplicate reads, and can route noisy shell output through RTK.
25
+ - **Coordinate agents** - Codex and Claude Code can share one project memory while worker lanes record owner, model, effort, worktree, logs, validation, and handoff.
26
+ - **Stay local** - no daemon, no hosted memory service, no vector database. The core is hooks plus SQLite/FTS5.
14
27
 
15
28
  ## The Problem
16
29
 
@@ -18,9 +31,17 @@ Claude Code and Codex start every session with amnesia. They don't remember what
18
31
 
19
32
  The longer you work with Claude Code, the worse this gets. Projects accumulate history — decisions, gotchas, architectural patterns, feature dependencies — and none of it survives across sessions.
20
33
 
21
- ## The Solution
34
+ ## The Product
35
+
36
+ Eagle Mem is a local runtime layer for AI coding agents. It adds three things that ordinary agent sessions do not have by default:
37
+
38
+ | Layer | What users feel | What Eagle Mem does |
39
+ |-------|-----------------|---------------------|
40
+ | **Recall** | "The agent remembers this repo." | Loads project overviews, summaries, decisions, memories, tasks, plans, and relevant indexed code. |
41
+ | **Guardrails** | "The agent cannot casually undo known decisions or push unverified feature changes." | Surfaces decisions before edits and enforces feature verification on push, PR, and publish boundaries. |
42
+ | **Lanes** | "A big task can survive compaction and split across agents." | Persists orchestrations, worker lanes, worktrees, logs, validation commands, and handoffs. |
22
43
 
23
- Eagle Mem is a recall and regression-control layer for Claude Code and Codex. Every session starts with context from previous sessions — summaries, decisions, memories, tasks, project overviews, and relevant code — injected automatically via hooks. Both agents share the same SQLite database at `~/.eagle-mem/memory.db`, and captured rows are source-attributed as `Claude Code` or `Codex`.
44
+ Both agents share the same SQLite database at `~/.eagle-mem/memory.db`, and captured rows are source-attributed as `Claude Code` or `Codex`.
24
45
 
25
46
  **Zero per-instance overhead.** No daemon, no vector DB, no MCP server. Just bash scripts, sqlite3 (WAL mode, FTS5 full-text search), and jq.
26
47
 
@@ -70,6 +91,7 @@ Hooks fire automatically at different points in the agent lifecycle:
70
91
  | **SessionEnd** | session closes | Re-syncs tasks, marks session completed |
71
92
 
72
93
  Codex shell hooks are registered for `Bash`, `exec_command`, `shell_command`, and `unified_exec` tool names so release-boundary protection works across current Codex shell paths.
94
+ Codex recall is emitted as compact hook JSON, so local Codex sessions get shared memory without the multi-screen hook dumps.
73
95
 
74
96
  ### Background Automation
75
97
 
@@ -85,13 +107,14 @@ These run automatically via SessionStart — no commands needed:
85
107
  Eagle Mem actively reduces token consumption:
86
108
 
87
109
  - **Injection compression** — zero-value stats are elided from the banner, overview is capped, compact reloads get 1 recent session instead of 3
88
- - **Command rewriting** — PreToolUse rewrites noisy Bash commands to pipe through `head -N` via `updatedInput`. Rules are learned by the curator from real usage, not hardcoded.
110
+ - **Command rewriting** — PreToolUse routes noisy shell output through RTK when available and blocks known raw-output commands in enforce mode when RTK is missing.
89
111
  - **Read-after-modify detection** — detects when you read a file that was just edited or written, nudges that the diff is already in context
90
112
  - **Read dedup tracking** — files read 3+ times in a session get a soft nudge
91
113
  - **Co-edit nudges** — learned from observation data: when you edit file X, PreToolUse reminds you that you usually also touch file Y
92
114
  - **Hot file awareness** — curator identifies files read in 50%+ of sessions; SessionStart flags them as "likely in context" to reduce re-reads
93
115
  - **Working set recovery** — on compact, SessionStart injects the files you were actively editing so you resume without re-reading everything
94
116
  - **Stuck loop detection** — if the same file is edited 5+ times in one session, PreToolUse nudges to reconsider the approach
117
+ - **RTK token guard** — optional `rtk` integration can rewrite or block noisy shell commands before raw output enters Claude Code or Codex context. Configure with `eagle-mem config set token_guard.rtk auto|off|enforce`.
95
118
 
96
119
  ### Anti-Regression
97
120
 
@@ -103,6 +126,7 @@ Eagle Mem prevents Claude from repeating past mistakes:
103
126
  - **Gotcha surfacing** — past surprises and gotchas are surfaced when editing related files
104
127
  - **Stale memory detection** — warns when edits may contradict stored memories
105
128
  - **Token guard** — when `rtk` is installed, raw shell output commands are rewritten or blocked with an RTK equivalent so large output is compacted before it enters agent context
129
+ - **Orchestration lanes** — long-running work can be split into durable worker lanes with owners, validation commands, worktree paths, status notes, and handoff output shared by Claude Code and Codex
106
130
 
107
131
  ## Commands
108
132
 
@@ -113,17 +137,22 @@ Eagle Mem prevents Claude from repeating past mistakes:
113
137
  | `eagle-mem uninstall` | Remove hooks and optionally delete data |
114
138
  | `eagle-mem search` | Search past sessions, memories, and code |
115
139
  | `eagle-mem health` | Diagnose pipeline health and background automation |
116
- | `eagle-mem config` | View or change LLM provider settings |
140
+ | `eagle-mem config` | View or change LLM provider and token-guard settings |
117
141
  | `eagle-mem guard` | Manage regression guardrails for files |
118
142
  | `eagle-mem overview` | Build or view project overview |
119
143
  | `eagle-mem memories` | View/sync agent memories |
120
144
  | `eagle-mem tasks` | View mirrored tasks |
145
+ | `eagle-mem orchestrate` | Coordinate durable worker lanes across agents |
121
146
  | `eagle-mem curate` | Run curator (co-edits, hot files, guardrails) |
122
147
  | `eagle-mem feature` | Track and verify features |
123
148
  | `eagle-mem prune` | Clean old sessions and stale data |
124
149
  | `eagle-mem scan` | Scan codebase and generate overview |
125
150
  | `eagle-mem index` | Index source files for FTS5 code search |
126
151
 
152
+ ### v4.8.1 Patch
153
+
154
+ `eagle-mem memories sync` is now safe on large Claude Code/Codex memory files. The memory mirror parser no longer uses early-exit pipelines under `pipefail`, avoiding exit `141` during sync.
155
+
127
156
  ### Search Modes
128
157
 
129
158
  ```bash
@@ -149,6 +178,32 @@ Verification is tied to the current git diff fingerprint. If the same diff was a
149
178
 
150
179
  Dry-run validation stays unblocked. For example, `gh pr create --dry-run` and `npm publish --dry-run` are treated as validation. Explicit real commands such as `npm publish --dry-run=false` are treated as release boundaries and will enforce pending feature verification.
151
180
 
181
+ ### Orchestrator/Worker Lanes
182
+
183
+ Use orchestration when a broad task is split across Claude Code, Codex, subagents, or separate worktrees. These are **agent-run commands**: Eagle Mem injects the protocol into Claude Code/Codex, and the active agent runs the lane/status/spawn commands itself. Users should not have to operate this manually.
184
+
185
+ By default Eagle Mem uses the opposite-agent worker model:
186
+
187
+ - Codex coordinator -> Claude Code worker using `claude-opus-4-7` at `xhigh`
188
+ - Claude Code coordinator -> Codex worker using `gpt-5.5` at `xhigh`
189
+
190
+ Worker models, effort, route, and worktree behavior are configurable in `~/.eagle-mem/config.toml` under `[orchestration]`.
191
+
192
+ ```bash
193
+ eagle-mem orchestrate init "Ship auth cleanup"
194
+ eagle-mem orchestrate lane add api --agent codex --desc "API fixes + tests" --validate "npm test"
195
+ eagle-mem orchestrate lane add docs --agent claude-code --desc "README and release notes"
196
+ eagle-mem orchestrate spawn api
197
+ eagle-mem orchestrate sync
198
+ eagle-mem orchestrate complete
199
+ eagle-mem orchestrate handoff --write docs/handoff-context.md
200
+ ```
201
+
202
+ `spawn` creates a git worktree, writes a self-contained worker prompt, launches the selected worker CLI, captures its log/exit status under `~/.eagle-mem/orchestrations/`, and updates lane/task state when the worker finishes. Worker hooks export the original Eagle Mem project name, so observations and summaries created inside worktrees still attach to the main project memory.
203
+ Use `lane complete` or `lane block` manually only when work happened outside the wrapper or the worker needs an explicit correction.
204
+
205
+ Each lane is stored in `orchestration_lanes` and mirrored into `agent_tasks`, so the next Claude Code or Codex session sees what is pending, who owns it, which worktree/log belongs to it, and how to validate it.
206
+
152
207
  ### Shared Claude Code + Codex Memory
153
208
 
154
209
  Both agents write to `~/.eagle-mem/memory.db`:
@@ -160,7 +215,7 @@ Both agents write to `~/.eagle-mem/memory.db`:
160
215
 
161
216
  That means opening the same project in Claude Code and Codex does not create two isolated memory worlds. They recall the same project history while preserving the source of each memory.
162
217
 
163
- ## Skills (Inside Claude Code)
218
+ ## Skills (Inside Claude Code and Codex)
164
219
 
165
220
  | Skill | What It Does |
166
221
  |-------|-------------|
@@ -168,6 +223,7 @@ That means opening the same project in Claude Code and Codex does not create two
168
223
  | `/eagle-mem-overview` | Build a rich project briefing from README, entry points, and git history |
169
224
  | `/eagle-mem-memories` | View and search mirrored agent memories and plans |
170
225
  | `/eagle-mem-tasks` | TaskAware Compact Loop — break complex work into tasks that survive `/compact` |
226
+ | `/eagle-mem-orchestrate` | Orchestrator/worker lane handoffs across Claude Code and Codex |
171
227
 
172
228
  ## Data
173
229
 
@@ -188,6 +244,8 @@ Single SQLite database at `~/.eagle-mem/memory.db` (WAL mode, FTS5 full-text sea
188
244
  | `feature_dependencies` | Inter-feature dependency relationships |
189
245
  | `feature_smoke_tests` | Smoke test definitions for feature verification |
190
246
  | `pending_feature_verifications` | Release blockers created when files tied to features change |
247
+ | `orchestrations` | Durable multi-agent work plans per project |
248
+ | `orchestration_lanes` | Worker lane ownership, status, validation, worktree, and notes |
191
249
  | `eagle_meta` | Internal metadata (last scan, last curate, etc.) |
192
250
  | `agent_memories` | Mirror of agent memory files, with source attribution |
193
251
  | `agent_plans` | Mirror of agent plan files, with source attribution |
@@ -213,6 +271,15 @@ eagle-mem config set agent_cli.preferred current
213
271
 
214
272
  Provider preference is local-first: Ollama is auto-detected when running, then Eagle Mem can use the installed Codex/Claude CLI via `agent_cli` before falling back to explicit Anthropic/OpenAI API providers. Eagle Mem works fully without a provider — LLM features gracefully degrade to heuristic fallbacks.
215
273
 
274
+ RTK is configured separately from the LLM provider:
275
+
276
+ ```bash
277
+ eagle-mem config set token_guard.rtk auto # default: use RTK when available
278
+ eagle-mem config set token_guard.rtk enforce # block known raw-output commands if RTK is missing
279
+ eagle-mem config set token_guard.rtk off # disable RTK behavior
280
+ eagle-mem config set token_guard.raw_bash block
281
+ ```
282
+
216
283
  ## License
217
284
 
218
285
  MIT
package/bin/eagle-mem CHANGED
@@ -26,6 +26,7 @@ case "$command" in
26
26
  overview) bash "$SCRIPTS_DIR/overview.sh" "$@" ;;
27
27
  memories) bash "$SCRIPTS_DIR/memories.sh" "$@" ;;
28
28
  tasks) bash "$SCRIPTS_DIR/tasks.sh" "$@" ;;
29
+ orchestrate) bash "$SCRIPTS_DIR/orchestrate.sh" "$@" ;;
29
30
  curate) bash "$SCRIPTS_DIR/curate.sh" "$@" ;;
30
31
  feature) bash "$SCRIPTS_DIR/feature.sh" "$@" ;;
31
32
  prune) bash "$SCRIPTS_DIR/prune.sh" "$@" ;;
@@ -0,0 +1,45 @@
1
+ -- Migration 029: Orchestrator/worker lane tracking.
2
+ -- Stores durable multi-agent work plans so Claude Code and Codex can share
3
+ -- lane ownership, status, validation commands, and handoff context.
4
+
5
+ CREATE TABLE IF NOT EXISTS orchestrations (
6
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
7
+ project TEXT NOT NULL,
8
+ name TEXT NOT NULL,
9
+ goal TEXT,
10
+ status TEXT NOT NULL DEFAULT 'active'
11
+ CHECK (status IN ('active', 'completed', 'cancelled')),
12
+ baseline_ref TEXT,
13
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
14
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
15
+ UNIQUE(project, name)
16
+ );
17
+
18
+ CREATE INDEX IF NOT EXISTS idx_orchestrations_project_status
19
+ ON orchestrations(project, status);
20
+
21
+ CREATE TABLE IF NOT EXISTS orchestration_lanes (
22
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
23
+ orchestration_id INTEGER NOT NULL REFERENCES orchestrations(id) ON DELETE CASCADE,
24
+ project TEXT NOT NULL,
25
+ lane_key TEXT NOT NULL,
26
+ title TEXT NOT NULL,
27
+ description TEXT,
28
+ agent TEXT NOT NULL DEFAULT 'codex',
29
+ worktree_path TEXT,
30
+ validation TEXT,
31
+ status TEXT NOT NULL DEFAULT 'pending'
32
+ CHECK (status IN ('pending', 'in_progress', 'blocked', 'completed', 'cancelled')),
33
+ source_task_id TEXT,
34
+ notes TEXT,
35
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
36
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
37
+ UNIQUE(orchestration_id, lane_key)
38
+ );
39
+
40
+ CREATE INDEX IF NOT EXISTS idx_orchestration_lanes_orchestration
41
+ ON orchestration_lanes(orchestration_id);
42
+ CREATE INDEX IF NOT EXISTS idx_orchestration_lanes_project_status
43
+ ON orchestration_lanes(project, status);
44
+ CREATE INDEX IF NOT EXISTS idx_orchestration_lanes_agent
45
+ ON orchestration_lanes(agent);
@@ -0,0 +1,88 @@
1
+ -- Migration 030: Scope orchestration lane identity to each orchestration.
2
+ -- Early 029 builds made lane_key unique per project, which allowed a lane
3
+ -- named "api" in one named orchestration to overwrite the same key in another.
4
+
5
+ DROP INDEX IF EXISTS idx_orchestration_lanes_orchestration;
6
+ DROP INDEX IF EXISTS idx_orchestration_lanes_project_status;
7
+ DROP INDEX IF EXISTS idx_orchestration_lanes_agent;
8
+
9
+ CREATE TABLE IF NOT EXISTS orchestration_lanes_new (
10
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
11
+ orchestration_id INTEGER NOT NULL REFERENCES orchestrations(id) ON DELETE CASCADE,
12
+ project TEXT NOT NULL,
13
+ lane_key TEXT NOT NULL,
14
+ title TEXT NOT NULL,
15
+ description TEXT,
16
+ agent TEXT NOT NULL DEFAULT 'codex',
17
+ worktree_path TEXT,
18
+ validation TEXT,
19
+ status TEXT NOT NULL DEFAULT 'pending'
20
+ CHECK (status IN ('pending', 'in_progress', 'blocked', 'completed', 'cancelled')),
21
+ source_task_id TEXT,
22
+ notes TEXT,
23
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
24
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
25
+ UNIQUE(orchestration_id, lane_key)
26
+ );
27
+
28
+ INSERT OR IGNORE INTO orchestration_lanes_new (
29
+ id, orchestration_id, project, lane_key, title, description, agent,
30
+ worktree_path, validation, status, source_task_id, notes, created_at, updated_at
31
+ )
32
+ SELECT
33
+ id, orchestration_id, project, lane_key, title, description, agent,
34
+ worktree_path, validation, status, source_task_id, notes, created_at, updated_at
35
+ FROM orchestration_lanes;
36
+
37
+ DROP TABLE orchestration_lanes;
38
+ ALTER TABLE orchestration_lanes_new RENAME TO orchestration_lanes;
39
+
40
+ CREATE INDEX IF NOT EXISTS idx_orchestration_lanes_orchestration
41
+ ON orchestration_lanes(orchestration_id);
42
+ CREATE INDEX IF NOT EXISTS idx_orchestration_lanes_project_status
43
+ ON orchestration_lanes(project, status);
44
+ CREATE INDEX IF NOT EXISTS idx_orchestration_lanes_agent
45
+ ON orchestration_lanes(agent);
46
+
47
+ UPDATE orchestration_lanes
48
+ SET source_task_id = 'lane-' || (
49
+ SELECT name FROM orchestrations WHERE orchestrations.id = orchestration_lanes.orchestration_id
50
+ ) || '-' || lane_key
51
+ WHERE source_task_id IS NULL
52
+ OR source_task_id = ''
53
+ OR source_task_id = 'lane-' || lane_key;
54
+
55
+ UPDATE agent_tasks
56
+ SET source_task_id = (
57
+ SELECT l.source_task_id
58
+ FROM orchestration_lanes l
59
+ JOIN orchestrations o ON o.id = l.orchestration_id
60
+ WHERE l.project = agent_tasks.project
61
+ AND (
62
+ agent_tasks.file_path = 'orchestration-lane://' || l.project || '/' || l.lane_key
63
+ OR agent_tasks.file_path = 'orchestration-lane://' || l.project || '/' || o.name || '/' || l.lane_key
64
+ )
65
+ LIMIT 1
66
+ ),
67
+ file_path = (
68
+ SELECT 'orchestration-lane://' || l.project || '/' || o.name || '/' || l.lane_key
69
+ FROM orchestration_lanes l
70
+ JOIN orchestrations o ON o.id = l.orchestration_id
71
+ WHERE l.project = agent_tasks.project
72
+ AND (
73
+ agent_tasks.file_path = 'orchestration-lane://' || l.project || '/' || l.lane_key
74
+ OR agent_tasks.file_path = 'orchestration-lane://' || l.project || '/' || o.name || '/' || l.lane_key
75
+ )
76
+ LIMIT 1
77
+ )
78
+ WHERE source_session_id = 'orchestration'
79
+ AND EXISTS (
80
+ SELECT 1
81
+ FROM orchestration_lanes l
82
+ JOIN orchestrations o ON o.id = l.orchestration_id
83
+ WHERE l.project = agent_tasks.project
84
+ AND (
85
+ agent_tasks.file_path = 'orchestration-lane://' || l.project || '/' || l.lane_key
86
+ OR agent_tasks.file_path = 'orchestration-lane://' || l.project || '/' || o.name || '/' || l.lane_key
87
+ )
88
+ );
@@ -0,0 +1,20 @@
1
+ -- Migration 031: Worker execution metadata for orchestration lanes.
2
+ -- Keeps worker routing, worktree, process, and log state durable so Claude Code
3
+ -- and Codex can hand off lane execution safely.
4
+
5
+ ALTER TABLE orchestration_lanes ADD COLUMN branch_name TEXT;
6
+ ALTER TABLE orchestration_lanes ADD COLUMN worker_agent TEXT;
7
+ ALTER TABLE orchestration_lanes ADD COLUMN worker_model TEXT;
8
+ ALTER TABLE orchestration_lanes ADD COLUMN worker_effort TEXT;
9
+ ALTER TABLE orchestration_lanes ADD COLUMN worker_pid INTEGER;
10
+ ALTER TABLE orchestration_lanes ADD COLUMN worker_log_path TEXT;
11
+ ALTER TABLE orchestration_lanes ADD COLUMN worker_exit_path TEXT;
12
+ ALTER TABLE orchestration_lanes ADD COLUMN worker_prompt_path TEXT;
13
+ ALTER TABLE orchestration_lanes ADD COLUMN worker_command TEXT;
14
+ ALTER TABLE orchestration_lanes ADD COLUMN worker_started_at TEXT;
15
+ ALTER TABLE orchestration_lanes ADD COLUMN worker_finished_at TEXT;
16
+
17
+ CREATE INDEX IF NOT EXISTS idx_orchestration_lanes_worker_pid
18
+ ON orchestration_lanes(worker_pid);
19
+ CREATE INDEX IF NOT EXISTS idx_orchestration_lanes_branch
20
+ ON orchestration_lanes(branch_name);
@@ -0,0 +1,9 @@
1
+ -- Migration 032: Stable run identity for reusable orchestration names.
2
+ -- A project may reuse the default orchestration name ("main") for multiple
3
+ -- goals, so worker branches and worktrees need a per-run key.
4
+
5
+ ALTER TABLE orchestrations ADD COLUMN run_key TEXT;
6
+
7
+ UPDATE orchestrations
8
+ SET run_key = 'r' || id
9
+ WHERE run_key IS NULL OR run_key = '';
@@ -91,9 +91,11 @@ Pending checks:"
91
91
 
92
92
  # ─── RTK command rewrite / enforcement ─────────────────
93
93
 
94
+ rtk_mode=$(eagle_token_guard_rtk_mode)
95
+ raw_bash_mode=$(eagle_token_guard_raw_bash_mode)
94
96
  rtk_cmd=$(eagle_rtk_rewrite_command "$cmd")
95
97
  if [ -n "$rtk_cmd" ]; then
96
- if [ "$agent" = "codex" ] && ! eagle_raw_bash_unlock_active; then
98
+ if [ "$agent" = "codex" ] && [ "$raw_bash_mode" != "allow" ] && ! eagle_raw_bash_unlock_active; then
97
99
  reason="Eagle Mem token guard blocked raw shell output.
98
100
 
99
101
  Use RTK so large output is compact before it enters context:
@@ -117,6 +119,27 @@ Temporary escape hatch for one-off raw output:
117
119
  updated_input=$(echo "$input" | jq --arg cmd "$rtk_cmd" '.tool_input + {"command":$cmd}')
118
120
  context+="Eagle Mem token guard: rewrote raw shell command through RTK to reduce context load: ${rtk_cmd}. "
119
121
  fi
122
+ elif [ "$rtk_mode" = "enforce" ] && ! command -v rtk >/dev/null 2>&1 && eagle_raw_output_command_needs_guard "$cmd" && ! eagle_raw_bash_unlock_active; then
123
+ reason="Eagle Mem token guard is set to enforce, but RTK is not installed or not on PATH.
124
+
125
+ This command can dump raw output into agent context:
126
+ $cmd
127
+
128
+ Install RTK or disable enforcement:
129
+ eagle-mem config set token_guard.rtk auto
130
+
131
+ Temporary escape hatch:
132
+ touch $EAGLE_RAW_BASH_UNLOCK"
133
+ jq -nc --arg reason "$reason" '{
134
+ "decision":"block",
135
+ "reason":$reason,
136
+ "hookSpecificOutput":{
137
+ "hookEventName":"PreToolUse",
138
+ "permissionDecision":"deny",
139
+ "permissionDecisionReason":$reason
140
+ }
141
+ }'
142
+ exit 0
120
143
  fi
121
144
 
122
145
  # ─── Feature verification context for non-blocked pushes ───
@@ -27,6 +27,8 @@ source_type=$(echo "$input" | jq -r '.source // empty')
27
27
  model=$(echo "$input" | jq -r '.model // empty')
28
28
  agent=$(eagle_agent_source_from_json "$input")
29
29
  agent_label=$(eagle_agent_label "$agent")
30
+ codex_compact=0
31
+ [ "$agent" = "codex" ] && codex_compact=1
30
32
 
31
33
  [ -z "$session_id" ] && exit 0
32
34
 
@@ -131,8 +133,7 @@ if [ "$stat_tasks_done" -gt 0 ]; then
131
133
  task_parts+="${stat_tasks_done} completed"
132
134
  fi
133
135
 
134
- stat_last_display="${stat_last_summary:0:60}"
135
- [ ${#stat_last_summary} -gt 60 ] && stat_last_display+="..."
136
+ stat_last_display=$(eagle_trim_text "$stat_last_summary" 60)
136
137
 
137
138
  eagle_banner="======================================
138
139
  Eagle Mem Recall Ready
@@ -178,9 +179,9 @@ fi
178
179
 
179
180
  overview=$(eagle_get_overview "$project")
180
181
  if [ -n "$overview" ]; then
181
- if [ ${#overview} -gt 500 ]; then
182
- overview="${overview:0:497}..."
183
- fi
182
+ overview_limit=500
183
+ [ "$codex_compact" -eq 1 ] && overview_limit=320
184
+ overview=$(eagle_trim_text "$overview" "$overview_limit")
184
185
  context+="
185
186
  === Eagle Mem: Project Overview ===
186
187
  $overview
@@ -196,8 +197,10 @@ fi
196
197
 
197
198
  if [ "$source_type" = "compact" ] || [ "$source_type" = "clear" ]; then
198
199
  _summary_limit=1
200
+ elif [ "$codex_compact" -eq 1 ]; then
201
+ _summary_limit=1
199
202
  else
200
- _summary_limit=3
203
+ _summary_limit=2
201
204
  fi
202
205
 
203
206
  recent=$(eagle_get_recent_summaries "$project" "$_summary_limit")
@@ -209,6 +212,21 @@ if [ -n "$recent" ]; then
209
212
  while IFS='|' read -r request completed learned next_steps created_at decisions gotchas key_files summary_agent; do
210
213
  [ -z "$request" ] && [ -z "$completed" ] && continue
211
214
  summary_agent_label=$(eagle_agent_label "$summary_agent")
215
+ request=$(eagle_trim_text "$request" 160)
216
+ completed=$(eagle_trim_text "$completed" 220)
217
+ learned=$(eagle_trim_text "$learned" 180)
218
+ decisions=$(eagle_trim_text "$decisions" 160)
219
+ gotchas=$(eagle_trim_text "$gotchas" 160)
220
+ key_files=$(eagle_trim_text "$key_files" 160)
221
+ next_steps=$(eagle_trim_text "$next_steps" 160)
222
+ if [ "$codex_compact" -eq 1 ]; then
223
+ request=$(eagle_trim_text "$request" 120)
224
+ completed=$(eagle_trim_text "$completed" 160)
225
+ learned=$(eagle_trim_text "$learned" 120)
226
+ decisions=""
227
+ key_files=""
228
+ next_steps=""
229
+ fi
212
230
  context+="
213
231
  --- $created_at ---"
214
232
  [ -n "$summary_agent" ] && context+="
@@ -234,12 +252,14 @@ fi
234
252
 
235
253
  # ─── Memories (skip if none) ─────────────────────────────
236
254
 
255
+ memory_limit=3
256
+ [ "$codex_compact" -eq 1 ] && memory_limit=2
237
257
  memories=$(eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at, origin_agent,
238
258
  CAST(julianday('now') - julianday(updated_at) AS INTEGER) as days_ago
239
259
  FROM agent_memories
240
260
  WHERE project = '$p_esc'
241
261
  ORDER BY updated_at DESC
242
- LIMIT 5;")
262
+ LIMIT $memory_limit;")
243
263
  if [ -n "$memories" ]; then
244
264
  context+="
245
265
  === Eagle Mem: Stored Memories ===
@@ -247,6 +267,7 @@ if [ -n "$memories" ]; then
247
267
  while IFS='|' read -r mname mtype mdesc _fpath _updated morigin days_ago; do
248
268
  [ -z "$mname" ] && continue
249
269
  origin_label=$(eagle_agent_label "$morigin")
270
+ mdesc=$(eagle_trim_text "$mdesc" 180)
250
271
  age_label=""
251
272
  if [ -n "$days_ago" ] && [ "$days_ago" -gt 0 ] 2>/dev/null; then
252
273
  if [ "$days_ago" -eq 1 ]; then
@@ -279,14 +300,17 @@ fi
279
300
 
280
301
  # ─── Tasks (skip if none) ────────────────────────────────
281
302
 
303
+ task_limit=5
304
+ [ "$codex_compact" -eq 1 ] && task_limit=3
282
305
  synced_tasks=$(eagle_db "SELECT subject, status, blocked_by, origin_agent FROM agent_tasks
283
306
  WHERE project = '$p_esc'
284
307
  AND status IN ('in_progress', 'pending')
308
+ AND source_session_id != 'orchestration'
285
309
  AND updated_at > strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-7 days')
286
310
  ORDER BY
287
311
  CASE status WHEN 'in_progress' THEN 0 ELSE 1 END,
288
312
  updated_at DESC
289
- LIMIT 10;")
313
+ LIMIT $task_limit;")
290
314
  if [ -n "$synced_tasks" ]; then
291
315
  context+="
292
316
  === Eagle Mem: Tasks ===
@@ -303,10 +327,54 @@ if [ -n "$synced_tasks" ]; then
303
327
  done <<< "$synced_tasks"
304
328
  fi
305
329
 
330
+ # ─── Orchestration lanes (skip if none) ───────────────────
331
+
332
+ lane_limit=8
333
+ [ "$codex_compact" -eq 1 ] && lane_limit=4
334
+ orchestration_lanes=$(eagle_db "SELECT o.name, o.goal, l.lane_key, l.title,
335
+ COALESCE(REPLACE(REPLACE(l.description, char(10), ' '), '|', '/'), ''),
336
+ l.agent, l.status, l.validation, l.worktree_path, l.notes
337
+ FROM orchestration_lanes l
338
+ JOIN orchestrations o ON o.id = l.orchestration_id
339
+ WHERE l.project = '$p_esc'
340
+ AND o.status = 'active'
341
+ AND l.status IN ('in_progress', 'pending', 'blocked')
342
+ ORDER BY
343
+ CASE l.status WHEN 'in_progress' THEN 0 WHEN 'blocked' THEN 1 ELSE 2 END,
344
+ l.updated_at DESC
345
+ LIMIT $lane_limit;" 2>/dev/null)
346
+ if [ -n "$orchestration_lanes" ]; then
347
+ context+="
348
+ === Eagle Mem: Orchestration Lanes ===
349
+ "
350
+ while IFS='|' read -r oname ogoal lkey ltitle ldesc lagent lstatus lvalidation lworktree lnotes; do
351
+ [ -z "$lkey" ] && continue
352
+ origin_label=$(eagle_agent_label "$lagent")
353
+ context+=" - [$lstatus][$origin_label] $lkey: $ltitle"
354
+ if [ -n "$ldesc" ]; then
355
+ desc_limit=220
356
+ [ "$codex_compact" -eq 1 ] && desc_limit=120
357
+ context+=" | scope: $(eagle_trim_text "$ldesc" "$desc_limit")"
358
+ fi
359
+ [ -n "$oname" ] && context+=" | plan: $oname"
360
+ [ -n "$lvalidation" ] && context+=" | validate: $lvalidation"
361
+ [ -n "$lworktree" ] && context+=" | worktree: $lworktree"
362
+ [ -n "$lnotes" ] && context+=" | notes: $lnotes"
363
+ context+="
364
+ "
365
+ done <<< "$orchestration_lanes"
366
+ context+="You, the active agent, must run 'eagle-mem orchestrate' before taking lane work. Do not ask the user to run these commands. Update lane status when work starts, blocks, or completes.
367
+ "
368
+ fi
369
+
306
370
  # ─── Pending feature verifications ───────────────────────
307
371
 
308
- pending_features=$(eagle_list_pending_feature_verifications "$project" 10 2>/dev/null)
372
+ pending_limit=5
373
+ [ "$codex_compact" -eq 1 ] && pending_limit=3
374
+ pending_features=$(eagle_list_pending_feature_verifications "$project" "$pending_limit" 2>/dev/null)
309
375
  if [ -n "$pending_features" ]; then
376
+ pending_total=$(eagle_db "SELECT COUNT(*) FROM pending_feature_verifications WHERE project = '$p_esc' AND status = 'pending';" 2>/dev/null)
377
+ pending_total=${pending_total:-0}
310
378
  context+="
311
379
  === Eagle Mem: Pending Feature Verification ===
312
380
  Release-boundary commands are blocked until these are verified or waived.
@@ -321,6 +389,10 @@ Release-boundary commands are blocked until these are verified or waived.
321
389
  context+="
322
390
  "
323
391
  done <<< "$pending_features"
392
+ if [ "$pending_total" -gt "$pending_limit" ] 2>/dev/null; then
393
+ context+=" - ... $((pending_total - pending_limit)) more pending; run: eagle-mem feature pending
394
+ "
395
+ fi
324
396
  context+="Resolve with: eagle-mem feature verify <name> --notes \"what passed\"; or eagle-mem feature waive <id> --reason \"why safe\".
325
397
  "
326
398
  fi
@@ -334,9 +406,14 @@ if [ -n "$hot_files" ]; then
334
406
  Frequently read — re-read sparingly if unchanged.
335
407
  "
336
408
  IFS=',' read -ra hf_arr <<< "$hot_files"
409
+ hf_count=0
410
+ hot_file_limit=8
411
+ [ "$codex_compact" -eq 1 ] && hot_file_limit=5
337
412
  for hf in "${hf_arr[@]}"; do
413
+ [ "$hf_count" -ge "$hot_file_limit" ] && break
338
414
  [ -n "$hf" ] && context+=" - $(basename "$hf")
339
415
  "
416
+ hf_count=$((hf_count + 1))
340
417
  done
341
418
  fi
342
419
 
@@ -359,7 +436,12 @@ fi
359
436
 
360
437
  # ─── Instructions (compressed) ───────────────────────────
361
438
 
362
- if [ "$source_type" = "compact" ] || [ "$source_type" = "clear" ]; then
439
+ if [ "$agent" = "codex" ]; then
440
+ context+="
441
+ === Eagle Mem: Active ===
442
+ Memory active for '$project'. Emit <eagle-summary> in final responses with request, completed, learned, decisions, gotchas, next_steps, key_files, files_read, files_modified, affected_features, verified_features, and regression_risks.
443
+ "
444
+ elif [ "$source_type" = "compact" ] || [ "$source_type" = "clear" ]; then
363
445
  context+="
364
446
  === Eagle Mem: Active ===
365
447
  Memory active. Attribute recalled context to Eagle Mem. Do not revert PostToolUse-surfaced decisions without asking. Emit <eagle-summary> before final response.
package/hooks/stop.sh CHANGED
@@ -57,14 +57,22 @@ request=""
57
57
  heuristic_reads=""
58
58
  heuristic_writes=""
59
59
 
60
- if [ -n "$transcript_path" ] && [ -f "$transcript_path" ]; then
61
- request=$(jq -r 'select(.type == "user") | .message.content | if type == "string" then . elif type == "array" then [.[] | select(.type == "text") | .text] | join(" ") else "" end' "$transcript_path" 2>/dev/null \
62
- | grep -v '<local-command-caveat>' \
60
+ eagle_clean_request_candidates() {
61
+ grep -v '<local-command-caveat>' \
63
62
  | grep -v '<system-reminder>' \
64
63
  | grep -v '<command-name>' \
65
64
  | grep -v '<command-message>' \
66
65
  | grep -v '^\[{' \
67
- | head -1 | cut -c1-500)
66
+ | grep -v '^# AGENTS.md instructions' \
67
+ | grep -v '^<environment_context>' \
68
+ | awk 'NF' \
69
+ | tail -1 \
70
+ | cut -c1-500
71
+ }
72
+
73
+ if [ -n "$transcript_path" ] && [ -f "$transcript_path" ]; then
74
+ request=$(jq -r 'select(.type == "user") | .message.content | if type == "string" then . elif type == "array" then [.[] | select(.type == "text") | .text] | join(" ") else "" end' "$transcript_path" 2>/dev/null \
75
+ | eagle_clean_request_candidates)
68
76
 
69
77
  if [ -z "$request" ]; then
70
78
  request=$(jq -r '
@@ -74,19 +82,12 @@ if [ -n "$transcript_path" ] && [ -f "$transcript_path" ]; then
74
82
  elif type == "array" then [.[]? | select(.type == "input_text" or .type == "text") | .text] | join(" ")
75
83
  else "" end
76
84
  ' "$transcript_path" 2>/dev/null \
77
- | grep -v '<local-command-caveat>' \
78
- | grep -v '<system-reminder>' \
79
- | grep -v '<command-name>' \
80
- | grep -v '<command-message>' \
81
- | grep -v '^\[{' \
82
- | head -1 | cut -c1-500)
85
+ | eagle_clean_request_candidates)
83
86
  fi
84
87
 
85
88
  if [ -z "$request" ]; then
86
89
  request=$(jq -r 'select(.type == "event_msg" and .payload.type == "user_message") | .payload.message // empty' "$transcript_path" 2>/dev/null \
87
- | grep -v '<local-command-caveat>' \
88
- | grep -v '<system-reminder>' \
89
- | head -1 | cut -c1-500)
90
+ | eagle_clean_request_candidates)
90
91
  fi
91
92
 
92
93
  heuristic_reads=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | select(.name == "Read") | .input.file_path // empty' "$transcript_path" 2>/dev/null | sort -u | head -20)