fix(agents): add waitForIdle to all flushPendingToolResults call sites

The original fix only covered the main run finally block, but there are
two additional call sites that can trigger flushPendingToolResults while
tools are still executing:

1. The catch block in attempt.ts (session setup error handler)
2. The finally block in compact.ts (compaction teardown)

Both now await agent.waitForIdle() with a 30s timeout before flushing,
matching the pattern already applied to the main finally block.

Production testing on VPS with debug logging confirmed these additional
paths can fire during sub-agent runs, producing spurious synthetic
'missing tool result' errors.
This commit is contained in:
Renue Development
2026-02-11 13:37:12 +11:00
parent d602bc16a1
commit bd2ea02541
2 changed files with 24 additions and 0 deletions

View File

@@ -464,6 +464,18 @@ export async function compactEmbeddedPiSessionDirect(
},
};
} finally {
// Wait for agent idle before flushing to prevent inserting synthetic
// "missing tool result" errors while tools are still executing.
if (session?.agent?.waitForIdle) {
try {
await Promise.race([
session.agent.waitForIdle(),
new Promise<void>((resolve) => setTimeout(resolve, 30_000)),
]);
} catch {
/* best-effort */
}
}
sessionManager.flushPendingToolResults?.();
session.dispose();
}

View File

@@ -565,6 +565,18 @@ export async function runEmbeddedAttempt(
activeSession.agent.replaceMessages(limited);
}
} catch (err) {
// Wait for agent idle before flushing to avoid the same race condition
// as the main finally block (see comment below).
if (activeSession?.agent?.waitForIdle) {
try {
await Promise.race([
activeSession.agent.waitForIdle(),
new Promise<void>((resolve) => setTimeout(resolve, 30_000)),
]);
} catch {
/* best-effort */
}
}
sessionManager.flushPendingToolResults?.();
activeSession.dispose();
throw err;