eagle-mem 4.10.12 → 4.11.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +20 -20
  3. package/architecture.html +26 -14
  4. package/bin/eagle-mem +4 -0
  5. package/db/039_recall_events.sql +27 -0
  6. package/db/040_graph_decision_nodes.sql +21 -0
  7. package/db/041_graph_semantic_edge_types.sql +21 -0
  8. package/db/042_orchestration_auto_events.sql +23 -0
  9. package/db/043_eagle_events.sql +22 -0
  10. package/docs/agent-compatibility/README.md +38 -0
  11. package/docs/agent-compatibility/claude-code.md +50 -0
  12. package/docs/agent-compatibility/codex.md +51 -0
  13. package/docs/agent-compatibility/opencode.md +71 -0
  14. package/hooks/post-tool-use.sh +8 -0
  15. package/hooks/pre-tool-use.sh +11 -3
  16. package/hooks/session-end.sh +3 -0
  17. package/hooks/session-start.sh +7 -0
  18. package/hooks/stop.sh +10 -1
  19. package/hooks/user-prompt-submit.sh +79 -6
  20. package/integrations/opencode_eagle_mem_plugin.js +387 -0
  21. package/lib/codex-hooks.sh +13 -6
  22. package/lib/common.sh +71 -8
  23. package/lib/db-events.sh +89 -0
  24. package/lib/db-features.sh +26 -23
  25. package/lib/db-graph.sh +154 -0
  26. package/lib/db-observations.sh +34 -0
  27. package/lib/db-orchestration.sh +149 -0
  28. package/lib/db.sh +2 -0
  29. package/lib/hooks.sh +12 -7
  30. package/lib/opencode-hooks.sh +105 -0
  31. package/lib/provider.sh +2 -2
  32. package/package.json +5 -2
  33. package/scripts/compaction.sh +108 -8
  34. package/scripts/dashboard.sh +372 -0
  35. package/scripts/doctor.sh +30 -3
  36. package/scripts/health.sh +40 -2
  37. package/scripts/help.sh +10 -2
  38. package/scripts/inspect.sh +285 -0
  39. package/scripts/install.sh +31 -7
  40. package/scripts/memories.sh +13 -0
  41. package/scripts/repair.sh +187 -0
  42. package/scripts/replay.sh +248 -0
  43. package/scripts/search.sh +44 -3
  44. package/scripts/statusline-em.sh +34 -7
  45. package/scripts/tasks.sh +34 -0
  46. package/scripts/test.sh +14 -0
  47. package/scripts/uninstall.sh +9 -0
  48. package/scripts/update.sh +18 -2
  49. package/skills/eagle-mem-feature/SKILL.md +3 -3
  50. package/tests/fixtures/agent-hooks/claude-statusline.json +32 -0
  51. package/tests/fixtures/agent-hooks/claude-user-prompt-submit.json +9 -0
  52. package/tests/fixtures/agent-hooks/codex-pre-tool-use.json +10 -0
  53. package/tests/fixtures/agent-hooks/codex-user-prompt-submit.json +7 -0
  54. package/tests/fixtures/agent-hooks/opencode-chat-message.json +36 -0
  55. package/tests/fixtures/agent-hooks/opencode-session-compacting.json +9 -0
  56. package/tests/fixtures/agent-hooks/opencode-todo-updated.json +13 -0
  57. package/tests/fixtures/agent-hooks/opencode-tool-execute-after.json +15 -0
  58. package/tests/fixtures/agent-hooks/opencode-tool-execute-before.json +12 -0
  59. package/tests/test_agent_compatibility_docs_gate.sh +123 -0
  60. package/tests/test_auto_orchestration_detection.sh +109 -0
  61. package/tests/test_claude_stop_hook_registration.sh +56 -0
  62. package/tests/test_codex_hooks_config.sh +73 -0
  63. package/tests/test_compaction_survival_matrix.sh +237 -0
  64. package/tests/test_dashboard.sh +96 -0
  65. package/tests/test_eagle_events.sh +96 -0
  66. package/tests/test_feature_verification_gate.sh +230 -0
  67. package/tests/test_opencode_hooks_config.sh +56 -0
  68. package/tests/test_opencode_plugin_adapter.sh +202 -0
  69. package/tests/test_recall_observability.sh +144 -0
  70. package/tests/test_reliability_guards.sh +20 -0
  71. package/tests/test_repair.sh +63 -0
  72. package/tests/test_rust_migration_plan.sh +75 -0
  73. package/tests/test_trust_surfaces.sh +123 -0
@@ -0,0 +1,71 @@
1
+ # OpenCode Compatibility
2
+
3
+ Last verified: 2026-06-02
4
+
5
+ ## Official Sources
6
+
7
+ - Plugins reference: https://opencode.ai/docs/plugins/
8
+ - Config reference: https://opencode.ai/docs/config/
9
+ - Config schema: https://opencode.ai/config.json
10
+
11
+ ## Installed Surface Verified Locally
12
+
13
+ - OpenCode CLI: `opencode --version` reported `1.14.24`.
14
+ - OpenCode plugin package: `@opencode-ai/plugin@1.14.24`.
15
+ - Resolved config path: `~/.config/opencode/opencode.json`.
16
+ - Global plugin directory: `~/.config/opencode/plugins/`.
17
+
18
+ ## Behavior Relied On
19
+
20
+ - OpenCode local plugins are JavaScript or TypeScript modules placed in `.opencode/plugins/` for project scope or `~/.config/opencode/plugins/` for global scope.
21
+ - Files in those plugin directories are loaded automatically at startup.
22
+ - NPM plugins are declared with the top-level `plugin` array in `opencode.json`, but Eagle Mem uses the global local-plugin directory so it does not need to mutate the user plugin array.
23
+ - Plugin functions receive a context object with `project`, `directory`, `worktree`, `client`, and `$`, then return a hooks object.
24
+ - The plugin event bus includes `session.created`, `session.updated`, `session.idle`, `session.compacted`, `session.deleted`, `message.updated`, `message.part.updated`, and `todo.updated`.
25
+ - Direct hook keys include `chat.message`, `tool.execute.before`, `tool.execute.after`, `shell.env`, and `experimental.session.compacting`.
26
+ - `tool.execute.before` can deny a tool call by throwing an error and can mutate `output.args` before execution.
27
+ - `tool.execute.after` receives tool output fields that can be annotated for the model.
28
+ - `experimental.session.compacting` fires before compaction and can push additional strings into `output.context`.
29
+ - `opencode --pure` runs without external plugins; doctor checks should report that Eagle Mem OpenCode support depends on non-pure OpenCode sessions.
30
+
31
+ ## Eagle Mem Mapping
32
+
33
+ - `chat.message` maps to Eagle Mem `SessionStart` once per session and `UserPromptSubmit` on each user message.
34
+ - `tool.execute.before` maps to Eagle Mem `PreToolUse`; denial becomes a thrown OpenCode plugin error and `updatedInput` mutates `output.args`.
35
+ - `tool.execute.after` maps to Eagle Mem `PostToolUse`; additional context is appended to the tool output so the agent can see guardrail or stale-memory hints.
36
+ - `todo.updated` maps OpenCode todos into Eagle Mem task records through synthetic `TaskCreated` and `TaskUpdate` hook payloads.
37
+ - `session.idle` maps to Eagle Mem `Stop` using the latest assistant text accumulated from message events.
38
+ - `session.deleted` maps to Eagle Mem `SessionEnd`.
39
+ - `experimental.session.compacting` maps to Eagle Mem compact recall by running `SessionStart` with `source=compact` and appending the returned context.
40
+
41
+ ## Eagle Mem Files Depending On This
42
+
43
+ - `integrations/opencode_eagle_mem_plugin.js`
44
+ - `lib/opencode-hooks.sh`
45
+ - `lib/common.sh`
46
+ - `scripts/install.sh`
47
+ - `scripts/update.sh`
48
+ - `scripts/uninstall.sh`
49
+ - `scripts/doctor.sh`
50
+ - `scripts/test.sh`
51
+ - `hooks/session-start.sh`
52
+ - `hooks/user-prompt-submit.sh`
53
+ - `hooks/pre-tool-use.sh`
54
+ - `hooks/post-tool-use.sh`
55
+ - `hooks/stop.sh`
56
+ - `hooks/session-end.sh`
57
+
58
+ ## Fixtures And Tests
59
+
60
+ - `tests/fixtures/agent-hooks/opencode-chat-message.json`
61
+ - `tests/fixtures/agent-hooks/opencode-tool-execute-before.json`
62
+ - `tests/fixtures/agent-hooks/opencode-tool-execute-after.json`
63
+ - `tests/fixtures/agent-hooks/opencode-todo-updated.json`
64
+ - `tests/fixtures/agent-hooks/opencode-session-compacting.json`
65
+ - `tests/test_agent_compatibility_docs_gate.sh`
66
+ - `tests/test_opencode_hooks_config.sh`
67
+ - `tests/test_opencode_plugin_adapter.sh`
68
+
69
+ ## Reverification Notes
70
+
71
+ When editing OpenCode support, re-read the plugins and config docs first. If OpenCode changes plugin load order, local plugin directories, hook names, `tool.execute.*` mutation semantics, `chat.message` payloads, or compaction hooks, update these fixtures before implementation.
@@ -122,6 +122,7 @@ esac
122
122
 
123
123
  project=$(eagle_project_from_hook_input "$input")
124
124
  [ -z "$project" ] && exit 0
125
+ eagle_hook_observability_begin "$input" "PostToolUse"
125
126
 
126
127
  # Ensure session row exists before inserting observations (FK constraint).
127
128
  # PostToolUse can race SessionStart — the session row might not exist yet.
@@ -249,5 +250,12 @@ eagle_posttool_decision_surface "$tool_name" "$fp" "$project" "$agent"
249
250
  if ! eagle_insert_observation "$session_id" "$project" "$tool_name" "$tool_summary" "$files_read" "$files_modified" "$output_bytes" "$output_lines" "$command_category" "$agent"; then
250
251
  eagle_log "ERROR" "PostToolUse: observation insert failed for session=$session_id tool=$tool_name"
251
252
  fi
253
+ eagle_hook_observability_set_detail "$(jq -nc \
254
+ --arg tool "$tool_name" \
255
+ --arg summary "$tool_summary" \
256
+ --arg files_read "$files_read" \
257
+ --arg files_modified "$files_modified" \
258
+ '{tool_name:$tool, summary:$summary, files_read:($files_read | fromjson? // []), files_modified:($files_modified | fromjson? // [])}')"
259
+ eagle_hook_observability_complete 0
252
260
 
253
261
  exit 0
@@ -33,6 +33,7 @@ esac
33
33
  [ ! -f "$EAGLE_MEM_DB" ] && exit 0
34
34
  project=$(eagle_project_from_hook_input "$input")
35
35
  [ -z "$project" ] && exit 0
36
+ eagle_hook_observability_begin "$input" "PreToolUse"
36
37
 
37
38
  context=""
38
39
  updated_input=""
@@ -150,9 +151,8 @@ One-off developer bypass:
150
151
  while IFS= read -r changed_file; do
151
152
  [ -z "$changed_file" ] && continue
152
153
  norm_file=$(eagle_project_file_path "$cwd" "$changed_file")
153
- fname=$(basename "$norm_file")
154
154
 
155
- feature_hits=$(eagle_find_feature_for_push "$project" "$fname")
155
+ feature_hits=$(eagle_find_feature_for_push "$project" "$norm_file")
156
156
 
157
157
  while IFS='|' read -r feat_name feat_smoke feat_deps feat_verified; do
158
158
  [ -z "$feat_name" ] && continue
@@ -383,15 +383,23 @@ Use the existing context, run a narrower search, or bypass once with:
383
383
  ;;
384
384
  esac
385
385
 
386
- [ -z "$context" ] && [ -z "$updated_input" ] && exit 0
386
+ if [ -z "$context" ] && [ -z "$updated_input" ]; then
387
+ eagle_hook_observability_set_detail "$(jq -nc --arg tool "$tool_name" --arg action "no_action" '{tool_name:$tool, action:$action}')"
388
+ eagle_hook_observability_complete 0
389
+ exit 0
390
+ fi
387
391
 
388
392
  # Codex PreToolUse now natively receives both blocking decisions and advisory context.
389
393
  # Removing the old early-exit to align Codex's pre-tool capabilities with Claude and Antigravity.
390
394
 
391
395
  if [ -n "$updated_input" ]; then
396
+ eagle_hook_observability_set_detail "$(jq -nc --arg tool "$tool_name" --arg action "updated_input" '{tool_name:$tool, action:$action}')"
397
+ eagle_hook_observability_complete 0
392
398
  jq -nc --arg ctx "$context" --argjson ui "$updated_input" \
393
399
  '{"hookSpecificOutput":{"hookEventName":"PreToolUse","updatedInput":$ui,"additionalContext":$ctx}}'
394
400
  else
401
+ eagle_hook_observability_set_detail "$(jq -nc --arg tool "$tool_name" --arg action "additional_context" '{tool_name:$tool, action:$action}')"
402
+ eagle_hook_observability_complete 0
395
403
  jq -nc --arg ctx "$context" '{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":$ctx}}'
396
404
  fi
397
405
 
@@ -24,6 +24,7 @@ agent=$(eagle_agent_source_from_json "$input")
24
24
  cwd=$(echo "$input" | jq -r '.cwd // empty')
25
25
  project=$(eagle_project_from_hook_input "$input")
26
26
  [ -z "$project" ] && exit 0
27
+ eagle_hook_observability_begin "$input" "SessionEnd"
27
28
 
28
29
  # Final sweep: re-capture all task files to catch status changes
29
30
  # Claude Code may update task status without triggering PostToolUse
@@ -40,6 +41,8 @@ fi
40
41
 
41
42
  eagle_end_session "$session_id"
42
43
  eagle_log "INFO" "SessionEnd: session=$session_id marked completed"
44
+ eagle_hook_observability_set_detail "$(jq -nc --arg action "session_completed" '{action:$action}')"
45
+ eagle_hook_observability_complete 0
43
46
 
44
47
  # Prune observations older than 90 days (keeps DB size bounded)
45
48
  eagle_prune_observations 90 "$project"
@@ -35,6 +35,7 @@ codex_compact=0
35
35
 
36
36
  project=$(eagle_project_from_hook_input "$input")
37
37
  [ -z "$project" ] && exit 0
38
+ eagle_hook_observability_begin "$input" "SessionStart"
38
39
 
39
40
  p_esc=$(eagle_sql_escape "$project")
40
41
  recall_scope=$(eagle_recall_project_scope_from_cwd "$cwd" "$project")
@@ -655,7 +656,13 @@ regression_risks: [risk, ...]
655
656
  fi
656
657
 
657
658
  if [ -n "$context" ]; then
659
+ eagle_hook_observability_set_detail "$(jq -nc \
660
+ --arg source_type "$source_type" \
661
+ --arg recall_scope "$recall_scope" \
662
+ --argjson injected_chars "${#context}" \
663
+ '{source_type:$source_type, recall_scope:$recall_scope, injected_chars:$injected_chars}')"
658
664
  eagle_emit_context_for_agent "$agent" "SessionStart" "$context"
659
665
  fi
666
+ eagle_hook_observability_complete 0
660
667
 
661
668
  exit 0
package/hooks/stop.sh CHANGED
@@ -31,6 +31,7 @@ agent=$(eagle_agent_source_from_json "$input")
31
31
 
32
32
  project=$(eagle_project_from_hook_input "$input")
33
33
  [ -z "$project" ] && exit 0
34
+ eagle_hook_observability_begin "$input" "Stop"
34
35
 
35
36
  eagle_log "INFO" "Stop: session=$session_id project=$project transcript=$transcript_path agent=$agent"
36
37
 
@@ -237,7 +238,7 @@ if [ "$needs_enrichment" -eq 1 ]; then
237
238
  # lifecycle timeouts and make the hook look broken to users.
238
239
  if [ "${EAGLE_MEM_STOP_ENRICH:-0}" != "1" ]; then
239
240
  defer_enrichment=1
240
- eagle_log "INFO" "Stop: LLM enrichment skipped fast hook path (provider=$provider)"
241
+ eagle_log "INFO" "Stop: LLM enrichment skipped on fast hook path; provider=$provider"
241
242
  elif [ "$provider" != "none" ] && [ -n "$text_content" ]; then
242
243
  excerpt=$(echo "$text_content" | tail -c 3000)
243
244
 
@@ -399,10 +400,18 @@ fi
399
400
  if [ -n "$request" ] || [ -n "$completed" ] || [ -n "$learned" ]; then
400
401
  if eagle_insert_summary "$session_id" "$project" "$request" "$investigated" "$learned" "$completed" "$next_steps" "$files_read" "$files_modified" "$notes" "$decisions" "$gotchas" "$key_files" "$agent"; then
401
402
  eagle_log "INFO" "Stop: summary saved for session=$session_id"
403
+ eagle_insert_event "$project" "$session_id" "$agent" "memory_created" "" "Stop" "ok" "$(jq -nc --arg source "summary" '{source:$source}')" >/dev/null 2>&1 || true
402
404
  else
403
405
  eagle_log "ERROR" "Stop: summary insert FAILED for session=$session_id — check DB constraints"
404
406
  fi
405
407
  fi
408
+ eagle_hook_observability_set_detail "$(jq -nc \
409
+ --argjson files_read "$files_read" \
410
+ --argjson files_modified "$files_modified" \
411
+ --arg request "$request" \
412
+ --arg completed "$completed" \
413
+ '{request_chars:($request | length), completed_chars:($completed | length), files_read:$files_read, files_modified:$files_modified}')"
414
+ eagle_hook_observability_complete 0
406
415
 
407
416
  if [ "$defer_enrichment" -eq 1 ] && [ "${EAGLE_MEM_STOP_BACKGROUND_ENRICH:-1}" = "1" ] && [ -n "$text_content" ]; then
408
417
  mkdir -p "$EAGLE_MEM_DIR/tmp" 2>/dev/null || true
@@ -30,11 +30,22 @@ recall_scope=$(eagle_recall_project_scope_from_cwd "$cwd" "$project")
30
30
  [ -z "$recall_scope" ] && recall_scope="$project"
31
31
  codex_compact=0
32
32
  [ "$agent" = "codex" ] && codex_compact=1
33
+ eagle_hook_observability_begin "$input" "UserPromptSubmit"
33
34
 
34
35
  # ─── Context pressure detection (turn counter since last compact) ──
35
36
  # Must run before any early exits so every prompt is counted
36
37
 
37
38
  context=""
39
+ summary_matches=0
40
+ memory_matches=0
41
+ code_matches=0
42
+ summary_refs="[]"
43
+ memory_refs="[]"
44
+ code_refs="[]"
45
+
46
+ count_pipe_rows() {
47
+ awk -F'|' '($1 != "" || $2 != "") { c++ } END { print c + 0 }'
48
+ }
38
49
 
39
50
  if [ -n "$session_id" ] && eagle_validate_session_id "$session_id"; then
40
51
  counter_file="$EAGLE_MEM_DIR/.turn-counter.${session_id}"
@@ -85,6 +96,9 @@ fi
85
96
  # Skip short prompts — not enough signal for meaningful search
86
97
  word_count=$(echo "$user_prompt" | wc -w | tr -d ' ')
87
98
  if [ "$word_count" -lt 3 ]; then
99
+ eagle_insert_recall_event "$session_id" "$recall_scope" "$cwd" "$agent" "$user_prompt" "" 0 0 0 "${#context}" "skipped_short_prompt" "" >/dev/null 2>&1 || true
100
+ eagle_hook_observability_set_detail "$(jq -nc --arg recall_status "skipped_short_prompt" --argjson injected_chars "${#context}" '{recall_status:$recall_status, injected_chars:$injected_chars}')"
101
+ eagle_hook_observability_complete 0
88
102
  eagle_emit_context_for_agent "$agent" "UserPromptSubmit" "$context"
89
103
  exit 0
90
104
  fi
@@ -105,6 +119,9 @@ fts_query=$(echo "$user_prompt" | tr -cs '[:alnum:]' ' ' | tr '[:upper:]' '[:low
105
119
  }')
106
120
 
107
121
  if [ -z "$fts_query" ]; then
122
+ eagle_insert_recall_event "$session_id" "$recall_scope" "$cwd" "$agent" "$user_prompt" "" 0 0 0 "${#context}" "skipped_no_query" "" >/dev/null 2>&1 || true
123
+ eagle_hook_observability_set_detail "$(jq -nc --arg recall_status "skipped_no_query" --argjson injected_chars "${#context}" '{recall_status:$recall_status, injected_chars:$injected_chars}')"
124
+ eagle_hook_observability_complete 0
108
125
  eagle_emit_context_for_agent "$agent" "UserPromptSubmit" "$context"
109
126
  exit 0
110
127
  fi
@@ -113,19 +130,27 @@ fi
113
130
 
114
131
  lower_prompt=$(printf '%s' "$user_prompt" | tr '[:upper:]' '[:lower:]')
115
132
  if printf '%s\n' "$lower_prompt" | grep -Eq '(orchestrat|worker|parallel|multi-agent|multi agent|split|lane|scope out|plan and get started|broad|full codebase|release|publish|ship)'; then
133
+ auto_orchestration=$(eagle_auto_orchestrate_from_prompt "$project" "$session_id" "$agent" "$user_prompt" "$cwd" "broad_prompt" 2>/dev/null || true)
134
+ if [ -n "$auto_orchestration" ]; then
135
+ IFS='|' read -r auto_name auto_id auto_lane_count _auto_lanes_json <<< "$auto_orchestration"
136
+ context+="
137
+ Eagle Mem orchestration: detected broad work and created durable orchestration '$auto_name' with $auto_lane_count lanes.
138
+ - Inspect it with: eagle-mem orchestrate --name $auto_name
139
+ - Lanes already exist as durable tasks; update them instead of relying on memory alone.
140
+ "
141
+ fi
116
142
  if [ "$codex_compact" -eq 1 ]; then
117
143
  context+="
118
144
  Eagle Mem orchestration:
119
- - For broad work, you run eagle-mem orchestrate yourself.
120
- - Use durable lanes, opposite-agent workers, and concise user-visible status.
145
+ - Continue from the durable lanes Eagle already created.
146
+ - Keep lane/task status current as work progresses.
121
147
  "
122
148
  else
123
149
  context+="=== Eagle Mem: Orchestration Protocol ===
124
- If this request is broad enough to split into worker lanes, YOU run the orchestration commands. Do not ask the user to run them.
150
+ Eagle has already detected broad work and registered durable orchestration state.
125
151
 
126
152
  Use:
127
- eagle-mem orchestrate init \"<goal>\"
128
- eagle-mem orchestrate lane add <key> --agent codex|claude-code --desc \"<self-contained scope>\" --validate \"<command>\"
153
+ eagle-mem orchestrate --name auto
129
154
  eagle-mem orchestrate lane start|block|complete <key>
130
155
 
131
156
  Keep this mostly invisible to the user; surface only concise status or handoff when useful.
@@ -146,6 +171,8 @@ memory_limit=3
146
171
 
147
172
  results=$(eagle_search_summaries "$fts_query" "$recall_scope" "$summary_limit")
148
173
  memory_results=$(eagle_search_agent_memories "$fts_query" "$recall_scope" "$memory_limit" 2>/dev/null || true)
174
+ summary_matches=$(printf '%s\n' "$results" | count_pipe_rows)
175
+ memory_matches=$(printf '%s\n' "$memory_results" | count_pipe_rows)
149
176
 
150
177
  if [ -n "$results" ] || [ -n "$memory_results" ]; then
151
178
  if [ "$codex_compact" -eq 1 ]; then
@@ -195,6 +222,13 @@ Eagle Mem recalls:
195
222
  context+="
196
223
  "
197
224
  fi
225
+ summary_refs=$(printf '%s' "$summary_refs" | jq -c \
226
+ --arg created_at "$created_at" \
227
+ --arg agent "$summary_agent" \
228
+ --arg request "$req" \
229
+ --arg completed "$completed" \
230
+ --arg learned "$learned" \
231
+ '. + [{created_at:$created_at, agent:$agent, request:$request, completed:$completed, learned:$learned}]' 2>/dev/null || printf '[]')
198
232
  done <<< "$results"
199
233
 
200
234
  while IFS='|' read -r mname mtype mdesc msnippet _mfile _mupdated morigin; do
@@ -219,6 +253,12 @@ Eagle Mem recalls:
219
253
  context+="
220
254
  "
221
255
  fi
256
+ memory_refs=$(printf '%s' "$memory_refs" | jq -c \
257
+ --arg name "$mname" \
258
+ --arg type "$mtype" \
259
+ --arg description "$mdesc" \
260
+ --arg agent "$morigin" \
261
+ '. + [{name:$name, type:$type, description:$description, agent:$agent}]' 2>/dev/null || printf '[]')
222
262
  done <<< "$memory_results"
223
263
  fi
224
264
 
@@ -226,6 +266,7 @@ fi
226
266
  has_chunks=$(eagle_count_code_chunks "$project")
227
267
  if [ "${has_chunks:-0}" -gt 0 ]; then
228
268
  code_results=$(eagle_search_code_chunks "$fts_query" "$project" "$code_limit")
269
+ code_matches=$(printf '%s\n' "$code_results" | count_pipe_rows)
229
270
 
230
271
  if [ -n "$code_results" ]; then
231
272
  if [ "$codex_compact" -eq 1 ]; then
@@ -246,11 +287,27 @@ Relevant code:
246
287
  [ -n "$lang" ] && context+=" ($lang)"
247
288
  context+="
248
289
  "
290
+ code_refs=$(printf '%s' "$code_refs" | jq -c \
291
+ --arg file "$fpath" \
292
+ --arg start_line "$sline" \
293
+ --arg end_line "$eline" \
294
+ --arg language "$lang" \
295
+ '. + [{file:$file, start_line:$start_line, end_line:$end_line, language:$language}]' 2>/dev/null || printf '[]')
249
296
  done <<< "$code_results"
250
297
  fi
251
298
  fi
252
299
 
253
- [ -z "$context" ] && exit 0
300
+ if [ -z "$context" ]; then
301
+ eagle_insert_recall_event "$session_id" "$recall_scope" "$cwd" "$agent" "$user_prompt" "$fts_query" "$summary_matches" "$memory_matches" "$code_matches" 0 "no_context" "" "$summary_refs" "$memory_refs" "$code_refs" >/dev/null 2>&1 || true
302
+ eagle_hook_observability_set_detail "$(jq -nc \
303
+ --arg recall_status "no_context" \
304
+ --argjson summary_matches "${summary_matches:-0}" \
305
+ --argjson memory_matches "${memory_matches:-0}" \
306
+ --argjson code_matches "${code_matches:-0}" \
307
+ '{recall_status:$recall_status, summary_matches:$summary_matches, memory_matches:$memory_matches, code_matches:$code_matches, injected_chars:0}')"
308
+ eagle_hook_observability_complete 0
309
+ exit 0
310
+ fi
254
311
 
255
312
  if [ "$codex_compact" -eq 1 ]; then
256
313
  context+="
@@ -264,5 +321,21 @@ IMPORTANT: If directly useful, start with one short Eagle Mem attribution line,
264
321
  "
265
322
  fi
266
323
 
324
+ eagle_insert_recall_event "$session_id" "$recall_scope" "$cwd" "$agent" "$user_prompt" "$fts_query" "$summary_matches" "$memory_matches" "$code_matches" "${#context}" "ok" "" "$summary_refs" "$memory_refs" "$code_refs" >/dev/null 2>&1 || true
325
+ eagle_insert_event "$recall_scope" "$session_id" "$agent" "context_injected" "" "UserPromptSubmit" "ok" "$(jq -nc \
326
+ --argjson summary_matches "${summary_matches:-0}" \
327
+ --argjson memory_matches "${memory_matches:-0}" \
328
+ --argjson code_matches "${code_matches:-0}" \
329
+ --argjson injected_chars "${#context}" \
330
+ '{summary_matches:$summary_matches, memory_matches:$memory_matches, code_matches:$code_matches, injected_chars:$injected_chars}')" >/dev/null 2>&1 || true
331
+ eagle_hook_observability_set_detail "$(jq -nc \
332
+ --arg recall_status "ok" \
333
+ --argjson summary_matches "${summary_matches:-0}" \
334
+ --argjson memory_matches "${memory_matches:-0}" \
335
+ --argjson code_matches "${code_matches:-0}" \
336
+ --argjson injected_chars "${#context}" \
337
+ '{recall_status:$recall_status, summary_matches:$summary_matches, memory_matches:$memory_matches, code_matches:$code_matches, injected_chars:$injected_chars}')"
338
+ eagle_hook_observability_complete 0
339
+
267
340
  eagle_emit_context_for_agent "$agent" "UserPromptSubmit" "$context"
268
341
  exit 0