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
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eagle-mem",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "Shared memory, release guardrails, RTK token protection, and worker lanes for Claude Code, Codex, Grok, and Google Antigravity",
|
|
3
|
+
"version": "4.12.0",
|
|
4
|
+
"description": "Shared memory, release guardrails, RTK token protection, and worker lanes for Claude Code, Codex, OpenCode, Grok, and Google Antigravity",
|
|
5
5
|
"bin": {
|
|
6
6
|
"eagle-mem": "bin/eagle-mem"
|
|
7
7
|
},
|
|
@@ -20,11 +20,14 @@
|
|
|
20
20
|
"docs/",
|
|
21
21
|
"architecture.html",
|
|
22
22
|
"integrations/*.py",
|
|
23
|
+
"integrations/*.js",
|
|
24
|
+
"tests/fixtures/",
|
|
23
25
|
"CHANGELOG.md"
|
|
24
26
|
],
|
|
25
27
|
"keywords": [
|
|
26
28
|
"claude-code",
|
|
27
29
|
"codex",
|
|
30
|
+
"opencode",
|
|
28
31
|
"grok",
|
|
29
32
|
"memory",
|
|
30
33
|
"sqlite",
|
package/scripts/compaction.sh
CHANGED
|
@@ -12,20 +12,117 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
|
12
12
|
. "$LIB_DIR/common.sh"
|
|
13
13
|
. "$LIB_DIR/db.sh"
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
json_output=false
|
|
16
|
+
project=""
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
while [ $# -gt 0 ]; do
|
|
19
|
+
case "$1" in
|
|
20
|
+
--json|-j) json_output=true; shift ;;
|
|
21
|
+
--project|-p) project="$2"; shift 2 ;;
|
|
22
|
+
--help|-h)
|
|
23
|
+
echo -e " ${BOLD}eagle-mem compaction${RESET} — Compaction Survival status"
|
|
24
|
+
echo ""
|
|
25
|
+
echo -e " ${BOLD}Usage:${RESET}"
|
|
26
|
+
echo -e " eagle-mem compaction"
|
|
27
|
+
echo -e " eagle-mem compaction --json"
|
|
28
|
+
echo -e " eagle-mem compaction --project <project>"
|
|
29
|
+
exit 0
|
|
30
|
+
;;
|
|
31
|
+
*) shift ;;
|
|
32
|
+
esac
|
|
33
|
+
done
|
|
34
|
+
|
|
35
|
+
compaction_fail() {
|
|
36
|
+
local error_code="$1"
|
|
37
|
+
local message="$2"
|
|
38
|
+
local db_status="${3:-unknown}"
|
|
39
|
+
local db_detail="${4:-}"
|
|
19
40
|
|
|
20
|
-
|
|
41
|
+
if [ "$json_output" = true ]; then
|
|
42
|
+
jq -nc \
|
|
43
|
+
--arg status "error" \
|
|
44
|
+
--arg command "compaction" \
|
|
45
|
+
--arg error "$error_code" \
|
|
46
|
+
--arg message "$message" \
|
|
47
|
+
--arg project "${project:-}" \
|
|
48
|
+
--arg db_status "$db_status" \
|
|
49
|
+
--arg db_detail "$db_detail" \
|
|
50
|
+
'{status:$status, command:$command, error:$error, message:$message,
|
|
51
|
+
project:$project, database:{integrity:{status:$db_status, detail:$db_detail}}}'
|
|
52
|
+
else
|
|
53
|
+
eagle_fail "$message"
|
|
54
|
+
[ -n "$db_detail" ] && eagle_dim " $db_detail"
|
|
55
|
+
fi
|
|
56
|
+
exit 1
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if [ -z "$project" ]; then
|
|
60
|
+
project=$(eagle_project_from_cwd "$(pwd)")
|
|
61
|
+
fi
|
|
21
62
|
project_sql=$(eagle_sql_escape "$project")
|
|
22
63
|
|
|
64
|
+
if ! eagle_ensure_db; then
|
|
65
|
+
compaction_fail "database_unavailable" "Database is unavailable; SQLite/FTS5 setup failed." "unavailable" "eagle_ensure_db failed"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
db_integrity_check=$(eagle_db_integrity_status "$EAGLE_MEM_DB" 2>/dev/null || true)
|
|
69
|
+
db_integrity_status="${db_integrity_check%%|*}"
|
|
70
|
+
db_integrity_detail="${db_integrity_check#*|}"
|
|
71
|
+
[ -n "$db_integrity_status" ] || db_integrity_status="unknown"
|
|
72
|
+
[ -n "$db_integrity_detail" ] || db_integrity_detail="not checked"
|
|
73
|
+
if [ "$db_integrity_status" != "ok" ]; then
|
|
74
|
+
compaction_fail "database_integrity" "Database integrity check failed; compaction state is unavailable." "$db_integrity_status" "$db_integrity_detail"
|
|
75
|
+
fi
|
|
76
|
+
|
|
23
77
|
# --- Metrics ---
|
|
24
78
|
enriched=$(eagle_db "SELECT COUNT(*) FROM summaries WHERE project='$project_sql' AND (learned != '' OR decisions != '' OR gotchas != '')" 2>/dev/null || echo 0)
|
|
25
79
|
total_summaries=$(eagle_db "SELECT COUNT(*) FROM summaries WHERE project='$project_sql'" 2>/dev/null || echo 0)
|
|
26
80
|
active_tasks=$(eagle_db "SELECT COUNT(*) FROM agent_tasks WHERE project='$project_sql' AND status IN ('pending','in_progress')" 2>/dev/null || echo 0)
|
|
27
81
|
stale_tasks=$(eagle_db "SELECT COUNT(*) FROM agent_tasks WHERE project='$project_sql' AND status='in_progress' AND updated_at < datetime('now','-7 days')" 2>/dev/null || echo 0)
|
|
82
|
+
durable_memories=$(eagle_db "SELECT COUNT(*) FROM agent_memories WHERE project='$project_sql'" 2>/dev/null || echo 0)
|
|
83
|
+
active_features=$(eagle_db "SELECT COUNT(*) FROM features WHERE project='$project_sql' AND status='active'" 2>/dev/null || echo 0)
|
|
84
|
+
recall_events=$(eagle_db "SELECT COUNT(*) FROM recall_events WHERE project='$project_sql'" 2>/dev/null || echo 0)
|
|
85
|
+
semantic_graph_nodes=$(eagle_db "SELECT COUNT(*) FROM graph_nodes WHERE project='$project_sql' AND node_type IN ('feature','memory','task','session','decision')" 2>/dev/null || echo 0)
|
|
28
86
|
last_capture=$(eagle_db "SELECT MAX(updated_at) FROM agent_tasks WHERE project='$project_sql'" 2>/dev/null || echo "never")
|
|
87
|
+
active_lanes=$(eagle_db "SELECT COUNT(*) FROM orchestration_lanes WHERE project='$project_sql' AND status NOT IN ('completed', 'cancelled')" 2>/dev/null || echo 0)
|
|
88
|
+
|
|
89
|
+
readiness="weak"
|
|
90
|
+
if [ "$enriched" -ge 3 ] && [ "$active_tasks" -gt 0 ]; then
|
|
91
|
+
readiness="strong"
|
|
92
|
+
elif [ "$enriched" -ge 1 ]; then
|
|
93
|
+
readiness="moderate"
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
if [ "$json_output" = true ]; then
|
|
97
|
+
jq -nc \
|
|
98
|
+
--arg status "ok" \
|
|
99
|
+
--arg project "$project" \
|
|
100
|
+
--arg db_integrity_status "$db_integrity_status" \
|
|
101
|
+
--arg db_integrity_detail "$db_integrity_detail" \
|
|
102
|
+
--argjson enriched "${enriched:-0}" \
|
|
103
|
+
--argjson total_summaries "${total_summaries:-0}" \
|
|
104
|
+
--argjson active_tasks "${active_tasks:-0}" \
|
|
105
|
+
--argjson stale_tasks "${stale_tasks:-0}" \
|
|
106
|
+
--argjson durable_memories "${durable_memories:-0}" \
|
|
107
|
+
--argjson active_features "${active_features:-0}" \
|
|
108
|
+
--argjson recall_events "${recall_events:-0}" \
|
|
109
|
+
--argjson semantic_graph_nodes "${semantic_graph_nodes:-0}" \
|
|
110
|
+
--arg last_capture "${last_capture:-never}" \
|
|
111
|
+
--argjson active_lanes "${active_lanes:-0}" \
|
|
112
|
+
--arg readiness "$readiness" \
|
|
113
|
+
'{status:$status, project:$project,
|
|
114
|
+
database:{integrity:{status:$db_integrity_status, detail:$db_integrity_detail}},
|
|
115
|
+
metrics:{enriched_summaries:$enriched, total_summaries:$total_summaries,
|
|
116
|
+
active_tasks:$active_tasks, stale_tasks:$stale_tasks,
|
|
117
|
+
durable_memories:$durable_memories, active_features:$active_features,
|
|
118
|
+
recall_events:$recall_events, semantic_graph_nodes:$semantic_graph_nodes,
|
|
119
|
+
last_durable_update:$last_capture, active_orchestration_lanes:$active_lanes},
|
|
120
|
+
readiness:$readiness}'
|
|
121
|
+
exit 0
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
eagle_banner
|
|
125
|
+
eagle_header "Compaction Survival"
|
|
29
126
|
|
|
30
127
|
echo ""
|
|
31
128
|
echo -e " Project: ${BOLD}$project${RESET}"
|
|
@@ -35,6 +132,10 @@ echo -e " ${BOLD}Context Survival Metrics${RESET}"
|
|
|
35
132
|
echo -e " ─────────────────────────────────────"
|
|
36
133
|
echo -e " Enriched summaries: ${GREEN}$enriched${RESET} / $total_summaries"
|
|
37
134
|
echo -e " Active durable tasks: ${CYAN}$active_tasks${RESET}"
|
|
135
|
+
echo -e " Durable memories: ${CYAN}$durable_memories${RESET}"
|
|
136
|
+
echo -e " Active features: ${CYAN}$active_features${RESET}"
|
|
137
|
+
echo -e " Recall events: ${CYAN}$recall_events${RESET}"
|
|
138
|
+
echo -e " Semantic graph nodes: ${CYAN}$semantic_graph_nodes${RESET}"
|
|
38
139
|
echo -e " Stale in_progress tasks:${RED}$stale_tasks${RESET}"
|
|
39
140
|
echo -e " Last durable update: $last_capture"
|
|
40
141
|
|
|
@@ -46,20 +147,19 @@ else
|
|
|
46
147
|
fi
|
|
47
148
|
|
|
48
149
|
# Orchestration lanes (for cross-agent long-running work survival)
|
|
49
|
-
active_lanes=$(eagle_db "SELECT COUNT(*) FROM orchestration_lanes WHERE project='$project_sql' AND status NOT IN ('completed', 'cancelled')" 2>/dev/null || echo 0)
|
|
50
150
|
echo -e " Active orchestration lanes: ${CYAN}$active_lanes${RESET}"
|
|
51
151
|
if [ "$active_lanes" -gt 0 ]; then
|
|
52
152
|
eagle_info "Long-running work is tracked in durable lanes — use 'eagle-mem orchestrate' to manage"
|
|
53
153
|
fi
|
|
54
154
|
|
|
55
|
-
if [ "$
|
|
155
|
+
if [ "$readiness" = "strong" ]; then
|
|
56
156
|
eagle_ok "Compaction Survival: Strong — future sessions will have good context"
|
|
57
|
-
elif [ "$
|
|
157
|
+
elif [ "$readiness" = "moderate" ]; then
|
|
58
158
|
eagle_info "Compaction Survival: Moderate — add more durable tasks and summaries"
|
|
59
159
|
else
|
|
60
|
-
eagle_warn "Compaction Survival: Weak — start using durable tasks and
|
|
160
|
+
eagle_warn "Compaction Survival: Weak — start using durable tasks and 'eagle-mem session save' captures"
|
|
61
161
|
fi
|
|
62
162
|
|
|
63
163
|
echo ""
|
|
64
164
|
eagle_dim "Run this anytime to check how safe the project is from /compact amnesia."
|
|
65
|
-
echo ""
|
|
165
|
+
echo ""
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════
|
|
3
|
+
# Eagle Mem — Local Dashboard
|
|
4
|
+
# Generates a static HTML inspection surface from ~/.eagle-mem/memory.db.
|
|
5
|
+
# ═══════════════════════════════════════════════════════════
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
10
|
+
|
|
11
|
+
. "$SCRIPTS_DIR/style.sh"
|
|
12
|
+
. "$LIB_DIR/common.sh"
|
|
13
|
+
. "$LIB_DIR/db.sh"
|
|
14
|
+
|
|
15
|
+
project=""
|
|
16
|
+
project_was_explicit=false
|
|
17
|
+
cross_project=false
|
|
18
|
+
output_path=""
|
|
19
|
+
json_output=false
|
|
20
|
+
open_after=false
|
|
21
|
+
|
|
22
|
+
show_help() {
|
|
23
|
+
echo -e " ${BOLD}eagle-mem dashboard${RESET} — Generate a local HTML memory dashboard"
|
|
24
|
+
echo ""
|
|
25
|
+
echo -e " ${BOLD}Usage:${RESET}"
|
|
26
|
+
echo -e " eagle-mem dashboard"
|
|
27
|
+
echo -e " eagle-mem dashboard ${CYAN}--project <name>${RESET}"
|
|
28
|
+
echo -e " eagle-mem dashboard ${CYAN}--output <path>${RESET}"
|
|
29
|
+
echo ""
|
|
30
|
+
echo -e " ${BOLD}Options:${RESET}"
|
|
31
|
+
echo -e " ${CYAN}-p, --project${RESET} <name> Project name (default: current dir scope)"
|
|
32
|
+
echo -e " ${CYAN}--all${RESET} Include all projects"
|
|
33
|
+
echo -e " ${CYAN}-o, --output${RESET} <path> Output file (default: ~/.eagle-mem/dashboard/index.html)"
|
|
34
|
+
echo -e " ${CYAN}--open${RESET} Open the generated dashboard"
|
|
35
|
+
echo -e " ${CYAN}-j, --json${RESET} Output structured JSON"
|
|
36
|
+
echo ""
|
|
37
|
+
exit 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
while [ $# -gt 0 ]; do
|
|
41
|
+
case "$1" in
|
|
42
|
+
--project|-p) project="$2"; project_was_explicit=true; shift 2 ;;
|
|
43
|
+
--all|-a) cross_project=true; shift ;;
|
|
44
|
+
--output|-o) output_path="$2"; shift 2 ;;
|
|
45
|
+
--open) open_after=true; shift ;;
|
|
46
|
+
--json|-j) json_output=true; shift ;;
|
|
47
|
+
--help|-h) show_help ;;
|
|
48
|
+
*)
|
|
49
|
+
eagle_err "Unknown option: $1"
|
|
50
|
+
exit 1
|
|
51
|
+
;;
|
|
52
|
+
esac
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
dashboard_fail() {
|
|
56
|
+
local error_code="$1"
|
|
57
|
+
local message="$2"
|
|
58
|
+
local db_status="${3:-unknown}"
|
|
59
|
+
local db_detail="${4:-}"
|
|
60
|
+
|
|
61
|
+
if [ "$json_output" = true ]; then
|
|
62
|
+
jq -nc \
|
|
63
|
+
--arg status "error" \
|
|
64
|
+
--arg command "dashboard" \
|
|
65
|
+
--arg error "$error_code" \
|
|
66
|
+
--arg message "$message" \
|
|
67
|
+
--arg project "${project:-}" \
|
|
68
|
+
--arg output "${output_path:-}" \
|
|
69
|
+
--arg db_status "$db_status" \
|
|
70
|
+
--arg db_detail "$db_detail" \
|
|
71
|
+
'{status:$status, command:$command, error:$error, message:$message,
|
|
72
|
+
project:$project, output:$output,
|
|
73
|
+
database:{integrity:{status:$db_status, detail:$db_detail}}}'
|
|
74
|
+
else
|
|
75
|
+
eagle_err "$message"
|
|
76
|
+
[ -n "$db_detail" ] && eagle_dim " $db_detail"
|
|
77
|
+
fi
|
|
78
|
+
exit 1
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
html_escape() {
|
|
82
|
+
jq -Rn --arg v "${1:-}" '$v | @html'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
json_len() {
|
|
86
|
+
printf '%s' "${1:-[]}" | jq 'length' 2>/dev/null || printf '0\n'
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if ! eagle_ensure_db; then
|
|
90
|
+
dashboard_fail "database_unavailable" "Database is unavailable; SQLite/FTS5 setup failed." "unavailable" "eagle_ensure_db failed"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
db_integrity_check=$(eagle_db_integrity_status "$EAGLE_MEM_DB" 2>/dev/null || true)
|
|
94
|
+
db_integrity_status="${db_integrity_check%%|*}"
|
|
95
|
+
db_integrity_detail="${db_integrity_check#*|}"
|
|
96
|
+
[ -n "$db_integrity_status" ] || db_integrity_status="unknown"
|
|
97
|
+
[ -n "$db_integrity_detail" ] || db_integrity_detail="not checked"
|
|
98
|
+
if [ "$db_integrity_status" != "ok" ]; then
|
|
99
|
+
dashboard_fail "database_integrity" "Database integrity check failed; dashboard is unavailable." "$db_integrity_status" "$db_integrity_detail"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
if [ -z "$project" ] && [ "$cross_project" = false ]; then
|
|
103
|
+
project=$(eagle_project_from_cwd "$(pwd)")
|
|
104
|
+
fi
|
|
105
|
+
if [ "$cross_project" = false ] && [ "$project_was_explicit" = false ]; then
|
|
106
|
+
project=$(eagle_recall_project_scope_from_cwd "$(pwd)" "$project")
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
project_label="$project"
|
|
110
|
+
if [ "$cross_project" = true ]; then
|
|
111
|
+
project_label="All projects"
|
|
112
|
+
else
|
|
113
|
+
project_label=$(eagle_project_scope_label "$project")
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
[ -n "$output_path" ] || output_path="$EAGLE_MEM_DIR/dashboard/index.html"
|
|
117
|
+
mkdir -p "$(dirname "$output_path")"
|
|
118
|
+
|
|
119
|
+
where_project="1 = 1"
|
|
120
|
+
if [ "$cross_project" = false ]; then
|
|
121
|
+
where_project=$(eagle_sql_project_scope_condition "project" "$project")
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
where_summary="1 = 1"
|
|
125
|
+
where_observation="1 = 1"
|
|
126
|
+
where_task="1 = 1"
|
|
127
|
+
where_graph="1 = 1"
|
|
128
|
+
if [ "$cross_project" = false ]; then
|
|
129
|
+
where_summary=$(eagle_sql_project_scope_condition "project" "$project")
|
|
130
|
+
where_observation=$(eagle_sql_project_scope_condition "project" "$project")
|
|
131
|
+
where_task=$(eagle_sql_project_scope_condition "project" "$project")
|
|
132
|
+
where_graph=$(eagle_sql_project_scope_condition "project" "$project")
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
overview_json="[]"
|
|
136
|
+
if [ "$cross_project" = false ]; then
|
|
137
|
+
overview_json=$(eagle_db_json "SELECT content, source, updated_at
|
|
138
|
+
FROM overviews
|
|
139
|
+
WHERE $where_project
|
|
140
|
+
ORDER BY updated_at DESC
|
|
141
|
+
LIMIT 1;" 2>/dev/null || printf '[]')
|
|
142
|
+
fi
|
|
143
|
+
summary_json=$(eagle_db_json "SELECT request, completed, learned, decisions, gotchas, key_files, agent, created_at
|
|
144
|
+
FROM summaries
|
|
145
|
+
WHERE $where_summary
|
|
146
|
+
ORDER BY created_at DESC
|
|
147
|
+
LIMIT 10;" 2>/dev/null || printf '[]')
|
|
148
|
+
recall_json="[]"
|
|
149
|
+
if [ -n "$(eagle_db "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'recall_events' LIMIT 1;" 2>/dev/null || true)" ]; then
|
|
150
|
+
recall_json=$(eagle_db_json "SELECT prompt_snippet, fts_query, summary_matches, memory_matches, code_matches,
|
|
151
|
+
injected_token_estimate, status, agent, created_at
|
|
152
|
+
FROM recall_events
|
|
153
|
+
WHERE $where_project
|
|
154
|
+
ORDER BY created_at DESC, id DESC
|
|
155
|
+
LIMIT 10;" 2>/dev/null || printf '[]')
|
|
156
|
+
fi
|
|
157
|
+
files_json=$(eagle_db_json "SELECT json_each.value AS file, COUNT(*) AS touches
|
|
158
|
+
FROM observations, json_each(observations.files_modified)
|
|
159
|
+
WHERE $where_observation
|
|
160
|
+
GROUP BY json_each.value
|
|
161
|
+
ORDER BY touches DESC
|
|
162
|
+
LIMIT 12;" 2>/dev/null || printf '[]')
|
|
163
|
+
agents_json=$(eagle_db_json "SELECT agent, COUNT(*) AS sessions
|
|
164
|
+
FROM sessions
|
|
165
|
+
WHERE $where_project
|
|
166
|
+
GROUP BY agent
|
|
167
|
+
ORDER BY sessions DESC;" 2>/dev/null || printf '[]')
|
|
168
|
+
tasks_json=$(eagle_db_json "SELECT source_task_id, subject, status, updated_at
|
|
169
|
+
FROM agent_tasks
|
|
170
|
+
WHERE $where_task AND status IN ('pending', 'in_progress', 'blocked')
|
|
171
|
+
ORDER BY updated_at DESC
|
|
172
|
+
LIMIT 10;" 2>/dev/null || printf '[]')
|
|
173
|
+
graph_json=$(eagle_db_json "SELECT node_type, COUNT(*) AS nodes
|
|
174
|
+
FROM graph_nodes
|
|
175
|
+
WHERE $where_graph
|
|
176
|
+
GROUP BY node_type
|
|
177
|
+
ORDER BY nodes DESC;" 2>/dev/null || printf '[]')
|
|
178
|
+
|
|
179
|
+
overview_count=$(json_len "$overview_json")
|
|
180
|
+
summary_count=$(json_len "$summary_json")
|
|
181
|
+
recall_count=$(json_len "$recall_json")
|
|
182
|
+
file_count=$(json_len "$files_json")
|
|
183
|
+
agent_count=$(json_len "$agents_json")
|
|
184
|
+
task_count=$(json_len "$tasks_json")
|
|
185
|
+
graph_type_count=$(json_len "$graph_json")
|
|
186
|
+
edge_count=$(eagle_db "SELECT COUNT(*) FROM graph_edges WHERE $where_graph;" 2>/dev/null || printf '0')
|
|
187
|
+
|
|
188
|
+
render_summary_rows() {
|
|
189
|
+
printf '%s' "$summary_json" | jq -c '.[]' | while IFS= read -r row; do
|
|
190
|
+
local created request completed learned agent
|
|
191
|
+
created=$(printf '%s' "$row" | jq -r '.created_at // ""')
|
|
192
|
+
request=$(printf '%s' "$row" | jq -r '.request // "Session summary"')
|
|
193
|
+
completed=$(printf '%s' "$row" | jq -r '.completed // ""')
|
|
194
|
+
learned=$(printf '%s' "$row" | jq -r '.learned // ""')
|
|
195
|
+
agent=$(printf '%s' "$row" | jq -r '.agent // ""')
|
|
196
|
+
printf '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n' \
|
|
197
|
+
"$(html_escape "$created")" "$(html_escape "$(eagle_agent_label "$agent")")" \
|
|
198
|
+
"$(html_escape "$request")" "$(html_escape "$completed")" "$(html_escape "$learned")"
|
|
199
|
+
done
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
render_recall_rows() {
|
|
203
|
+
printf '%s' "$recall_json" | jq -c '.[]' | while IFS= read -r row; do
|
|
204
|
+
local created prompt query summaries memories code tokens status agent
|
|
205
|
+
created=$(printf '%s' "$row" | jq -r '.created_at // ""')
|
|
206
|
+
prompt=$(printf '%s' "$row" | jq -r '.prompt_snippet // ""')
|
|
207
|
+
query=$(printf '%s' "$row" | jq -r '.fts_query // ""')
|
|
208
|
+
summaries=$(printf '%s' "$row" | jq -r '.summary_matches // 0')
|
|
209
|
+
memories=$(printf '%s' "$row" | jq -r '.memory_matches // 0')
|
|
210
|
+
code=$(printf '%s' "$row" | jq -r '.code_matches // 0')
|
|
211
|
+
tokens=$(printf '%s' "$row" | jq -r '.injected_token_estimate // 0')
|
|
212
|
+
status=$(printf '%s' "$row" | jq -r '.status // ""')
|
|
213
|
+
agent=$(printf '%s' "$row" | jq -r '.agent // ""')
|
|
214
|
+
printf '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s/%s/%s</td><td>%s</td></tr>\n' \
|
|
215
|
+
"$(html_escape "$created")" "$(html_escape "$(eagle_agent_label "$agent")")" \
|
|
216
|
+
"$(html_escape "$status")" "$(html_escape "$prompt")" "$(html_escape "$query")" \
|
|
217
|
+
"$(html_escape "$summaries")" "$(html_escape "$memories")" "$(html_escape "$code")" \
|
|
218
|
+
"$(html_escape "$tokens")"
|
|
219
|
+
done
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
render_simple_rows() {
|
|
223
|
+
local json="$1" first="$2" second="$3"
|
|
224
|
+
printf '%s' "$json" | jq -c '.[]' | while IFS= read -r row; do
|
|
225
|
+
local a b
|
|
226
|
+
a=$(printf '%s' "$row" | jq -r --arg first "$first" '.[$first] // ""')
|
|
227
|
+
b=$(printf '%s' "$row" | jq -r --arg second "$second" '.[$second] // ""')
|
|
228
|
+
printf '<tr><td>%s</td><td>%s</td></tr>\n' "$(html_escape "$a")" "$(html_escape "$b")"
|
|
229
|
+
done
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
overview_content="No project overview recorded yet."
|
|
233
|
+
if [ "$overview_count" -gt 0 ]; then
|
|
234
|
+
overview_content=$(printf '%s' "$overview_json" | jq -r '.[0].content // "No project overview recorded yet."')
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
cat > "$output_path" <<HTML
|
|
238
|
+
<!doctype html>
|
|
239
|
+
<html lang="en">
|
|
240
|
+
<head>
|
|
241
|
+
<meta charset="utf-8">
|
|
242
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
243
|
+
<title>Eagle Mem Dashboard - $(html_escape "$project_label")</title>
|
|
244
|
+
<style>
|
|
245
|
+
:root { color-scheme: light; --ink:#111; --muted:#626262; --line:#d8d8d8; --soft:#f6f6f3; --accent:#096b72; --warn:#8a3f00; }
|
|
246
|
+
* { box-sizing: border-box; }
|
|
247
|
+
body { margin: 0; font-family: Helvetica, Arial, sans-serif; color: var(--ink); background: #fff; line-height: 1.45; }
|
|
248
|
+
header { padding: 28px 36px 20px; border-bottom: 2px solid var(--ink); background: var(--soft); }
|
|
249
|
+
main { padding: 24px 36px 44px; display: grid; gap: 24px; }
|
|
250
|
+
h1 { margin: 0; font-size: 30px; letter-spacing: 0; }
|
|
251
|
+
h2 { margin: 0 0 12px; font-size: 18px; letter-spacing: 0; }
|
|
252
|
+
.meta { margin-top: 8px; color: var(--muted); font-size: 14px; }
|
|
253
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }
|
|
254
|
+
.metric { border: 1px solid var(--line); padding: 14px; background: #fff; }
|
|
255
|
+
.metric strong { display:block; font-size: 24px; }
|
|
256
|
+
section { border-top: 1px solid var(--line); padding-top: 18px; }
|
|
257
|
+
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
258
|
+
th, td { padding: 9px 8px; border-bottom: 1px solid var(--line); vertical-align: top; text-align: left; }
|
|
259
|
+
th { color: var(--muted); font-weight: 700; background: #fafafa; }
|
|
260
|
+
p { max-width: 900px; margin: 0; }
|
|
261
|
+
code { font-family: "Courier New", monospace; font-size: 12px; }
|
|
262
|
+
.empty { color: var(--muted); padding: 12px 0; }
|
|
263
|
+
</style>
|
|
264
|
+
</head>
|
|
265
|
+
<body>
|
|
266
|
+
<header>
|
|
267
|
+
<h1>Eagle Mem Dashboard</h1>
|
|
268
|
+
<div class="meta">Project: $(html_escape "$project_label") | Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ) | DB integrity: $(html_escape "$db_integrity_status")</div>
|
|
269
|
+
</header>
|
|
270
|
+
<main>
|
|
271
|
+
<section>
|
|
272
|
+
<h2>Project Brain</h2>
|
|
273
|
+
<p>$(html_escape "$overview_content")</p>
|
|
274
|
+
</section>
|
|
275
|
+
<section>
|
|
276
|
+
<h2>Signals</h2>
|
|
277
|
+
<div class="grid">
|
|
278
|
+
<div class="metric"><strong>$summary_count</strong> recent summaries</div>
|
|
279
|
+
<div class="metric"><strong>$recall_count</strong> recall events</div>
|
|
280
|
+
<div class="metric"><strong>$file_count</strong> touched files</div>
|
|
281
|
+
<div class="metric"><strong>$task_count</strong> active tasks</div>
|
|
282
|
+
<div class="metric"><strong>$graph_type_count</strong> graph node types</div>
|
|
283
|
+
<div class="metric"><strong>$edge_count</strong> graph edges</div>
|
|
284
|
+
</div>
|
|
285
|
+
</section>
|
|
286
|
+
<section>
|
|
287
|
+
<h2>Recall Inspector</h2>
|
|
288
|
+
<table>
|
|
289
|
+
<thead><tr><th>Time</th><th>Agent</th><th>Status</th><th>Prompt</th><th>Query</th><th>Summary/Memory/Code</th><th>Token estimate</th></tr></thead>
|
|
290
|
+
<tbody>
|
|
291
|
+
$(render_recall_rows)
|
|
292
|
+
</tbody>
|
|
293
|
+
</table>
|
|
294
|
+
</section>
|
|
295
|
+
<section>
|
|
296
|
+
<h2>Timeline</h2>
|
|
297
|
+
<table>
|
|
298
|
+
<thead><tr><th>Time</th><th>Agent</th><th>Request</th><th>Completed</th><th>Learned</th></tr></thead>
|
|
299
|
+
<tbody>
|
|
300
|
+
$(render_summary_rows)
|
|
301
|
+
</tbody>
|
|
302
|
+
</table>
|
|
303
|
+
</section>
|
|
304
|
+
<section>
|
|
305
|
+
<h2>File Intelligence</h2>
|
|
306
|
+
<table>
|
|
307
|
+
<thead><tr><th>File</th><th>Touches</th></tr></thead>
|
|
308
|
+
<tbody>
|
|
309
|
+
$(render_simple_rows "$files_json" "file" "touches")
|
|
310
|
+
</tbody>
|
|
311
|
+
</table>
|
|
312
|
+
</section>
|
|
313
|
+
<section>
|
|
314
|
+
<h2>Agent Comparison</h2>
|
|
315
|
+
<table>
|
|
316
|
+
<thead><tr><th>Agent</th><th>Sessions</th></tr></thead>
|
|
317
|
+
<tbody>
|
|
318
|
+
$(render_simple_rows "$agents_json" "agent" "sessions")
|
|
319
|
+
</tbody>
|
|
320
|
+
</table>
|
|
321
|
+
</section>
|
|
322
|
+
<section>
|
|
323
|
+
<h2>Active Tasks</h2>
|
|
324
|
+
<table>
|
|
325
|
+
<thead><tr><th>Task</th><th>Status</th></tr></thead>
|
|
326
|
+
<tbody>
|
|
327
|
+
$(render_simple_rows "$tasks_json" "subject" "status")
|
|
328
|
+
</tbody>
|
|
329
|
+
</table>
|
|
330
|
+
</section>
|
|
331
|
+
<section>
|
|
332
|
+
<h2>Graph Readiness</h2>
|
|
333
|
+
<table>
|
|
334
|
+
<thead><tr><th>Node type</th><th>Count</th></tr></thead>
|
|
335
|
+
<tbody>
|
|
336
|
+
$(render_simple_rows "$graph_json" "node_type" "nodes")
|
|
337
|
+
</tbody>
|
|
338
|
+
</table>
|
|
339
|
+
</section>
|
|
340
|
+
</main>
|
|
341
|
+
</body>
|
|
342
|
+
</html>
|
|
343
|
+
HTML
|
|
344
|
+
|
|
345
|
+
if [ "$json_output" = true ]; then
|
|
346
|
+
jq -nc \
|
|
347
|
+
--arg status "ok" \
|
|
348
|
+
--arg command "dashboard" \
|
|
349
|
+
--arg project "$project_label" \
|
|
350
|
+
--arg output "$output_path" \
|
|
351
|
+
--argjson summaries "$summary_count" \
|
|
352
|
+
--argjson recall_events "$recall_count" \
|
|
353
|
+
--argjson files "$file_count" \
|
|
354
|
+
--argjson active_tasks "$task_count" \
|
|
355
|
+
--argjson graph_node_types "$graph_type_count" \
|
|
356
|
+
--argjson graph_edges "${edge_count:-0}" \
|
|
357
|
+
'{status:$status, command:$command, project:$project, output:$output,
|
|
358
|
+
counts:{summaries:$summaries, recall_events:$recall_events, files:$files,
|
|
359
|
+
active_tasks:$active_tasks, graph_node_types:$graph_node_types,
|
|
360
|
+
graph_edges:$graph_edges}}'
|
|
361
|
+
else
|
|
362
|
+
eagle_ok "Dashboard generated"
|
|
363
|
+
eagle_kv "Output:" "$output_path"
|
|
364
|
+
fi
|
|
365
|
+
|
|
366
|
+
if [ "$open_after" = true ]; then
|
|
367
|
+
if command -v open >/dev/null 2>&1; then
|
|
368
|
+
open "$output_path" >/dev/null 2>&1 || true
|
|
369
|
+
elif command -v xdg-open >/dev/null 2>&1; then
|
|
370
|
+
xdg-open "$output_path" >/dev/null 2>&1 || true
|
|
371
|
+
fi
|
|
372
|
+
fi
|
package/scripts/doctor.sh
CHANGED
|
@@ -13,6 +13,7 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
|
13
13
|
|
|
14
14
|
. "$SCRIPTS_DIR/style.sh"
|
|
15
15
|
. "$LIB_DIR/common.sh"
|
|
16
|
+
. "$LIB_DIR/opencode-hooks.sh"
|
|
16
17
|
|
|
17
18
|
mode="install-footprint"
|
|
18
19
|
json_output=false
|
|
@@ -49,7 +50,10 @@ case "$mode" in
|
|
|
49
50
|
esac
|
|
50
51
|
|
|
51
52
|
package_version=$(jq -r .version "$PACKAGE_DIR/package.json" 2>/dev/null || echo "unknown")
|
|
52
|
-
installed_version
|
|
53
|
+
installed_version=""
|
|
54
|
+
if [ -f "$EAGLE_MEM_DIR/.version" ]; then
|
|
55
|
+
installed_version=$(tr -d '[:space:]' < "$EAGLE_MEM_DIR/.version" 2>/dev/null || true)
|
|
56
|
+
fi
|
|
53
57
|
[ -z "$installed_version" ] && installed_version="not installed"
|
|
54
58
|
|
|
55
59
|
sqlite_bin=$(eagle_sqlite_path)
|
|
@@ -63,6 +67,11 @@ runtime_exists=false
|
|
|
63
67
|
db_exists=false
|
|
64
68
|
[ -d "$EAGLE_MEM_DIR" ] && runtime_exists=true
|
|
65
69
|
[ -f "$EAGLE_MEM_DB" ] && db_exists=true
|
|
70
|
+
db_integrity_check=$(eagle_db_integrity_status "$EAGLE_MEM_DB" 2>/dev/null || true)
|
|
71
|
+
db_integrity_status="${db_integrity_check%%|*}"
|
|
72
|
+
db_integrity_detail="${db_integrity_check#*|}"
|
|
73
|
+
[ -n "$db_integrity_status" ] || db_integrity_status="unknown"
|
|
74
|
+
[ -n "$db_integrity_detail" ] || db_integrity_detail="not checked"
|
|
66
75
|
|
|
67
76
|
doctor_compare_group() {
|
|
68
77
|
local group="$1"
|
|
@@ -149,10 +158,12 @@ if [ -f "$EAGLE_SETTINGS" ] && command -v jq >/dev/null 2>&1; then
|
|
|
149
158
|
fi
|
|
150
159
|
fi
|
|
151
160
|
|
|
161
|
+
opencode_plugin=$(eagle_opencode_plugin_state)
|
|
162
|
+
|
|
152
163
|
overall="Healthy"
|
|
153
164
|
if [ "$runtime_exists" != true ] || [ "$db_exists" != true ]; then
|
|
154
165
|
overall="Not installed"
|
|
155
|
-
elif [ "$sqlite_fts5" != true ] || [ "$sum_missing" -gt 0 ] || [ "$sum_drift" -gt 0 ] || [ "$manifest_status" != "ok" ]; then
|
|
166
|
+
elif [ "$sqlite_fts5" != true ] || [ "$db_integrity_status" != "ok" ] || [ "$sum_missing" -gt 0 ] || [ "$sum_drift" -gt 0 ] || [ "$manifest_status" != "ok" ]; then
|
|
156
167
|
overall="Needs attention"
|
|
157
168
|
fi
|
|
158
169
|
|
|
@@ -167,8 +178,12 @@ if [ "$json_output" = true ]; then
|
|
|
167
178
|
--arg sqlite_bin "${sqlite_bin:-}" \
|
|
168
179
|
--arg sqlite_version "${sqlite_version:-}" \
|
|
169
180
|
--argjson sqlite_fts5 "$sqlite_fts5" \
|
|
181
|
+
--argjson db_exists "$db_exists" \
|
|
182
|
+
--arg db_integrity_status "$db_integrity_status" \
|
|
183
|
+
--arg db_integrity_detail "$db_integrity_detail" \
|
|
170
184
|
--arg claude_hooks "$claude_hooks" \
|
|
171
185
|
--arg codex_hooks "$codex_hooks" \
|
|
186
|
+
--arg opencode_plugin "$opencode_plugin" \
|
|
172
187
|
--arg statusline "$statusline_state" \
|
|
173
188
|
--arg hooks_cmp "$hooks_cmp" \
|
|
174
189
|
--arg lib_cmp "$lib_cmp" \
|
|
@@ -186,7 +201,8 @@ if [ "$json_output" = true ]; then
|
|
|
186
201
|
'{overall:$overall, package_dir:$package_dir, runtime_dir:$runtime_dir, db:$db,
|
|
187
202
|
versions:{package:$package_version, installed:$installed_version},
|
|
188
203
|
sqlite:{path:$sqlite_bin, version:$sqlite_version, fts5:$sqlite_fts5},
|
|
189
|
-
|
|
204
|
+
database:{exists:$db_exists, integrity:{status:$db_integrity_status, detail:$db_integrity_detail}},
|
|
205
|
+
hooks:{claude:$claude_hooks, codex:$codex_hooks, opencode:$opencode_plugin, statusline:$statusline},
|
|
190
206
|
runtime_drift:{hooks:$hooks_cmp, lib:$lib_cmp, db:$db_cmp, scripts:$scripts_cmp},
|
|
191
207
|
manifest:{path:$manifest_path, status:$manifest_status, checked:$manifest_checked,
|
|
192
208
|
missing:$manifest_missing, drift:$manifest_drift, version:$manifest_version,
|
|
@@ -217,11 +233,22 @@ if [ -n "$sqlite_bin" ]; then
|
|
|
217
233
|
else
|
|
218
234
|
eagle_fail "SQLite not found"
|
|
219
235
|
fi
|
|
236
|
+
if [ "$db_exists" = true ]; then
|
|
237
|
+
if [ "$db_integrity_status" = "ok" ]; then
|
|
238
|
+
eagle_ok "Database integrity: ok"
|
|
239
|
+
else
|
|
240
|
+
eagle_fail "Database integrity: $db_integrity_status"
|
|
241
|
+
eagle_dim " $db_integrity_detail"
|
|
242
|
+
fi
|
|
243
|
+
else
|
|
244
|
+
eagle_fail "Database missing"
|
|
245
|
+
fi
|
|
220
246
|
echo ""
|
|
221
247
|
|
|
222
248
|
echo -e " ${BOLD}Hooks / Skills${RESET}"
|
|
223
249
|
eagle_kv "Claude Code:" "$claude_hooks"
|
|
224
250
|
eagle_kv "Codex:" "$codex_hooks"
|
|
251
|
+
eagle_kv "OpenCode:" "$opencode_plugin"
|
|
225
252
|
[ -d "$EAGLE_GROK_DIR" ] && eagle_kv "Grok skills:" "$EAGLE_GROK_SKILLS_DIR"
|
|
226
253
|
eagle_kv "Statusline:" "$statusline_state"
|
|
227
254
|
echo ""
|
|
@@ -90,8 +90,14 @@ gotchas=$(extract_section "$enrich_result" "GOTCHAS" | eagle_redact)
|
|
|
90
90
|
key_files=$(extract_section "$enrich_result" "KEY_FILES" | eagle_redact)
|
|
91
91
|
|
|
92
92
|
if [ -n "$request" ] || [ -n "$completed" ] || [ -n "$learned" ] || [ -n "$decisions" ] || [ -n "$gotchas" ] || [ -n "$key_files" ]; then
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
existing_source=$(eagle_summary_capture_source "$session_id" 2>/dev/null || true)
|
|
94
|
+
if [ "$existing_source" = "agent" ]; then
|
|
95
|
+
# An agent-authored row exists — only fill gaps, never overwrite it.
|
|
96
|
+
eagle_insert_summary_fill_only "$session_id" "$project" "$request" "" "$learned" "$completed" "" "[]" "[]" "" "$decisions" "$gotchas" "$key_files" "$agent" "enrich"
|
|
97
|
+
else
|
|
98
|
+
eagle_insert_summary "$session_id" "$project" "$request" "" "$learned" "$completed" "" "[]" "[]" "" "$decisions" "$gotchas" "$key_files" "$agent" "enrich"
|
|
99
|
+
fi
|
|
100
|
+
eagle_log "INFO" "Summary enrichment saved for session=$session_id provider=$provider source=${existing_source:-none}"
|
|
95
101
|
fi
|
|
96
102
|
|
|
97
103
|
rm -f "$job_file" 2>/dev/null || true
|