fix(repl): show recon/probe activity + don't let the idle guardrail kill recon

Symptom: with a non-streaming subscription CLI (codex), a long/intense recon
showed nothing in the feed ("phase starting") and the 5-min idle guardrail killed
the run before any agent ran.
- render_compact now SHOWS recon/probe/ai-recon/skills-audit/loaded/running lines
  (were dropped) so a long recon no longer looks frozen.
- Idle guardrail reworked: resets on ANY streamed activity (not only new
  findings) and only ARMS after exploitation starts (agent launch / vote) — recon
  can never trip it. Message: "no activity in N min".
- RunLive.ingest sets phase=recon on recon/probe lines (was stuck at "starting").
This commit is contained in:
CyberSecurityUP committed 2026-07-10 17:01:42 -03:00
1 parent d9c191ec39
commit 5b9d485025
2 files changed
+24 -10

No files matched your search

+8 -2
View File
@@ -1076,10 +1076,16 @@ pub(crate) fn render_compact(raw: &str) -> Option<String> {
"ai" => return None, // skip verbose model chatter in background feed
_ => {
let low = line.to_lowercase();
if low.contains("recon complete") { "\x1b[36m 🔍 recon complete\x1b[0m".into() }
// Recon / probe activity — SHOW it so a long recon (esp. via a
// non-streaming CLI like codex) doesn't look frozen.
if low.starts_with("probe:") { format!("\x1b[36m 🔎 {}\x1b[0m", trunc1(line, 130)) }
else if low.contains("recon complete") { "\x1b[36m 🔍 recon complete\x1b[0m".into() }
else if low.starts_with("recon") || low.starts_with("ai-recon") || low.contains("recon round") || low.contains("intensity") { format!("\x1b[36m 🔍 {}\x1b[0m", trunc1(line, 130)) }
else if low.starts_with("skills audit") || low.starts_with("ai engagement") { format!("\x1b[36m 🤖 {}\x1b[0m", trunc1(line, 130)) }
else if low.starts_with("loaded ") || low.starts_with("running ") { format!("\x1b[36m 🧭 {}\x1b[0m", trunc1(line, 130)) }
else if low.contains("selected") && low.contains("agent") { format!("\x1b[36m 🧭 {}\x1b[0m", trunc1(line, 110)) }
else if low.starts_with("vote") && low.contains("confirmed") { format!("\x1b[1;32m ✓ {}\x1b[0m", trunc1(line, 110)) }
else if low.starts_with("exploit") || low.starts_with("test ") || low.contains("launching agent") { format!("\x1b[35m 🧪 {}\x1b[0m", trunc1(line, 110)) }
else if low.starts_with("exploit") || low.starts_with("test ") || low.starts_with("ai ") || low.starts_with("skill ") || low.contains("launching agent") { format!("\x1b[35m 🧪 {}\x1b[0m", trunc1(line, 110)) }
else if low.starts_with("vote") { format!("\x1b[2m · {}\x1b[0m", trunc1(line, 110)) }
else if low.contains("fail") || low.contains("error") { format!("\x1b[31m ✗ {}\x1b[0m", trunc1(line, 110)) }
else { return None; }