From f502bfbd42e3e7cf514c002062a302f8d8aed4ac Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 3 Jul 2026 11:00:50 -0400 Subject: [PATCH] Treat local agent API failures as unhealthy --- src/agent/local-agents.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/agent/local-agents.ts b/src/agent/local-agents.ts index d2c86ae..fa6dd2e 100644 --- a/src/agent/local-agents.ts +++ b/src/agent/local-agents.ts @@ -236,6 +236,13 @@ export interface AgentRunResult { error?: string; } +function agentFailureOutput(output: string): string | null { + const text = output.trim(); + if (/^API call failed\b/i.test(text)) return text.slice(0, 300); + if (/connection error/i.test(text) && /failed/i.test(text)) return text.slice(0, 300); + return null; +} + /** * Drive a connected agent with a one-shot headless prompt (real round-trip — SPENDS the agent's quota). * Used by /ping (liveness proof) and /dispatch (actually using the operator). @@ -270,7 +277,9 @@ export function runLocalAgent( child.on('close', (code) => { const latencyMs = Date.now() - t0; const output = out.trim().slice(0, maxChars); - if (code === 0) finish({ ok: true, latencyMs, output }); + const semanticError = agentFailureOutput(output); + if (code === 0 && !semanticError) finish({ ok: true, latencyMs, output }); + else if (semanticError) finish({ ok: false, latencyMs, output, error: semanticError }); else finish({ ok: false, latencyMs, output, error: (errOut.trim() || `exited with code ${code}`).slice(0, 300) }); }); }); @@ -329,7 +338,9 @@ export function localAgentChat(id: string, prompt: string, opts: { model?: strin let content = out.trim(); if (outFile) { try { content = (readFileSync(outFile, 'utf8').trim() || content); } catch { /* fall back to stdout */ } } finish(() => { - if (code === 0 && content) resolve(content); + const semanticError = agentFailureOutput(content); + if (code === 0 && content && !semanticError) resolve(content); + else if (semanticError) reject(new Error(semanticError)); else reject(new Error((errOut.trim() || content || `exited with code ${code}`).slice(0, 800))); }); });