Skip to content
This repository was archived by the owner on Apr 11, 2026. It is now read-only.

Commit 6461794

Browse files
z23ccclaude
andcommitted
fix(hooks): add 10s timeout to gemini CLI in auto-memory hook
The auto-memory Stop hook calls `gemini -p` for AI summarization of session transcripts. When the Gemini CLI hangs (auth issues, network), the hook blocks indefinitely until killed by session teardown (SIGKILL). Replace Command::output() with spawn + poll loop with 10s timeout. On timeout, gracefully falls back to pattern matching extraction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3805203 commit 6461794

1 file changed

Lines changed: 47 additions & 6 deletions

File tree

flowctl/crates/flowctl-cli/src/commands/hook/auto_memory.rs

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,15 +169,56 @@ fn summarize_with_gemini(text: &str) -> Vec<Memory> {
169169

170170
let prompt = format!("{SUMMARIZE_PROMPT}{truncated}");
171171

172-
let result = Command::new("gemini")
172+
// Spawn with timeout — gemini CLI can hang on auth/network issues.
173+
// 10s is generous for a short API call; if it takes longer, fall back
174+
// to pattern matching rather than blocking the session teardown.
175+
let mut child = match Command::new("gemini")
173176
.args(["-p", &prompt])
174-
.output();
175-
176-
let output = match result {
177-
Ok(o) if o.status.success() => String::from_utf8_lossy(&o.stdout).trim().to_string(),
178-
_ => return Vec::new(),
177+
.stdout(std::process::Stdio::piped())
178+
.stderr(std::process::Stdio::null())
179+
.spawn()
180+
{
181+
Ok(c) => c,
182+
Err(_) => return Vec::new(),
179183
};
180184

185+
let timeout = std::time::Duration::from_secs(10);
186+
let start = std::time::Instant::now();
187+
loop {
188+
match child.try_wait() {
189+
Ok(Some(status)) => {
190+
if !status.success() {
191+
return Vec::new();
192+
}
193+
break;
194+
}
195+
Ok(None) => {
196+
if start.elapsed() > timeout {
197+
let _ = child.kill();
198+
let _ = child.wait();
199+
eprintln!("auto-memory: gemini timed out after 10s, falling back to pattern matching");
200+
return Vec::new();
201+
}
202+
std::thread::sleep(std::time::Duration::from_millis(100));
203+
}
204+
Err(_) => return Vec::new(),
205+
}
206+
}
207+
208+
let output = child
209+
.stdout
210+
.take()
211+
.map(|mut s| {
212+
let mut buf = String::new();
213+
std::io::Read::read_to_string(&mut s, &mut buf).ok();
214+
buf.trim().to_string()
215+
})
216+
.unwrap_or_default();
217+
218+
if output.is_empty() {
219+
return Vec::new();
220+
}
221+
181222
// Extract JSON array from output (may have surrounding text)
182223
let re = Regex::new(r"(?s)\[.*\]").unwrap();
183224
let json_str = match re.find(&output) {

0 commit comments

Comments
 (0)