eagle-mem 4.7.1 → 4.8.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 +46 -4
- package/bin/eagle-mem +1 -0
- package/db/029_orchestration_lanes.sql +45 -0
- package/db/030_orchestration_lane_scope.sql +88 -0
- package/db/031_orchestration_workers.sql +20 -0
- package/db/032_orchestration_run_keys.sql +9 -0
- package/hooks/pre-tool-use.sh +24 -1
- package/hooks/session-start.sh +92 -10
- package/hooks/stop.sh +14 -13
- package/hooks/user-prompt-submit.sh +70 -12
- package/lib/common.sh +170 -3
- package/lib/db-observations.sh +7 -0
- package/lib/db-sessions.sh +7 -1
- package/lib/db-summaries.sh +6 -2
- package/lib/provider.sh +34 -0
- package/package.json +3 -1
- package/scripts/config.sh +30 -0
- package/scripts/health.sh +66 -0
- package/scripts/help.sh +12 -1
- package/scripts/orchestrate.sh +1268 -0
- package/skills/eagle-mem-orchestrate/SKILL.md +149 -0
- package/skills/eagle-mem-tasks/SKILL.md +2 -0
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
**Context that survives `/compact`.**
|
|
11
11
|
|
|
12
|
-
**v4.
|
|
12
|
+
**v4.8.0 ships first-class cross-agent orchestration, compact Codex hook recall, enforced anti-regression checks, and configurable RTK token guardrails.**
|
|
13
13
|
Claude Code and Codex can now share the same local Eagle Mem database, while every captured row records which agent created it.
|
|
14
14
|
|
|
15
15
|
## The Problem
|
|
@@ -70,6 +70,7 @@ Hooks fire automatically at different points in the agent lifecycle:
|
|
|
70
70
|
| **SessionEnd** | session closes | Re-syncs tasks, marks session completed |
|
|
71
71
|
|
|
72
72
|
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.
|
|
73
|
+
Codex recall is emitted as compact hook JSON, so local Codex sessions get shared memory without the multi-screen hook dumps.
|
|
73
74
|
|
|
74
75
|
### Background Automation
|
|
75
76
|
|
|
@@ -85,13 +86,14 @@ These run automatically via SessionStart — no commands needed:
|
|
|
85
86
|
Eagle Mem actively reduces token consumption:
|
|
86
87
|
|
|
87
88
|
- **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
|
|
89
|
+
- **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
90
|
- **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
91
|
- **Read dedup tracking** — files read 3+ times in a session get a soft nudge
|
|
91
92
|
- **Co-edit nudges** — learned from observation data: when you edit file X, PreToolUse reminds you that you usually also touch file Y
|
|
92
93
|
- **Hot file awareness** — curator identifies files read in 50%+ of sessions; SessionStart flags them as "likely in context" to reduce re-reads
|
|
93
94
|
- **Working set recovery** — on compact, SessionStart injects the files you were actively editing so you resume without re-reading everything
|
|
94
95
|
- **Stuck loop detection** — if the same file is edited 5+ times in one session, PreToolUse nudges to reconsider the approach
|
|
96
|
+
- **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
97
|
|
|
96
98
|
### Anti-Regression
|
|
97
99
|
|
|
@@ -103,6 +105,7 @@ Eagle Mem prevents Claude from repeating past mistakes:
|
|
|
103
105
|
- **Gotcha surfacing** — past surprises and gotchas are surfaced when editing related files
|
|
104
106
|
- **Stale memory detection** — warns when edits may contradict stored memories
|
|
105
107
|
- **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
|
|
108
|
+
- **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
109
|
|
|
107
110
|
## Commands
|
|
108
111
|
|
|
@@ -113,11 +116,12 @@ Eagle Mem prevents Claude from repeating past mistakes:
|
|
|
113
116
|
| `eagle-mem uninstall` | Remove hooks and optionally delete data |
|
|
114
117
|
| `eagle-mem search` | Search past sessions, memories, and code |
|
|
115
118
|
| `eagle-mem health` | Diagnose pipeline health and background automation |
|
|
116
|
-
| `eagle-mem config` | View or change LLM provider settings |
|
|
119
|
+
| `eagle-mem config` | View or change LLM provider and token-guard settings |
|
|
117
120
|
| `eagle-mem guard` | Manage regression guardrails for files |
|
|
118
121
|
| `eagle-mem overview` | Build or view project overview |
|
|
119
122
|
| `eagle-mem memories` | View/sync agent memories |
|
|
120
123
|
| `eagle-mem tasks` | View mirrored tasks |
|
|
124
|
+
| `eagle-mem orchestrate` | Coordinate durable worker lanes across agents |
|
|
121
125
|
| `eagle-mem curate` | Run curator (co-edits, hot files, guardrails) |
|
|
122
126
|
| `eagle-mem feature` | Track and verify features |
|
|
123
127
|
| `eagle-mem prune` | Clean old sessions and stale data |
|
|
@@ -149,6 +153,32 @@ Verification is tied to the current git diff fingerprint. If the same diff was a
|
|
|
149
153
|
|
|
150
154
|
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
155
|
|
|
156
|
+
### Orchestrator/Worker Lanes
|
|
157
|
+
|
|
158
|
+
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.
|
|
159
|
+
|
|
160
|
+
By default Eagle Mem uses the opposite-agent worker model:
|
|
161
|
+
|
|
162
|
+
- Codex coordinator -> Claude Code worker using `claude-opus-4-7` at `xhigh`
|
|
163
|
+
- Claude Code coordinator -> Codex worker using `gpt-5.5` at `xhigh`
|
|
164
|
+
|
|
165
|
+
Worker models, effort, route, and worktree behavior are configurable in `~/.eagle-mem/config.toml` under `[orchestration]`.
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
eagle-mem orchestrate init "Ship auth cleanup"
|
|
169
|
+
eagle-mem orchestrate lane add api --agent codex --desc "API fixes + tests" --validate "npm test"
|
|
170
|
+
eagle-mem orchestrate lane add docs --agent claude-code --desc "README and release notes"
|
|
171
|
+
eagle-mem orchestrate spawn api
|
|
172
|
+
eagle-mem orchestrate sync
|
|
173
|
+
eagle-mem orchestrate complete
|
|
174
|
+
eagle-mem orchestrate handoff --write docs/handoff-context.md
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`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.
|
|
178
|
+
Use `lane complete` or `lane block` manually only when work happened outside the wrapper or the worker needs an explicit correction.
|
|
179
|
+
|
|
180
|
+
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.
|
|
181
|
+
|
|
152
182
|
### Shared Claude Code + Codex Memory
|
|
153
183
|
|
|
154
184
|
Both agents write to `~/.eagle-mem/memory.db`:
|
|
@@ -160,7 +190,7 @@ Both agents write to `~/.eagle-mem/memory.db`:
|
|
|
160
190
|
|
|
161
191
|
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
192
|
|
|
163
|
-
## Skills (Inside Claude Code)
|
|
193
|
+
## Skills (Inside Claude Code and Codex)
|
|
164
194
|
|
|
165
195
|
| Skill | What It Does |
|
|
166
196
|
|-------|-------------|
|
|
@@ -168,6 +198,7 @@ That means opening the same project in Claude Code and Codex does not create two
|
|
|
168
198
|
| `/eagle-mem-overview` | Build a rich project briefing from README, entry points, and git history |
|
|
169
199
|
| `/eagle-mem-memories` | View and search mirrored agent memories and plans |
|
|
170
200
|
| `/eagle-mem-tasks` | TaskAware Compact Loop — break complex work into tasks that survive `/compact` |
|
|
201
|
+
| `/eagle-mem-orchestrate` | Orchestrator/worker lane handoffs across Claude Code and Codex |
|
|
171
202
|
|
|
172
203
|
## Data
|
|
173
204
|
|
|
@@ -188,6 +219,8 @@ Single SQLite database at `~/.eagle-mem/memory.db` (WAL mode, FTS5 full-text sea
|
|
|
188
219
|
| `feature_dependencies` | Inter-feature dependency relationships |
|
|
189
220
|
| `feature_smoke_tests` | Smoke test definitions for feature verification |
|
|
190
221
|
| `pending_feature_verifications` | Release blockers created when files tied to features change |
|
|
222
|
+
| `orchestrations` | Durable multi-agent work plans per project |
|
|
223
|
+
| `orchestration_lanes` | Worker lane ownership, status, validation, worktree, and notes |
|
|
191
224
|
| `eagle_meta` | Internal metadata (last scan, last curate, etc.) |
|
|
192
225
|
| `agent_memories` | Mirror of agent memory files, with source attribution |
|
|
193
226
|
| `agent_plans` | Mirror of agent plan files, with source attribution |
|
|
@@ -213,6 +246,15 @@ eagle-mem config set agent_cli.preferred current
|
|
|
213
246
|
|
|
214
247
|
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
248
|
|
|
249
|
+
RTK is configured separately from the LLM provider:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
eagle-mem config set token_guard.rtk auto # default: use RTK when available
|
|
253
|
+
eagle-mem config set token_guard.rtk enforce # block known raw-output commands if RTK is missing
|
|
254
|
+
eagle-mem config set token_guard.rtk off # disable RTK behavior
|
|
255
|
+
eagle-mem config set token_guard.raw_bash block
|
|
256
|
+
```
|
|
257
|
+
|
|
216
258
|
## License
|
|
217
259
|
|
|
218
260
|
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 = '';
|
package/hooks/pre-tool-use.sh
CHANGED
|
@@ -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 ───
|
package/hooks/session-start.sh
CHANGED
|
@@ -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
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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=
|
|
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
|
|
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
|
|
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
|
-
|
|
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 [ "$
|
|
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
|
-
|
|
61
|
-
|
|
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
|
-
|
|
|
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
|
-
|
|
|
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
|
-
|
|
|
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)
|