openclaw/src/agents/pi-embedded-runner/wait-for-idle-before-flush.ts

59 lines
1.7 KiB
TypeScript

type IdleAwareAgent = {
waitForIdle?: (() => Promise<void>) | undefined;
};
type ToolResultFlushManager = {
flushPendingToolResults?: (() => void) | undefined;
clearPendingToolResults?: (() => void) | undefined;
};
export const DEFAULT_WAIT_FOR_IDLE_TIMEOUT_MS = 30_000;
async function waitForAgentIdleBestEffort(
agent: IdleAwareAgent | null | undefined,
timeoutMs: number,
): Promise<boolean> {
const waitForIdle = agent?.waitForIdle;
if (typeof waitForIdle !== "function") {
return false;
}
const idleResolved = Symbol("idle");
const idleTimedOut = Symbol("timeout");
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
try {
const outcome = await Promise.race([
waitForIdle.call(agent).then(() => idleResolved),
new Promise<symbol>((resolve) => {
timeoutHandle = setTimeout(() => resolve(idleTimedOut), timeoutMs);
timeoutHandle.unref?.();
}),
]);
return outcome === idleTimedOut;
} catch {
// Best-effort during cleanup.
return false;
} finally {
if (timeoutHandle) {
clearTimeout(timeoutHandle);
}
}
}
export async function flushPendingToolResultsAfterIdle(opts: {
agent: IdleAwareAgent | null | undefined;
sessionManager: ToolResultFlushManager | null | undefined;
timeoutMs?: number;
clearPendingOnTimeout?: boolean;
}): Promise<void> {
const timedOut = await waitForAgentIdleBestEffort(
opts.agent,
opts.timeoutMs ?? DEFAULT_WAIT_FOR_IDLE_TIMEOUT_MS,
);
if (timedOut && opts.clearPendingOnTimeout && opts.sessionManager?.clearPendingToolResults) {
opts.sessionManager.clearPendingToolResults();
return;
}
opts.sessionManager?.flushPendingToolResults?.();
}