eagle-mem 4.10.9 → 4.10.11
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 +24 -0
- package/README.md +6 -1
- package/bin/eagle-mem +1 -0
- package/hooks/post-tool-use.sh +9 -6
- package/hooks/pre-tool-use.sh +68 -1
- package/hooks/stop.sh +1 -1
- package/hooks/user-prompt-submit.sh +1 -1
- package/lib/common.sh +85 -0
- package/lib/hooks-sessionstart.sh +60 -7
- package/lib/provider.sh +187 -40
- package/package.json +1 -1
- package/scripts/config.sh +2 -0
- package/scripts/curate.sh +139 -63
- package/scripts/health.sh +5 -7
- package/scripts/help.sh +1 -0
- package/scripts/index.sh +33 -3
- package/scripts/logs.sh +84 -0
- package/scripts/scan.sh +11 -1
- package/scripts/test.sh +1 -0
- package/tests/test_curate_graph_memories.sh +99 -52
- package/tests/test_graph_memory.sh +60 -1
- package/tests/test_reliability_guards.sh +107 -0
package/lib/provider.sh
CHANGED
|
@@ -152,6 +152,8 @@ eagle_config_init() {
|
|
|
152
152
|
# Which LLM provider to use for the curator and analysis features
|
|
153
153
|
# Options: "ollama" (free, local), "agent_cli" (Codex/Claude CLI auth), "anthropic", "openai"
|
|
154
154
|
type = "$provider"
|
|
155
|
+
# "auto" retries local/agent/API fallbacks when the primary provider fails.
|
|
156
|
+
fallback = "auto"
|
|
155
157
|
|
|
156
158
|
[ollama]
|
|
157
159
|
url = "$ollama_url"
|
|
@@ -207,6 +209,12 @@ rtk = "auto"
|
|
|
207
209
|
# "allow" keeps RTK advisory only.
|
|
208
210
|
raw_bash = "block"
|
|
209
211
|
|
|
212
|
+
[read_guard]
|
|
213
|
+
# advisory = score repeated/expensive reads and nudge; block = deny high-score rereads.
|
|
214
|
+
mode = "advisory"
|
|
215
|
+
score_threshold = "70"
|
|
216
|
+
block_threshold = "90"
|
|
217
|
+
|
|
210
218
|
[redaction]
|
|
211
219
|
# Additional secret patterns (regex) beyond built-in defaults
|
|
212
220
|
# extra_patterns = ["MY_CUSTOM_SECRET_.*"]
|
|
@@ -223,14 +231,39 @@ eagle_llm_call() {
|
|
|
223
231
|
local system_prompt="${2:-You are a helpful assistant that analyzes software development sessions.}"
|
|
224
232
|
local max_tokens="${3:-1024}"
|
|
225
233
|
|
|
226
|
-
local provider
|
|
234
|
+
local provider chain candidate result rc tried=0
|
|
227
235
|
provider=$(eagle_config_get "provider" "type" "none")
|
|
236
|
+
chain=$(_eagle_provider_chain "$provider")
|
|
237
|
+
|
|
238
|
+
if [ -z "$chain" ]; then
|
|
239
|
+
eagle_log "ERROR" "No LLM provider configured or available. Run: eagle-mem config"
|
|
240
|
+
return 1
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
while IFS= read -r candidate; do
|
|
244
|
+
[ -z "$candidate" ] && continue
|
|
245
|
+
tried=$((tried + 1))
|
|
246
|
+
result=$(_eagle_call_provider_once "$candidate" "$prompt" "$system_prompt" "$max_tokens")
|
|
247
|
+
rc=$?
|
|
248
|
+
if [ "$rc" -eq 0 ] && [ -n "$result" ]; then
|
|
249
|
+
[ "$candidate" != "$provider" ] && eagle_log "INFO" "LLM provider fallback succeeded: primary=$provider used=$candidate"
|
|
250
|
+
printf '%s\n' "$result"
|
|
251
|
+
return 0
|
|
252
|
+
fi
|
|
253
|
+
eagle_log "WARN" "LLM provider candidate failed: primary=$provider candidate=$candidate rc=$rc"
|
|
254
|
+
done <<< "$chain"
|
|
255
|
+
|
|
256
|
+
eagle_log "ERROR" "All LLM provider candidates failed: primary=$provider tried=$tried"
|
|
257
|
+
return 1
|
|
258
|
+
}
|
|
228
259
|
|
|
260
|
+
_eagle_call_provider_once() {
|
|
261
|
+
local provider="$1" prompt="$2" system_prompt="$3" max_tokens="$4"
|
|
229
262
|
case "$provider" in
|
|
230
|
-
ollama)
|
|
263
|
+
ollama) _eagle_call_ollama "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
231
264
|
agent_cli) _eagle_call_agent_cli "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
232
265
|
anthropic) _eagle_call_anthropic "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
233
|
-
openai)
|
|
266
|
+
openai) _eagle_call_openai "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
234
267
|
none)
|
|
235
268
|
eagle_log "ERROR" "No LLM provider configured. Run: eagle-mem config"
|
|
236
269
|
return 1
|
|
@@ -242,6 +275,79 @@ eagle_llm_call() {
|
|
|
242
275
|
esac
|
|
243
276
|
}
|
|
244
277
|
|
|
278
|
+
_eagle_provider_chain() {
|
|
279
|
+
local primary="${1:-none}" fallback
|
|
280
|
+
fallback=$(eagle_config_get "provider" "fallback" "auto")
|
|
281
|
+
|
|
282
|
+
local chain=""
|
|
283
|
+
_eagle_provider_chain_add() {
|
|
284
|
+
local candidate="$1" required="${2:-available}"
|
|
285
|
+
[ -n "$candidate" ] || return 0
|
|
286
|
+
case " $chain " in *" $candidate "*) return 0 ;; esac
|
|
287
|
+
if [ "$required" = "available" ] && ! _eagle_provider_available "$candidate"; then
|
|
288
|
+
return 0
|
|
289
|
+
fi
|
|
290
|
+
chain="${chain}${chain:+ }$candidate"
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if [ "$primary" != "none" ]; then
|
|
294
|
+
_eagle_provider_chain_add "$primary" "always"
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
if [ "$fallback" != "off" ]; then
|
|
298
|
+
case "$primary" in
|
|
299
|
+
ollama) _eagle_provider_chain_add "agent_cli"; _eagle_provider_chain_add "anthropic"; _eagle_provider_chain_add "openai" ;;
|
|
300
|
+
agent_cli) _eagle_provider_chain_add "ollama"; _eagle_provider_chain_add "anthropic"; _eagle_provider_chain_add "openai" ;;
|
|
301
|
+
anthropic) _eagle_provider_chain_add "ollama"; _eagle_provider_chain_add "agent_cli"; _eagle_provider_chain_add "openai" ;;
|
|
302
|
+
openai) _eagle_provider_chain_add "ollama"; _eagle_provider_chain_add "agent_cli"; _eagle_provider_chain_add "anthropic" ;;
|
|
303
|
+
none|"") _eagle_provider_chain_add "ollama"; _eagle_provider_chain_add "agent_cli"; _eagle_provider_chain_add "anthropic"; _eagle_provider_chain_add "openai" ;;
|
|
304
|
+
esac
|
|
305
|
+
fi
|
|
306
|
+
|
|
307
|
+
printf '%s\n' "$chain" | tr ' ' '\n' | sed '/^$/d'
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
_eagle_provider_available() {
|
|
311
|
+
case "$1" in
|
|
312
|
+
ollama)
|
|
313
|
+
[ -n "$(eagle_detect_ollama "$(eagle_config_get "ollama" "url" "$EAGLE_DEFAULT_OLLAMA_URL")" 2>/dev/null || true)" ]
|
|
314
|
+
;;
|
|
315
|
+
agent_cli)
|
|
316
|
+
[ -n "$(_eagle_agent_cli_target_chain)" ]
|
|
317
|
+
;;
|
|
318
|
+
anthropic)
|
|
319
|
+
[ -n "${ANTHROPIC_API_KEY:-}" ]
|
|
320
|
+
;;
|
|
321
|
+
openai)
|
|
322
|
+
[ -n "${OPENAI_API_KEY:-}" ]
|
|
323
|
+
;;
|
|
324
|
+
*) return 1 ;;
|
|
325
|
+
esac
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
_eagle_provider_model_label() {
|
|
329
|
+
case "$1" in
|
|
330
|
+
ollama) printf 'ollama:%s' "$(eagle_config_get "ollama" "model" "mistral")" ;;
|
|
331
|
+
agent_cli) printf 'agent_cli:%s' "$(_eagle_agent_cli_target_summary)" ;;
|
|
332
|
+
anthropic) printf 'anthropic:%s' "$(eagle_config_get "anthropic" "model" "claude-haiku-4-5-20251001")" ;;
|
|
333
|
+
openai) printf 'openai:%s' "$(eagle_config_get "openai" "model" "gpt-4o-mini")" ;;
|
|
334
|
+
*) printf '%s' "$1" ;;
|
|
335
|
+
esac
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
eagle_llm_provider_label() {
|
|
339
|
+
local provider chain labels="" candidate sep=""
|
|
340
|
+
provider=$(eagle_config_get "provider" "type" "none")
|
|
341
|
+
chain=$(_eagle_provider_chain "$provider")
|
|
342
|
+
while IFS= read -r candidate; do
|
|
343
|
+
[ -z "$candidate" ] && continue
|
|
344
|
+
labels="${labels}${sep}$(_eagle_provider_model_label "$candidate")"
|
|
345
|
+
sep=" -> "
|
|
346
|
+
done <<< "$chain"
|
|
347
|
+
[ -n "$labels" ] || labels="none"
|
|
348
|
+
printf '%s\n' "$labels"
|
|
349
|
+
}
|
|
350
|
+
|
|
245
351
|
_eagle_call_ollama() {
|
|
246
352
|
local prompt="$1" system="$2" max_tokens="$3"
|
|
247
353
|
local url model
|
|
@@ -281,35 +387,57 @@ _eagle_call_ollama() {
|
|
|
281
387
|
}
|
|
282
388
|
|
|
283
389
|
_eagle_agent_cli_target() {
|
|
284
|
-
local
|
|
390
|
+
local first
|
|
391
|
+
first=$(_eagle_agent_cli_target_chain | sed -n '1p')
|
|
392
|
+
if [ -n "$first" ]; then
|
|
393
|
+
printf '%s\n' "$first"
|
|
394
|
+
else
|
|
395
|
+
printf 'none\n'
|
|
396
|
+
fi
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
_eagle_agent_cli_target_chain() {
|
|
400
|
+
local preferred current preferred_target targets="" candidate
|
|
285
401
|
preferred=$(eagle_config_get "agent_cli" "preferred" "current")
|
|
402
|
+
current=""
|
|
403
|
+
if [ -n "${EAGLE_AGENT_SOURCE:-${EAGLE_AGENT:-}}" ]; then
|
|
404
|
+
current=$(eagle_agent_source)
|
|
405
|
+
fi
|
|
286
406
|
|
|
287
407
|
case "$preferred" in
|
|
288
|
-
codex|openai-codex)
|
|
289
|
-
claude|claude-code|cloud-code)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
elif command -v codex &>/dev/null; then
|
|
294
|
-
echo "codex"
|
|
295
|
-
elif command -v claude &>/dev/null; then
|
|
296
|
-
echo "claude-code"
|
|
297
|
-
else
|
|
298
|
-
echo "none"
|
|
299
|
-
fi
|
|
300
|
-
;;
|
|
301
|
-
current|*)
|
|
302
|
-
if [ -n "${EAGLE_AGENT_SOURCE:-${EAGLE_AGENT:-}}" ]; then
|
|
303
|
-
eagle_agent_source
|
|
304
|
-
elif command -v codex &>/dev/null; then
|
|
305
|
-
echo "codex"
|
|
306
|
-
elif command -v claude &>/dev/null; then
|
|
307
|
-
echo "claude-code"
|
|
308
|
-
else
|
|
309
|
-
echo "none"
|
|
310
|
-
fi
|
|
311
|
-
;;
|
|
408
|
+
codex|openai-codex) preferred_target="codex" ;;
|
|
409
|
+
claude|claude-code|cloud-code) preferred_target="claude-code" ;;
|
|
410
|
+
current) preferred_target="$current" ;;
|
|
411
|
+
auto|"") preferred_target="" ;;
|
|
412
|
+
*) preferred_target="$preferred" ;;
|
|
312
413
|
esac
|
|
414
|
+
|
|
415
|
+
for candidate in "$preferred_target" "$current" codex claude-code; do
|
|
416
|
+
case "$candidate" in
|
|
417
|
+
codex|claude-code) ;;
|
|
418
|
+
*) continue ;;
|
|
419
|
+
esac
|
|
420
|
+
case "$candidate" in
|
|
421
|
+
codex) command -v codex >/dev/null 2>&1 || continue ;;
|
|
422
|
+
claude-code) command -v claude >/dev/null 2>&1 || continue ;;
|
|
423
|
+
esac
|
|
424
|
+
case " $targets " in *" $candidate "*) continue ;; esac
|
|
425
|
+
targets="${targets}${targets:+ }$candidate"
|
|
426
|
+
done
|
|
427
|
+
|
|
428
|
+
printf '%s\n' "$targets" | tr ' ' '\n' | sed '/^$/d'
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
_eagle_agent_cli_target_summary() {
|
|
432
|
+
local chain summary="" sep="" target
|
|
433
|
+
chain=$(_eagle_agent_cli_target_chain)
|
|
434
|
+
while IFS= read -r target; do
|
|
435
|
+
[ -z "$target" ] && continue
|
|
436
|
+
summary="${summary}${sep}${target}"
|
|
437
|
+
sep=" -> "
|
|
438
|
+
done <<< "$chain"
|
|
439
|
+
[ -n "$summary" ] || summary="none"
|
|
440
|
+
printf '%s\n' "$summary"
|
|
313
441
|
}
|
|
314
442
|
|
|
315
443
|
_eagle_agent_cli_prompt_file() {
|
|
@@ -327,17 +455,32 @@ _eagle_agent_cli_prompt_file() {
|
|
|
327
455
|
|
|
328
456
|
_eagle_call_agent_cli() {
|
|
329
457
|
local prompt="$1" system="$2" max_tokens="$3"
|
|
330
|
-
local target
|
|
331
|
-
|
|
458
|
+
local target targets result rc tried=0
|
|
459
|
+
targets=$(_eagle_agent_cli_target_chain)
|
|
332
460
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
461
|
+
if [ -z "$targets" ]; then
|
|
462
|
+
eagle_log "ERROR" "agent_cli provider unavailable: no supported Codex or Claude CLI found"
|
|
463
|
+
return 1
|
|
464
|
+
fi
|
|
465
|
+
|
|
466
|
+
while IFS= read -r target; do
|
|
467
|
+
[ -z "$target" ] && continue
|
|
468
|
+
tried=$((tried + 1))
|
|
469
|
+
case "$target" in
|
|
470
|
+
codex) result=$(_eagle_call_codex_cli "$prompt" "$system" "$max_tokens"); rc=$? ;;
|
|
471
|
+
claude-code) result=$(_eagle_call_claude_cli "$prompt" "$system" "$max_tokens"); rc=$? ;;
|
|
472
|
+
*) rc=1; result="" ;;
|
|
473
|
+
esac
|
|
474
|
+
if [ "$rc" -eq 0 ] && [ -n "$result" ]; then
|
|
475
|
+
[ "$tried" -gt 1 ] && eagle_log "INFO" "agent_cli fallback succeeded with $target"
|
|
476
|
+
printf '%s\n' "$result"
|
|
477
|
+
return 0
|
|
478
|
+
fi
|
|
479
|
+
eagle_log "WARN" "agent_cli target failed: target=$target rc=$rc"
|
|
480
|
+
done <<< "$targets"
|
|
481
|
+
|
|
482
|
+
eagle_log "ERROR" "All agent_cli targets failed: targets=$(printf '%s' "$targets" | tr '\n' ',')"
|
|
483
|
+
return 1
|
|
341
484
|
}
|
|
342
485
|
|
|
343
486
|
_eagle_call_codex_cli() {
|
|
@@ -539,16 +682,19 @@ eagle_show_config() {
|
|
|
539
682
|
return 1
|
|
540
683
|
fi
|
|
541
684
|
|
|
542
|
-
local provider model
|
|
685
|
+
local provider model fallback
|
|
543
686
|
provider=$(eagle_config_get "provider" "type" "none")
|
|
687
|
+
fallback=$(eagle_config_get "provider" "fallback" "auto")
|
|
544
688
|
if [ "$provider" = "agent_cli" ]; then
|
|
545
|
-
model=$(
|
|
689
|
+
model=$(_eagle_agent_cli_target_summary)
|
|
546
690
|
else
|
|
547
691
|
model=$(eagle_config_get "$provider" "model" "unknown")
|
|
548
692
|
fi
|
|
549
693
|
|
|
550
694
|
echo "Provider: $provider"
|
|
551
695
|
echo "Model: $model"
|
|
696
|
+
echo "Fallback: $fallback"
|
|
697
|
+
echo "Chain: $(eagle_llm_provider_label)"
|
|
552
698
|
|
|
553
699
|
if [ "$provider" = "ollama" ]; then
|
|
554
700
|
local url
|
|
@@ -564,6 +710,7 @@ eagle_show_config() {
|
|
|
564
710
|
fi
|
|
565
711
|
elif [ "$provider" = "agent_cli" ]; then
|
|
566
712
|
echo "Preferred: $(eagle_config_get "agent_cli" "preferred" "current")"
|
|
713
|
+
echo "Targets: $(_eagle_agent_cli_target_summary)"
|
|
567
714
|
echo "Codex: $(command -v codex 2>/dev/null || echo "not found")"
|
|
568
715
|
echo "Claude: $(command -v claude 2>/dev/null || echo "not found")"
|
|
569
716
|
fi
|
package/package.json
CHANGED
package/scripts/config.sh
CHANGED
|
@@ -36,6 +36,7 @@ show_help() {
|
|
|
36
36
|
echo -e " eagle-mem config set updates.allow patch"
|
|
37
37
|
echo -e " eagle-mem config set token_guard.rtk enforce"
|
|
38
38
|
echo -e " eagle-mem config set token_guard.raw_bash block"
|
|
39
|
+
echo -e " eagle-mem config set read_guard.mode advisory"
|
|
39
40
|
echo ""
|
|
40
41
|
exit 0
|
|
41
42
|
}
|
|
@@ -72,6 +73,7 @@ case "$subcommand" in
|
|
|
72
73
|
eagle_info " eagle-mem config set anthropic.model claude-haiku-4-5-20251001"
|
|
73
74
|
eagle_info " eagle-mem config set token_guard.rtk enforce"
|
|
74
75
|
eagle_info " eagle-mem config set token_guard.raw_bash block"
|
|
76
|
+
eagle_info " eagle-mem config set read_guard.mode advisory"
|
|
75
77
|
exit 1
|
|
76
78
|
fi
|
|
77
79
|
section="${key%%.*}"
|
package/scripts/curate.sh
CHANGED
|
@@ -37,6 +37,46 @@ Options:
|
|
|
37
37
|
EOF
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
parse_consolidations_json() {
|
|
41
|
+
local result="$1"
|
|
42
|
+
local trimmed json_payload
|
|
43
|
+
|
|
44
|
+
trimmed=$(printf '%s' "$result" | tr -d '\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
45
|
+
case "$trimmed" in
|
|
46
|
+
""|NONE|none|null)
|
|
47
|
+
printf '[]\n'
|
|
48
|
+
return 0
|
|
49
|
+
;;
|
|
50
|
+
esac
|
|
51
|
+
|
|
52
|
+
json_payload=$(printf '%s' "$result" \
|
|
53
|
+
| sed -e '1s/^[[:space:]]*```json[[:space:]]*$//' \
|
|
54
|
+
-e '1s/^[[:space:]]*```[[:space:]]*$//' \
|
|
55
|
+
-e '$s/^[[:space:]]*```[[:space:]]*$//')
|
|
56
|
+
|
|
57
|
+
printf '%s' "$json_payload" | jq -c '
|
|
58
|
+
def trim: gsub("^\\s+|\\s+$"; "");
|
|
59
|
+
def names:
|
|
60
|
+
if type == "array" then map(tostring | trim) | map(select(length > 0))
|
|
61
|
+
elif type == "string" then split(",") | map(trim) | map(select(length > 0))
|
|
62
|
+
else []
|
|
63
|
+
end;
|
|
64
|
+
def root:
|
|
65
|
+
if type == "array" then .
|
|
66
|
+
elif type == "object" then (.consolidations // .items // .instructions // [])
|
|
67
|
+
else []
|
|
68
|
+
end;
|
|
69
|
+
root
|
|
70
|
+
| map({
|
|
71
|
+
source_names: ((.source_names // .sourceNames // .source_memories // .sourceMemories // .original_names // .originalNames // .originals // .names) | names),
|
|
72
|
+
new_name: ((.new_name // .newName // .name // .title // "") | tostring | trim),
|
|
73
|
+
description: ((.description // "") | tostring),
|
|
74
|
+
value: ((.value // .content // .compiled_truth // .compiledTruth // "") | tostring)
|
|
75
|
+
})
|
|
76
|
+
| map(select((.source_names | length) > 0 and (.new_name | length) > 0))
|
|
77
|
+
' 2>/dev/null
|
|
78
|
+
}
|
|
79
|
+
|
|
40
80
|
while [ $# -gt 0 ]; do
|
|
41
81
|
case "$1" in
|
|
42
82
|
-h|--help)
|
|
@@ -60,19 +100,27 @@ fi
|
|
|
60
100
|
|
|
61
101
|
p_esc=$(eagle_sql_escape "$project")
|
|
62
102
|
|
|
103
|
+
cleanup_curate() {
|
|
104
|
+
local rc=$?
|
|
105
|
+
eagle_run_finish "$rc" "$LINENO"
|
|
106
|
+
}
|
|
107
|
+
eagle_run_start "curate" "$project" "$(pwd)"
|
|
108
|
+
trap cleanup_curate EXIT
|
|
109
|
+
|
|
63
110
|
# Verify provider is configured
|
|
64
111
|
provider=$(eagle_config_get "provider" "type" "none")
|
|
65
112
|
if [ "$provider" = "none" ]; then
|
|
66
113
|
eagle_err "No LLM provider configured. Run: eagle-mem config init"
|
|
67
114
|
exit 1
|
|
68
115
|
fi
|
|
69
|
-
eagle_info "Provider: $
|
|
116
|
+
eagle_info "Provider: $(eagle_llm_provider_label)"
|
|
70
117
|
eagle_info "Project: $project"
|
|
71
118
|
[ "$DRY_RUN" -eq 1 ] && eagle_info "Dry run — no changes will be made"
|
|
72
119
|
echo ""
|
|
73
120
|
|
|
74
121
|
# ─── 1. Analyze gotchas for promotion ─────────────────────
|
|
75
122
|
|
|
123
|
+
eagle_run_step "promote_gotchas"
|
|
76
124
|
eagle_info "Analyzing gotchas for promotion..."
|
|
77
125
|
|
|
78
126
|
recent_gotchas=$(eagle_db "SELECT gotchas, created_at
|
|
@@ -559,7 +607,11 @@ if [ -n "$co_edit_data" ]; then
|
|
|
559
607
|
co_wire_count=$((co_wire_count + 1))
|
|
560
608
|
fi
|
|
561
609
|
done <<< "$co_edit_data"
|
|
562
|
-
|
|
610
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
611
|
+
eagle_info "Would wire $co_wire_count co-edited file edges"
|
|
612
|
+
else
|
|
613
|
+
eagle_ok "Wired $co_wire_count co-edited file edges"
|
|
614
|
+
fi
|
|
563
615
|
fi
|
|
564
616
|
|
|
565
617
|
# 7.2 Wire session nodes and access edges
|
|
@@ -569,35 +621,55 @@ if [ -n "$recent_sessions" ]; then
|
|
|
569
621
|
if [ "$DRY_RUN" -eq 0 ]; then
|
|
570
622
|
session_wire_count=$(eagle_graph_wire_recent_session_edges "$project" 15)
|
|
571
623
|
fi
|
|
572
|
-
|
|
624
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
625
|
+
eagle_info "Would wire $session_wire_count recent session nodes and edges"
|
|
626
|
+
else
|
|
627
|
+
eagle_ok "Wired $session_wire_count recent session nodes and edges"
|
|
628
|
+
fi
|
|
573
629
|
fi
|
|
574
630
|
|
|
575
631
|
# 7.3 Offline Memory Consolidation (Compiled Truth vs Evidence)
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
632
|
+
active_memory_rows=$(eagle_db_json "SELECT memory_name, memory_type, description, content FROM agent_memories WHERE project = '$p_esc';" || {
|
|
633
|
+
eagle_log "WARN" "Unable to read active agent memories for consolidation"
|
|
634
|
+
echo "[]"
|
|
635
|
+
})
|
|
636
|
+
active_memory_count=$(printf '%s' "$active_memory_rows" | jq 'length' 2>/dev/null || echo 0)
|
|
637
|
+
if [ "$active_memory_count" -gt 0 ]; then
|
|
638
|
+
wired_memory_count=0
|
|
581
639
|
memory_node_index=0
|
|
582
|
-
while [ "$memory_node_index" -lt "$
|
|
583
|
-
mname=$(printf '%s' "$
|
|
584
|
-
mcontent=$(printf '%s' "$
|
|
640
|
+
while [ "$memory_node_index" -lt "$active_memory_count" ]; do
|
|
641
|
+
mname=$(printf '%s' "$active_memory_rows" | jq -r ".[$memory_node_index].memory_name // empty")
|
|
642
|
+
mcontent=$(printf '%s' "$active_memory_rows" | jq -r ".[$memory_node_index].content // empty")
|
|
585
643
|
memory_node_index=$((memory_node_index + 1))
|
|
586
644
|
[ -n "$mname" ] || continue
|
|
587
645
|
if [ "$DRY_RUN" -eq 0 ]; then
|
|
588
646
|
eagle_graph_add_node "$project" "memory" "$mname" "$mcontent" ""
|
|
589
647
|
fi
|
|
590
|
-
|
|
648
|
+
wired_memory_count=$((wired_memory_count + 1))
|
|
591
649
|
done
|
|
592
|
-
|
|
650
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
651
|
+
eagle_info "Would wire $wired_memory_count agent memory graph nodes"
|
|
652
|
+
else
|
|
653
|
+
eagle_ok "Wired $wired_memory_count agent memory graph nodes"
|
|
654
|
+
fi
|
|
593
655
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
656
|
+
memory_prompt_json=$(printf '%s' "$active_memory_rows" | jq -c '[.[] | {
|
|
657
|
+
memory_name: .memory_name,
|
|
658
|
+
memory_type: .memory_type,
|
|
659
|
+
description: .description,
|
|
660
|
+
content: .content
|
|
661
|
+
}]')
|
|
598
662
|
|
|
599
|
-
|
|
600
|
-
|
|
663
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
664
|
+
eagle_info "Would analyze $wired_memory_count agent memories for consolidation"
|
|
665
|
+
else
|
|
666
|
+
consolidation_prompt="Analyze these mirrored agent memories for project '$project'. Identify memories that are redundant, overlap in scope, or describe the same subsystem/gotcha/concept.
|
|
667
|
+
|
|
668
|
+
INPUT_MEMORIES_JSON:
|
|
669
|
+
$memory_prompt_json
|
|
670
|
+
|
|
671
|
+
For any memories that should be consolidated, merge them into a single Compiled Truth summary.
|
|
672
|
+
The consolidated memory value MUST be formatted exactly as:
|
|
601
673
|
--- Compiled Truth ---
|
|
602
674
|
<A structured, clear, up-to-date summary of the topic, gotten by merging the duplicate/overlapping memories. Keep it extremely precise.>
|
|
603
675
|
|
|
@@ -605,55 +677,59 @@ The consolidated memory MUST be formatted exactly as:
|
|
|
605
677
|
- <Original memory title 1>: <brief original description or timestamp>
|
|
606
678
|
- <Original memory title 2>: <brief original description or timestamp>
|
|
607
679
|
|
|
608
|
-
|
|
609
|
-
|
|
680
|
+
Return strict JSON only, with this schema:
|
|
681
|
+
{
|
|
682
|
+
\"consolidations\": [
|
|
683
|
+
{
|
|
684
|
+
\"source_names\": [\"<exact source memory_name>\", \"<exact source memory_name>\"],
|
|
685
|
+
\"new_name\": \"<new consolidated memory name>\",
|
|
686
|
+
\"description\": \"<new description>\",
|
|
687
|
+
\"value\": \"<the merged compiled truth + evidence content>\"
|
|
688
|
+
}
|
|
689
|
+
]
|
|
690
|
+
}
|
|
610
691
|
|
|
611
|
-
If no memories need consolidation,
|
|
692
|
+
If no memories need consolidation, return {\"consolidations\":[]}."
|
|
612
693
|
|
|
613
|
-
|
|
694
|
+
consolidation_result=$(eagle_llm_call "$consolidation_prompt" "You consolidate software development memories into a single compiled truth. Return strict JSON only." 1024 || true)
|
|
695
|
+
if ! parsed_consolidations=$(parse_consolidations_json "$consolidation_result"); then
|
|
696
|
+
eagle_log "WARN" "Unable to parse memory consolidation provider response"
|
|
697
|
+
parsed_consolidations="[]"
|
|
698
|
+
fi
|
|
614
699
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
700
|
+
consolidation_count=$(printf '%s' "$parsed_consolidations" | jq 'length' 2>/dev/null || echo 0)
|
|
701
|
+
if [ "$consolidation_count" -gt 0 ]; then
|
|
702
|
+
cons_count=0
|
|
703
|
+
consolidation_index=0
|
|
704
|
+
while [ "$consolidation_index" -lt "$consolidation_count" ]; do
|
|
705
|
+
consolidation_item=$(printf '%s' "$parsed_consolidations" | jq -c ".[$consolidation_index]")
|
|
706
|
+
consolidation_index=$((consolidation_index + 1))
|
|
707
|
+
new_name=$(printf '%s' "$consolidation_item" | jq -r '.new_name // empty')
|
|
708
|
+
val_part=$(printf '%s' "$consolidation_item" | jq -r '.value // empty')
|
|
709
|
+
[ -n "$new_name" ] || continue
|
|
710
|
+
|
|
711
|
+
eagle_graph_add_node "$project" "memory" "$new_name" "$val_part" ""
|
|
712
|
+
new_node_id=$(eagle_graph_get_node_id "$project" "memory" "$new_name")
|
|
713
|
+
|
|
714
|
+
old_count=$(printf '%s' "$consolidation_item" | jq '.source_names | length' 2>/dev/null || echo 0)
|
|
715
|
+
old_index=0
|
|
716
|
+
while [ "$old_index" -lt "$old_count" ]; do
|
|
717
|
+
old_n=$(printf '%s' "$consolidation_item" | jq -r ".source_names[$old_index] // empty")
|
|
718
|
+
old_index=$((old_index + 1))
|
|
719
|
+
[ -n "$old_n" ] || continue
|
|
720
|
+
old_node_id=$(eagle_graph_get_node_id "$project" "memory" "$old_n")
|
|
721
|
+
if [ -n "$old_node_id" ] && [ -n "$new_node_id" ]; then
|
|
722
|
+
eagle_graph_add_edge "$project" "$new_node_id" "$old_node_id" "supersedes" 1.0
|
|
634
723
|
else
|
|
635
|
-
|
|
636
|
-
eagle_graph_add_node "$project" "memory" "$new_name" "$val_part" ""
|
|
637
|
-
new_node_id=$(eagle_graph_get_node_id "$project" "memory" "$new_name")
|
|
638
|
-
|
|
639
|
-
# 2. Wire supersedes edges from new node to old nodes, and mark old nodes as inactive/superseded
|
|
640
|
-
IFS=',' read -ra name_arr <<< "$names_part"
|
|
641
|
-
for old_n in "${name_arr[@]}"; do
|
|
642
|
-
old_n=$(echo "$old_n" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
643
|
-
if [ -z "$old_n" ]; then continue; fi
|
|
644
|
-
old_node_id=$(eagle_graph_get_node_id "$project" "memory" "$old_n")
|
|
645
|
-
if [ -n "$old_node_id" ] && [ -n "$new_node_id" ]; then
|
|
646
|
-
eagle_graph_add_edge "$project" "$new_node_id" "$old_node_id" "supersedes" 1.0
|
|
647
|
-
fi
|
|
648
|
-
done
|
|
649
|
-
cons_count=$((cons_count + 1))
|
|
724
|
+
eagle_log "WARN" "Unable to wire supersedes edge: source='$old_n' consolidated='$new_name'"
|
|
650
725
|
fi
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
726
|
+
done
|
|
727
|
+
cons_count=$((cons_count + 1))
|
|
728
|
+
done
|
|
729
|
+
eagle_ok "Consolidated $cons_count sets of overlapping agent memories"
|
|
730
|
+
else
|
|
731
|
+
eagle_ok "Agent memories are fully consolidated and up to date"
|
|
732
|
+
fi
|
|
657
733
|
fi
|
|
658
734
|
else
|
|
659
735
|
eagle_dim " No active agent memories to consolidate"
|
package/scripts/health.sh
CHANGED
|
@@ -136,14 +136,11 @@ max_score=$((max_score + 15))
|
|
|
136
136
|
|
|
137
137
|
provider=$(eagle_config_get "provider" "type" "none")
|
|
138
138
|
if [ "$provider" != "none" ]; then
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
else
|
|
142
|
-
model=$(eagle_config_get "$provider" "model" "default")
|
|
143
|
-
fi
|
|
144
|
-
eagle_ok "Provider: ${provider} (${model})"
|
|
139
|
+
provider_chain=$(eagle_llm_provider_label)
|
|
140
|
+
eagle_ok "Provider: ${provider_chain}"
|
|
145
141
|
score=$((score + 15))
|
|
146
142
|
else
|
|
143
|
+
provider_chain="none"
|
|
147
144
|
eagle_fail "No LLM provider — curator and enrichment disabled"
|
|
148
145
|
issues+=("Configure a provider: eagle-mem config init")
|
|
149
146
|
fi
|
|
@@ -321,6 +318,7 @@ if [ "$JSON_OUT" -eq 1 ]; then
|
|
|
321
318
|
--argjson enriched_summaries "${enriched_summaries:-0}" \
|
|
322
319
|
--argjson features "${feature_count:-0}" \
|
|
323
320
|
--arg provider "$provider" \
|
|
321
|
+
--arg provider_chain "$provider_chain" \
|
|
324
322
|
--arg token_guard_rtk "$rtk_mode" \
|
|
325
323
|
--arg token_guard_raw_bash "$raw_bash_mode" \
|
|
326
324
|
--arg rtk_bin "${rtk_bin:-}" \
|
|
@@ -345,7 +343,7 @@ if [ "$JSON_OUT" -eq 1 ]; then
|
|
|
345
343
|
'{project:$project, score:$score, max:$max_score, pct:$pct, grade:$grade,
|
|
346
344
|
capture:{sessions:$total_sessions, summaries:$total_summaries, heuristic:$heuristic_summaries},
|
|
347
345
|
enrichment:$enriched_summaries,
|
|
348
|
-
features:$features, provider:$provider,
|
|
346
|
+
features:$features, provider:$provider, provider_chain:$provider_chain,
|
|
349
347
|
token_guard:{rtk:$token_guard_rtk, raw_bash:$token_guard_raw_bash, rtk_bin:$rtk_bin},
|
|
350
348
|
orchestration:{
|
|
351
349
|
route:$orchestration_route,
|
package/scripts/help.sh
CHANGED
|
@@ -23,6 +23,7 @@ echo -e " ${CYAN}uninstall${RESET} Remove hooks and optionally delete data"
|
|
|
23
23
|
echo -e " ${CYAN}search${RESET} Search past sessions, memories, and code"
|
|
24
24
|
echo -e " ${CYAN}health${RESET} Diagnose pipeline health and background automation"
|
|
25
25
|
echo -e " ${CYAN}doctor${RESET} Show install footprint, hooks, SQLite, manifest, and runtime drift"
|
|
26
|
+
echo -e " ${CYAN}logs${RESET} Inspect command-scoped scan/index/curate logs"
|
|
26
27
|
echo -e " ${CYAN}updates${RESET} Auto-update status and policy"
|
|
27
28
|
echo -e " ${CYAN}overview${RESET} Build or view project overview"
|
|
28
29
|
echo -e " ${CYAN}session${RESET} Save a manual session summary"
|