refactor(eval): changed timeout from inactivity to wall-clock budget

- Only bridge heartbeats (`agent()`/`llm()`) now re-arm the watchdog; compute, stdout, `log()`/`phase()`, and ordinary tool calls count against the budget.
- Emitted an immediate heartbeat at bridge call start to avoid early abort near budget edge.
- Removed `idle` flag and "of inactivity" suffix from timeout annotation strings.
- Updated docs, prompts, and comments to reflect the new wall-clock semantics.
This commit is contained in:
can1357
2026-06-01 17:17:00 +02:00
parent c7923e77eb
commit dbd9489010
11 changed files with 200 additions and 66 deletions
@@ -60,11 +60,10 @@ function isTimeoutReason(reason: unknown): boolean {
);
}
function formatJsTimeoutAnnotation(timeoutMs: number | undefined, idle: boolean): string {
const suffix = idle ? " of inactivity" : "";
function formatJsTimeoutAnnotation(timeoutMs: number | undefined): string {
if (timeoutMs === undefined) return "Command timed out";
const secs = Math.max(1, Math.round(timeoutMs / 1000));
return `Command timed out after ${secs} seconds${suffix}`;
return `Command timed out after ${secs} seconds`;
}
export async function executeJs(code: string, options: JsExecutorOptions): Promise<JsResult> {
@@ -86,10 +85,9 @@ export async function executeJs(code: string, options: JsExecutorOptions): Promi
options.signal && timeoutSignal
? AbortSignal.any([options.signal, timeoutSignal])
: (options.signal ?? timeoutSignal);
// Idle mode: the eval tool drives cancellation via an idle-aware `signal` and
// passes only an inactivity budget. Use it for worker cold-start headroom and
// timeout-annotation text; never derive a competing fixed timer from it.
const idleMode = legacyTimeoutMs === undefined && options.idleTimeoutMs !== undefined;
// The eval tool drives cancellation via an idle-aware `signal` and passes only
// an inactivity budget; use it solely as worker cold-start headroom and never
// derive a competing fixed timer from it.
const acquireBudgetMs = legacyTimeoutMs ?? options.idleTimeoutMs;
try {
@@ -133,7 +131,7 @@ export async function executeJs(code: string, options: JsExecutorOptions): Promi
if (signal?.aborted || isAbortError(error)) {
const timedOut = Boolean(timeoutSignal?.aborted) || isTimeoutReason(options.signal?.reason);
if (timedOut) {
outputSink.push(formatJsTimeoutAnnotation(legacyTimeoutMs ?? options.idleTimeoutMs, idleMode));
outputSink.push(formatJsTimeoutAnnotation(legacyTimeoutMs ?? options.idleTimeoutMs));
}
const summary = await outputSink.dump();
return {