eagle-mem 4.10.13 → 4.12.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/CHANGELOG.md +25 -0
- package/README.md +22 -22
- package/architecture.html +26 -14
- package/bin/eagle-mem +4 -0
- package/db/039_recall_events.sql +27 -0
- package/db/040_graph_decision_nodes.sql +21 -0
- package/db/041_graph_semantic_edge_types.sql +21 -0
- package/db/042_orchestration_auto_events.sql +23 -0
- package/db/043_eagle_events.sql +22 -0
- package/db/044_summary_capture_source.sql +12 -0
- package/docs/agent-compatibility/README.md +38 -0
- package/docs/agent-compatibility/claude-code.md +58 -0
- package/docs/agent-compatibility/codex.md +57 -0
- package/docs/agent-compatibility/opencode.md +72 -0
- package/hooks/post-tool-use.sh +8 -0
- package/hooks/pre-tool-use.sh +10 -1
- package/hooks/session-end.sh +3 -0
- package/hooks/session-start.sh +15 -17
- package/hooks/stop.sh +34 -5
- package/hooks/user-prompt-submit.sh +85 -10
- package/integrations/opencode_eagle_mem_plugin.js +387 -0
- package/lib/codex-hooks.sh +13 -6
- package/lib/common.sh +77 -7
- package/lib/db-events.sh +89 -0
- package/lib/db-graph.sh +154 -0
- package/lib/db-observations.sh +34 -0
- package/lib/db-orchestration.sh +149 -0
- package/lib/db-summaries.sh +70 -3
- package/lib/db.sh +2 -0
- package/lib/hooks.sh +41 -7
- package/lib/opencode-hooks.sh +105 -0
- package/lib/provider.sh +2 -2
- package/package.json +5 -2
- package/scripts/compaction.sh +109 -9
- package/scripts/dashboard.sh +372 -0
- package/scripts/doctor.sh +30 -3
- package/scripts/enrich-summary.sh +8 -2
- package/scripts/health.sh +40 -2
- package/scripts/help.sh +10 -2
- package/scripts/inspect.sh +285 -0
- package/scripts/install.sh +36 -7
- package/scripts/memories.sh +13 -0
- package/scripts/repair.sh +187 -0
- package/scripts/replay.sh +248 -0
- package/scripts/search.sh +44 -3
- package/scripts/session.sh +155 -18
- package/scripts/statusline-em.sh +34 -7
- package/scripts/tasks.sh +34 -0
- package/scripts/test.sh +13 -0
- package/scripts/uninstall.sh +9 -0
- package/scripts/update.sh +21 -2
- package/tests/fixtures/agent-hooks/claude-statusline.json +32 -0
- package/tests/fixtures/agent-hooks/claude-user-prompt-submit.json +9 -0
- package/tests/fixtures/agent-hooks/codex-pre-tool-use.json +10 -0
- package/tests/fixtures/agent-hooks/codex-user-prompt-submit.json +7 -0
- package/tests/fixtures/agent-hooks/opencode-chat-message.json +36 -0
- package/tests/fixtures/agent-hooks/opencode-session-compacting.json +9 -0
- package/tests/fixtures/agent-hooks/opencode-todo-updated.json +13 -0
- package/tests/fixtures/agent-hooks/opencode-tool-execute-after.json +15 -0
- package/tests/fixtures/agent-hooks/opencode-tool-execute-before.json +12 -0
- package/tests/test_agent_compatibility_docs_gate.sh +123 -0
- package/tests/test_auto_orchestration_detection.sh +109 -0
- package/tests/test_claude_stop_hook_registration.sh +56 -0
- package/tests/test_clean_session_capture.sh +105 -0
- package/tests/test_codex_hooks_config.sh +73 -0
- package/tests/test_compaction_survival_matrix.sh +237 -0
- package/tests/test_dashboard.sh +96 -0
- package/tests/test_eagle_events.sh +96 -0
- package/tests/test_opencode_hooks_config.sh +56 -0
- package/tests/test_opencode_plugin_adapter.sh +202 -0
- package/tests/test_recall_observability.sh +144 -0
- package/tests/test_repair.sh +63 -0
- package/tests/test_rust_migration_plan.sh +75 -0
- package/tests/test_trust_surfaces.sh +123 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# End-to-end compaction survival matrix. A pre-compact session creates every
|
|
3
|
+
# durable entity Eagle Mem promises to preserve, then a post-compact prompt must
|
|
4
|
+
# recover it through search, recall, replay, status, and the project graph.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
8
|
+
EAGLE_BIN="$ROOT_DIR/bin/eagle-mem"
|
|
9
|
+
|
|
10
|
+
tmp_dir=$(mktemp -d "$ROOT_DIR/.tmp-compaction-survival.XXXXXX")
|
|
11
|
+
trap 'rm -rf "$tmp_dir"' EXIT
|
|
12
|
+
|
|
13
|
+
export HOME="$tmp_dir/home"
|
|
14
|
+
export EAGLE_MEM_DIR="$tmp_dir/eagle-mem"
|
|
15
|
+
mkdir -p "$HOME" "$EAGLE_MEM_DIR"
|
|
16
|
+
|
|
17
|
+
. "$ROOT_DIR/lib/common.sh"
|
|
18
|
+
"$ROOT_DIR/db/migrate.sh" >/dev/null
|
|
19
|
+
. "$ROOT_DIR/lib/db.sh"
|
|
20
|
+
|
|
21
|
+
assert_json() {
|
|
22
|
+
local json="$1" filter="$2" message="$3"
|
|
23
|
+
if ! printf '%s' "$json" | jq -e "$filter" >/dev/null; then
|
|
24
|
+
echo "$message" >&2
|
|
25
|
+
echo "$json" >&2
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
assert_contains() {
|
|
31
|
+
local haystack="$1" needle="$2" message="$3"
|
|
32
|
+
case "$haystack" in
|
|
33
|
+
*"$needle"*) ;;
|
|
34
|
+
*)
|
|
35
|
+
echo "$message" >&2
|
|
36
|
+
echo "Expected to find: $needle" >&2
|
|
37
|
+
echo "$haystack" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
strip_ansi() {
|
|
44
|
+
sed -E $'s/\x1b\\[[0-9;]*m//g'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
project="project-compaction-matrix"
|
|
48
|
+
export EAGLE_MEM_PROJECT="$project"
|
|
49
|
+
repo="$HOME/$project"
|
|
50
|
+
mkdir -p "$repo/src"
|
|
51
|
+
|
|
52
|
+
cat > "$repo/src/auth-client.ts" <<'EOF'
|
|
53
|
+
export function refreshOauthToken() {
|
|
54
|
+
return "oauth-token-refresh compaction survival rotates credentials safely";
|
|
55
|
+
}
|
|
56
|
+
EOF
|
|
57
|
+
|
|
58
|
+
pre_session="compact-pre-session"
|
|
59
|
+
post_session="compact-post-session"
|
|
60
|
+
files_json='["src/auth-client.ts"]'
|
|
61
|
+
|
|
62
|
+
seed_summary() {
|
|
63
|
+
local session_id="$1" request="$2" learned="$3" completed="$4" decisions="$5"
|
|
64
|
+
eagle_upsert_session "$session_id" "$project" "$repo" "test-model" "test" "codex" >/dev/null
|
|
65
|
+
eagle_insert_summary \
|
|
66
|
+
"$session_id" \
|
|
67
|
+
"$project" \
|
|
68
|
+
"$request" \
|
|
69
|
+
"Read src/auth-client.ts and the oauth-token-refresh path" \
|
|
70
|
+
"$learned" \
|
|
71
|
+
"$completed" \
|
|
72
|
+
"Verify oauth-token-refresh after compact in a new session" \
|
|
73
|
+
"$files_json" \
|
|
74
|
+
"$files_json" \
|
|
75
|
+
"compaction survival matrix seed" \
|
|
76
|
+
"$decisions" \
|
|
77
|
+
"Do not regress oauth-token-refresh token rotation after compaction" \
|
|
78
|
+
"src/auth-client.ts" \
|
|
79
|
+
"codex" >/dev/null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
seed_summary \
|
|
83
|
+
"$pre_session" \
|
|
84
|
+
"Create oauth-token-refresh compaction survival context" \
|
|
85
|
+
"oauth-token-refresh must survive compaction through summaries, memories, tasks, features, recall, and graph edges" \
|
|
86
|
+
"Stored durable oauth-token-refresh compaction survival context" \
|
|
87
|
+
"Decision: oauth-token-refresh is the durable compaction survival feature for auth replay"
|
|
88
|
+
seed_summary \
|
|
89
|
+
"compact-pre-session-2" \
|
|
90
|
+
"Record oauth-token-refresh retry decision" \
|
|
91
|
+
"Retry safety depends on rotated credentials" \
|
|
92
|
+
"Stored second enriched oauth-token-refresh summary" \
|
|
93
|
+
"Decision: oauth-token-refresh keeps retry safety explicit"
|
|
94
|
+
seed_summary \
|
|
95
|
+
"compact-pre-session-3" \
|
|
96
|
+
"Record oauth-token-refresh stale-token gotcha" \
|
|
97
|
+
"Stale refresh tokens fail silently without durable recall" \
|
|
98
|
+
"Stored third enriched oauth-token-refresh summary" \
|
|
99
|
+
"Decision: oauth-token-refresh stale-token gotcha must remain searchable"
|
|
100
|
+
|
|
101
|
+
eagle_insert_observation \
|
|
102
|
+
"$pre_session" \
|
|
103
|
+
"$project" \
|
|
104
|
+
"Edit" \
|
|
105
|
+
"Updated oauth-token-refresh compaction fixture" \
|
|
106
|
+
"$files_json" \
|
|
107
|
+
"$files_json" \
|
|
108
|
+
"" "" "" "codex" >/dev/null
|
|
109
|
+
|
|
110
|
+
memory_file="$tmp_dir/oauth-token-refresh-memory.md"
|
|
111
|
+
cat > "$memory_file" <<'EOF'
|
|
112
|
+
---
|
|
113
|
+
name: oauth-token-refresh memory
|
|
114
|
+
description: oauth-token-refresh compaction survival decision
|
|
115
|
+
type: decision
|
|
116
|
+
originSessionId: compact-pre-session
|
|
117
|
+
---
|
|
118
|
+
oauth-token-refresh must survive compaction and recall the auth client before edits.
|
|
119
|
+
EOF
|
|
120
|
+
eagle_capture_agent_memory "$memory_file" "$pre_session" "$project" "codex" >/dev/null
|
|
121
|
+
|
|
122
|
+
task_file="$tmp_dir/oauth-token-refresh-task.json"
|
|
123
|
+
cat > "$task_file" <<'EOF'
|
|
124
|
+
{
|
|
125
|
+
"id": "compact-task-oauth",
|
|
126
|
+
"subject": "Verify oauth-token-refresh compaction survival",
|
|
127
|
+
"description": "Ensure oauth-token-refresh survives compact through summary, memory, feature, recall, replay, and graph lookup.",
|
|
128
|
+
"activeForm": "Verifying oauth-token-refresh survival",
|
|
129
|
+
"status": "in_progress",
|
|
130
|
+
"blocks": [],
|
|
131
|
+
"blockedBy": []
|
|
132
|
+
}
|
|
133
|
+
EOF
|
|
134
|
+
eagle_capture_agent_task "$task_file" "$pre_session" "$project" "codex" >/dev/null
|
|
135
|
+
|
|
136
|
+
eagle_upsert_feature "$project" "oauth-token-refresh" "OAuth token refresh compaction survival feature" >/dev/null
|
|
137
|
+
feature_id=$(eagle_get_feature_id "$project" "oauth-token-refresh")
|
|
138
|
+
[ -n "$feature_id" ] || { echo "feature id missing" >&2; exit 1; }
|
|
139
|
+
eagle_add_feature_file "$feature_id" "src/auth-client.ts" "auth client" >/dev/null
|
|
140
|
+
eagle_add_feature_smoke_test "$feature_id" "bash tests/test_compaction_survival_matrix.sh" "compaction survival matrix" >/dev/null
|
|
141
|
+
|
|
142
|
+
(cd "$repo" && "$EAGLE_BIN" graph rebuild >/dev/null)
|
|
143
|
+
eagle_graph_wire_recent_session_edges "$project" 10 >/dev/null
|
|
144
|
+
|
|
145
|
+
for node_type in feature memory task session decision file; do
|
|
146
|
+
count=$(eagle_db "SELECT COUNT(*) FROM graph_nodes WHERE project = '$project' AND node_type = '$node_type';")
|
|
147
|
+
[ "${count:-0}" -gt 0 ] || {
|
|
148
|
+
echo "expected graph node type $node_type to survive, got $count" >&2
|
|
149
|
+
exit 1
|
|
150
|
+
}
|
|
151
|
+
done
|
|
152
|
+
|
|
153
|
+
semantic_edges=$(eagle_db "SELECT COUNT(*)
|
|
154
|
+
FROM graph_edges e
|
|
155
|
+
JOIN graph_nodes s ON s.id = e.source_node_id
|
|
156
|
+
JOIN graph_nodes t ON t.id = e.target_node_id
|
|
157
|
+
WHERE e.project = '$project'
|
|
158
|
+
AND (
|
|
159
|
+
(s.node_type = 'feature' AND t.node_type = 'file' AND e.edge_type = 'covers')
|
|
160
|
+
OR (s.node_type = 'memory' AND t.node_type = 'feature' AND e.edge_type = 'mentions')
|
|
161
|
+
OR (s.node_type = 'task' AND t.node_type = 'feature' AND e.edge_type = 'mentions')
|
|
162
|
+
OR (s.node_type = 'decision' AND t.node_type = 'feature' AND e.edge_type = 'mentions')
|
|
163
|
+
OR (s.node_type = 'decision' AND t.node_type = 'file' AND e.edge_type = 'touches')
|
|
164
|
+
);")
|
|
165
|
+
[ "${semantic_edges:-0}" -ge 5 ] || {
|
|
166
|
+
echo "expected semantic graph edges for durable context, got $semantic_edges" >&2
|
|
167
|
+
exit 1
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
summary_search=$(cd "$repo" && "$EAGLE_BIN" search "oauth token refresh compaction" --json)
|
|
171
|
+
assert_json "$summary_search" 'length >= 1 and (.[0].completed | contains("oauth-token-refresh"))' "summary search did not recover pre-compact decision"
|
|
172
|
+
|
|
173
|
+
memory_search=$(cd "$repo" && "$EAGLE_BIN" search --memories "oauth token refresh compaction" --json)
|
|
174
|
+
assert_json "$memory_search" 'length >= 1 and (.[0].memory_name | contains("oauth-token-refresh"))' "memory search did not recover mirrored memory"
|
|
175
|
+
|
|
176
|
+
task_search=$(cd "$repo" && "$EAGLE_BIN" search --tasks "oauth token refresh compaction" --json)
|
|
177
|
+
assert_json "$task_search" 'length >= 1 and (.[0].subject | contains("oauth-token-refresh"))' "task search did not recover active task"
|
|
178
|
+
|
|
179
|
+
feature_show=$(cd "$repo" && "$EAGLE_BIN" feature show "oauth-token-refresh")
|
|
180
|
+
assert_contains "$feature_show" "src/auth-client.ts" "feature show did not recover feature file binding"
|
|
181
|
+
|
|
182
|
+
compaction_json=$(cd "$repo" && "$EAGLE_BIN" compaction --json)
|
|
183
|
+
assert_json "$compaction_json" '
|
|
184
|
+
.status == "ok"
|
|
185
|
+
and .readiness == "strong"
|
|
186
|
+
and .metrics.enriched_summaries >= 3
|
|
187
|
+
and .metrics.active_tasks >= 1
|
|
188
|
+
and .metrics.durable_memories >= 1
|
|
189
|
+
and .metrics.active_features >= 1
|
|
190
|
+
and .metrics.semantic_graph_nodes >= 5
|
|
191
|
+
' "compaction --json did not report durable survival metrics"
|
|
192
|
+
|
|
193
|
+
eagle_upsert_session "$post_session" "$project" "$repo" "test-model" "test" "codex" >/dev/null
|
|
194
|
+
hook_input=$(jq -nc \
|
|
195
|
+
--arg sid "$post_session" \
|
|
196
|
+
--arg cwd "$repo" \
|
|
197
|
+
--arg prompt "Before editing auth client restore oauth-token-refresh compaction survival context" \
|
|
198
|
+
'{session_id:$sid, cwd:$cwd, prompt:$prompt}')
|
|
199
|
+
|
|
200
|
+
hook_output=$(EAGLE_MEM_PROJECT="$project" EAGLE_MEM_DIR="$EAGLE_MEM_DIR" bash "$ROOT_DIR/hooks/user-prompt-submit.sh" <<< "$hook_input")
|
|
201
|
+
assert_contains "$hook_output" "Eagle Mem recalls" "post-compact prompt did not receive recalled memory context"
|
|
202
|
+
assert_contains "$hook_output" "Relevant Code" "post-compact prompt did not receive indexed code context"
|
|
203
|
+
|
|
204
|
+
event_json=$(eagle_db_json "SELECT summary_matches, memory_matches, code_matches, summary_refs, memory_refs, code_refs, status
|
|
205
|
+
FROM recall_events
|
|
206
|
+
WHERE session_id = '$post_session'
|
|
207
|
+
ORDER BY id DESC
|
|
208
|
+
LIMIT 1;")
|
|
209
|
+
assert_json "$event_json" '
|
|
210
|
+
length == 1
|
|
211
|
+
and .[0].status == "ok"
|
|
212
|
+
and .[0].summary_matches >= 1
|
|
213
|
+
and .[0].memory_matches >= 1
|
|
214
|
+
and .[0].code_matches >= 1
|
|
215
|
+
and ((.[0].summary_refs | fromjson) | length >= 1)
|
|
216
|
+
and ((.[0].memory_refs | fromjson) | length >= 1)
|
|
217
|
+
and ((.[0].code_refs | fromjson) | length >= 1)
|
|
218
|
+
' "post-compact recall event did not capture expected refs"
|
|
219
|
+
|
|
220
|
+
replay_json=$(cd "$repo" && "$EAGLE_BIN" replay "$post_session" --json)
|
|
221
|
+
assert_json "$replay_json" '
|
|
222
|
+
.status == "ok"
|
|
223
|
+
and .session_id == "compact-post-session"
|
|
224
|
+
and (.recall_events | length >= 1)
|
|
225
|
+
and (.recall_events[0].summary_refs | length >= 1)
|
|
226
|
+
and (.recall_events[0].memory_refs | length >= 1)
|
|
227
|
+
and (.recall_events[0].code_refs | length >= 1)
|
|
228
|
+
' "replay did not preserve post-compact recall evidence"
|
|
229
|
+
|
|
230
|
+
neighbors_output=$(cd "$repo" && "$EAGLE_BIN" graph neighbors "oauth-token-refresh" | strip_ansi)
|
|
231
|
+
assert_contains "$neighbors_output" "src/auth-client.ts" "graph neighbors did not expose feature-file survival edge"
|
|
232
|
+
assert_contains "$neighbors_output" "compact-task-oauth" "graph neighbors did not expose task-feature survival edge"
|
|
233
|
+
|
|
234
|
+
compaction_after_recall=$(cd "$repo" && "$EAGLE_BIN" compaction --json)
|
|
235
|
+
assert_json "$compaction_after_recall" '.metrics.recall_events >= 1' "compaction metrics did not count post-compact recall event"
|
|
236
|
+
|
|
237
|
+
echo "compaction survival matrix regressions passed"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Dashboard regression: generated HTML should expose the human product surface
|
|
3
|
+
# promised by Eagle Mem's memory layer.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
7
|
+
|
|
8
|
+
tmp_dir=$(mktemp -d "$ROOT_DIR/.tmp-dashboard.XXXXXX")
|
|
9
|
+
trap 'rm -rf "$tmp_dir"' EXIT
|
|
10
|
+
|
|
11
|
+
export HOME="$tmp_dir/home"
|
|
12
|
+
export EAGLE_MEM_DIR="$tmp_dir/eagle-mem"
|
|
13
|
+
mkdir -p "$HOME" "$EAGLE_MEM_DIR"
|
|
14
|
+
|
|
15
|
+
. "$ROOT_DIR/lib/common.sh"
|
|
16
|
+
"$ROOT_DIR/db/migrate.sh" >/dev/null
|
|
17
|
+
. "$ROOT_DIR/lib/db.sh"
|
|
18
|
+
|
|
19
|
+
repo="$tmp_dir/repo"
|
|
20
|
+
mkdir -p "$repo"
|
|
21
|
+
project="project-dashboard"
|
|
22
|
+
project_sql=$(eagle_sql_escape "$project")
|
|
23
|
+
|
|
24
|
+
eagle_upsert_session "dash-session-1" "$project" "$repo" "test-model" "test" "codex" >/dev/null
|
|
25
|
+
eagle_insert_summary \
|
|
26
|
+
"dash-session-1" \
|
|
27
|
+
"$project" \
|
|
28
|
+
"Review dashboard memory surface" \
|
|
29
|
+
"Read docs and hooks" \
|
|
30
|
+
"Dashboard should reveal recall events" \
|
|
31
|
+
"Generated dashboard" \
|
|
32
|
+
"" \
|
|
33
|
+
"[]" \
|
|
34
|
+
"[]" \
|
|
35
|
+
"dashboard note" \
|
|
36
|
+
"Keep HTML for humans" \
|
|
37
|
+
"" \
|
|
38
|
+
"scripts/dashboard.sh" \
|
|
39
|
+
"codex" >/dev/null
|
|
40
|
+
|
|
41
|
+
eagle_upsert_overview "$project" "Dashboard project overview with current architecture and risks." "test" >/dev/null
|
|
42
|
+
eagle_insert_observation "dash-session-1" "$project" "Edit" "edited dashboard" "[]" "[\"scripts/dashboard.sh\"]" "" "" "" "codex" >/dev/null
|
|
43
|
+
eagle_insert_recall_event \
|
|
44
|
+
"dash-session-1" "$project" "$repo" "codex" \
|
|
45
|
+
"Show me what Eagle recalled for dashboard work" \
|
|
46
|
+
"eagle OR dashboard OR recall" \
|
|
47
|
+
2 1 3 960 "ok" "" >/dev/null
|
|
48
|
+
|
|
49
|
+
eagle_db "INSERT INTO agent_tasks (project, source_session_id, source_task_id, subject, description, active_form, status, content_hash, origin_agent)
|
|
50
|
+
VALUES ('$project_sql', 'dash-session-1', 'task-dashboard', 'Build dashboard surface',
|
|
51
|
+
'Expose memory inspection in HTML', 'Build dashboard surface', 'in_progress',
|
|
52
|
+
'dashboard-task-hash', 'codex');" >/dev/null
|
|
53
|
+
|
|
54
|
+
eagle_graph_add_node "$project" "project" "$project" "Dashboard graph project" "" >/dev/null
|
|
55
|
+
eagle_graph_add_node "$project" "file" "scripts/dashboard.sh" "" "$repo/scripts/dashboard.sh" >/dev/null
|
|
56
|
+
|
|
57
|
+
dashboard_file="$tmp_dir/dashboard/index.html"
|
|
58
|
+
dashboard_json=$(cd "$repo" && EAGLE_MEM_DIR="$EAGLE_MEM_DIR" "$ROOT_DIR/bin/eagle-mem" dashboard --project "$project" --output "$dashboard_file" --json)
|
|
59
|
+
|
|
60
|
+
printf '%s' "$dashboard_json" | jq -e '
|
|
61
|
+
.status == "ok"
|
|
62
|
+
and .output != ""
|
|
63
|
+
and .counts.summaries >= 1
|
|
64
|
+
and .counts.recall_events >= 1
|
|
65
|
+
and .counts.files >= 1
|
|
66
|
+
and .counts.active_tasks >= 1
|
|
67
|
+
and .counts.graph_node_types >= 1
|
|
68
|
+
' >/dev/null || {
|
|
69
|
+
echo "dashboard --json did not report expected counts" >&2
|
|
70
|
+
echo "$dashboard_json" >&2
|
|
71
|
+
exit 1
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
[ -f "$dashboard_file" ] || {
|
|
75
|
+
echo "dashboard output file was not created" >&2
|
|
76
|
+
exit 1
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for needle in \
|
|
80
|
+
"Eagle Mem Dashboard" \
|
|
81
|
+
"Project Brain" \
|
|
82
|
+
"Recall Inspector" \
|
|
83
|
+
"Timeline" \
|
|
84
|
+
"File Intelligence" \
|
|
85
|
+
"Agent Comparison" \
|
|
86
|
+
"Active Tasks" \
|
|
87
|
+
"Graph Readiness" \
|
|
88
|
+
"Show me what Eagle recalled" \
|
|
89
|
+
"scripts/dashboard.sh"; do
|
|
90
|
+
if ! grep -q "$needle" "$dashboard_file"; then
|
|
91
|
+
echo "dashboard missing expected content: $needle" >&2
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
done
|
|
95
|
+
|
|
96
|
+
echo "dashboard regressions passed"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Hooks should emit a general Eagle event log so users can inspect what Eagle
|
|
3
|
+
# did, not only the final memory rows.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
7
|
+
|
|
8
|
+
tmp_dir=$(mktemp -d "$ROOT_DIR/.tmp-eagle-events.XXXXXX")
|
|
9
|
+
trap 'rm -rf "$tmp_dir"' EXIT
|
|
10
|
+
|
|
11
|
+
export HOME="$tmp_dir/home"
|
|
12
|
+
export EAGLE_MEM_DIR="$tmp_dir/eagle-mem"
|
|
13
|
+
mkdir -p "$HOME" "$EAGLE_MEM_DIR"
|
|
14
|
+
|
|
15
|
+
. "$ROOT_DIR/lib/common.sh"
|
|
16
|
+
"$ROOT_DIR/db/migrate.sh" >/dev/null
|
|
17
|
+
. "$ROOT_DIR/lib/db.sh"
|
|
18
|
+
|
|
19
|
+
repo="$tmp_dir/repo"
|
|
20
|
+
mkdir -p "$repo"
|
|
21
|
+
project="project-events"
|
|
22
|
+
|
|
23
|
+
eagle_upsert_session "seed-events-session" "$project" "$repo" "test-model" "test" "codex" >/dev/null
|
|
24
|
+
eagle_insert_summary \
|
|
25
|
+
"seed-events-session" \
|
|
26
|
+
"$project" \
|
|
27
|
+
"Implement hook event logging" \
|
|
28
|
+
"Read hook observability code" \
|
|
29
|
+
"Hooks need visible events" \
|
|
30
|
+
"Added event logging" \
|
|
31
|
+
"" \
|
|
32
|
+
"[]" \
|
|
33
|
+
"[]" \
|
|
34
|
+
"event note" \
|
|
35
|
+
"Record hook_started and hook_completed" \
|
|
36
|
+
"" \
|
|
37
|
+
"hooks/user-prompt-submit.sh" \
|
|
38
|
+
"codex" >/dev/null
|
|
39
|
+
|
|
40
|
+
hook_input=$(jq -nc \
|
|
41
|
+
--arg sid "event-hook-session" \
|
|
42
|
+
--arg cwd "$repo" \
|
|
43
|
+
--arg prompt "Please review hook event logging before editing observability" \
|
|
44
|
+
'{session_id:$sid, cwd:$cwd, prompt:$prompt, hook_event_name:"UserPromptSubmit"}')
|
|
45
|
+
|
|
46
|
+
hook_output=$(EAGLE_MEM_PROJECT="$project" EAGLE_MEM_DIR="$EAGLE_MEM_DIR" EAGLE_AGENT_SOURCE="codex" bash "$ROOT_DIR/hooks/user-prompt-submit.sh" <<< "$hook_input")
|
|
47
|
+
case "$hook_output" in
|
|
48
|
+
*"Eagle Mem recalls"*|*"Relevant"*) ;;
|
|
49
|
+
*)
|
|
50
|
+
echo "UserPromptSubmit did not inject event-test context" >&2
|
|
51
|
+
echo "$hook_output" >&2
|
|
52
|
+
exit 1
|
|
53
|
+
;;
|
|
54
|
+
esac
|
|
55
|
+
|
|
56
|
+
events_json=$(eagle_db_json "SELECT event_type, hook_event_name, status, detail_json
|
|
57
|
+
FROM eagle_events
|
|
58
|
+
WHERE session_id = 'event-hook-session'
|
|
59
|
+
ORDER BY id ASC;")
|
|
60
|
+
|
|
61
|
+
printf '%s' "$events_json" | jq -e '
|
|
62
|
+
length >= 3
|
|
63
|
+
and any(.[]; .event_type == "hook_started" and .hook_event_name == "UserPromptSubmit")
|
|
64
|
+
and any(.[]; .event_type == "context_injected" and .hook_event_name == "UserPromptSubmit")
|
|
65
|
+
and any(.[]; .event_type == "hook_completed" and .hook_event_name == "UserPromptSubmit" and .status == "ok")
|
|
66
|
+
and any(.[]; .event_type == "hook_completed" and ((.detail_json | fromjson).injected_chars > 0))
|
|
67
|
+
' >/dev/null || {
|
|
68
|
+
echo "eagle_events did not capture expected UserPromptSubmit lifecycle" >&2
|
|
69
|
+
echo "$events_json" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
inspect_json=$(cd "$repo" && EAGLE_MEM_DIR="$EAGLE_MEM_DIR" "$ROOT_DIR/bin/eagle-mem" inspect events --project "$project" --session event-hook-session --json)
|
|
74
|
+
printf '%s' "$inspect_json" | jq -e '
|
|
75
|
+
.status == "ok"
|
|
76
|
+
and .action == "events"
|
|
77
|
+
and (.events | length >= 3)
|
|
78
|
+
and any(.events[]; .event_type == "context_injected" and .detail.injected_chars > 0)
|
|
79
|
+
' >/dev/null || {
|
|
80
|
+
echo "inspect events --json did not return event details" >&2
|
|
81
|
+
echo "$inspect_json" >&2
|
|
82
|
+
exit 1
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
inspect_text=$(cd "$repo" && EAGLE_MEM_DIR="$EAGLE_MEM_DIR" "$ROOT_DIR/bin/eagle-mem" inspect events --project "$project" --session event-hook-session --last)
|
|
86
|
+
case "$inspect_text" in
|
|
87
|
+
*"Event Inspector"*hook_completed*UserPromptSubmit*) ;;
|
|
88
|
+
*)
|
|
89
|
+
echo "inspect events --last did not render hook event" >&2
|
|
90
|
+
echo "$inspect_text" >&2
|
|
91
|
+
exit 1
|
|
92
|
+
;;
|
|
93
|
+
esac
|
|
94
|
+
|
|
95
|
+
echo "eagle event regressions passed"
|
|
96
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Regression coverage for OpenCode plugin and skill registration helpers.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
6
|
+
tmp_dir=$(mktemp -d "$ROOT_DIR/.tmp-opencode-hooks.XXXXXX")
|
|
7
|
+
trap 'rm -rf "$tmp_dir"' EXIT
|
|
8
|
+
|
|
9
|
+
export HOME="$tmp_dir/home"
|
|
10
|
+
export EAGLE_MEM_DIR="$tmp_dir/eagle-mem"
|
|
11
|
+
mkdir -p "$HOME" "$EAGLE_MEM_DIR"
|
|
12
|
+
|
|
13
|
+
. "$ROOT_DIR/scripts/style.sh"
|
|
14
|
+
. "$ROOT_DIR/lib/common.sh"
|
|
15
|
+
. "$ROOT_DIR/lib/opencode-hooks.sh"
|
|
16
|
+
|
|
17
|
+
fail() {
|
|
18
|
+
echo "opencode hooks config test failed: $*" >&2
|
|
19
|
+
exit 1
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
first_skill=$(find "$ROOT_DIR/skills" -mindepth 1 -maxdepth 1 -type d -name "eagle-mem-*" | sort | head -n 1)
|
|
23
|
+
[ -n "$first_skill" ] || fail "no Eagle Mem skills found for OpenCode symlink test"
|
|
24
|
+
first_skill_name=$(basename "$first_skill")
|
|
25
|
+
|
|
26
|
+
mkdir -p "$EAGLE_OPENCODE_DIR"
|
|
27
|
+
eagle_opencode_detected || fail "OpenCode should be detected when config directory exists"
|
|
28
|
+
|
|
29
|
+
eagle_install_opencode_plugin "$ROOT_DIR" "0" >/dev/null
|
|
30
|
+
[ -f "$EAGLE_OPENCODE_PLUGIN" ] || fail "plugin was not installed"
|
|
31
|
+
grep -q "EAGLE_MEM_OPENCODE_PLUGIN" "$EAGLE_OPENCODE_PLUGIN" || fail "installed plugin is missing Eagle Mem ownership marker"
|
|
32
|
+
[ "$(eagle_opencode_plugin_state)" = "registered" ] || fail "plugin state should be registered"
|
|
33
|
+
|
|
34
|
+
eagle_install_opencode_plugin "$ROOT_DIR" "0" >/dev/null
|
|
35
|
+
[ "$(eagle_opencode_plugin_state)" = "registered" ] || fail "plugin reinstall should be idempotent"
|
|
36
|
+
|
|
37
|
+
eagle_install_opencode_skills "$ROOT_DIR" "0" >/dev/null
|
|
38
|
+
[ -L "$EAGLE_OPENCODE_SKILLS_DIR/$first_skill_name" ] || fail "OpenCode skill symlink was not created"
|
|
39
|
+
mkdir -p "$EAGLE_OPENCODE_SKILLS_DIR/eagle-mem-user-directory"
|
|
40
|
+
|
|
41
|
+
eagle_remove_opencode_plugin || fail "owned plugin should be removable"
|
|
42
|
+
[ ! -f "$EAGLE_OPENCODE_PLUGIN" ] || fail "owned plugin still exists after removal"
|
|
43
|
+
|
|
44
|
+
eagle_remove_opencode_skills >/dev/null || true
|
|
45
|
+
[ ! -e "$EAGLE_OPENCODE_SKILLS_DIR/$first_skill_name" ] || fail "OpenCode skill symlink still exists after removal"
|
|
46
|
+
[ -d "$EAGLE_OPENCODE_SKILLS_DIR/eagle-mem-user-directory" ] || fail "OpenCode skill removal deleted a real directory"
|
|
47
|
+
|
|
48
|
+
mkdir -p "$EAGLE_OPENCODE_PLUGINS_DIR"
|
|
49
|
+
printf '%s\n' "custom opencode plugin" > "$EAGLE_OPENCODE_PLUGIN"
|
|
50
|
+
if eagle_install_opencode_plugin "$ROOT_DIR" "0" >/dev/null 2>&1; then
|
|
51
|
+
fail "custom OpenCode plugin should not be overwritten"
|
|
52
|
+
fi
|
|
53
|
+
grep -q "custom opencode plugin" "$EAGLE_OPENCODE_PLUGIN" || fail "custom plugin contents were overwritten"
|
|
54
|
+
[ "$(eagle_opencode_plugin_state)" = "custom" ] || fail "custom plugin state should be reported"
|
|
55
|
+
|
|
56
|
+
echo "opencode hooks config test passed"
|