* Fix subagent announce race and timeout handling Bug 1: Subagent announce fires before model failover retries finish - Problem: CLI provider emitted lifecycle error on each attempt, causing subagent registry to prematurely call beginSubagentCleanup() and announce with incorrect status before failover retries completed - Fix: Removed lifecycle error emission from CLI provider's attempt-level .catch() in agent-runner-execution.ts. Errors still propagate to runWithModelFallback for retry, but no intermediate lifecycle events are emitted. Only the final outcome (after all retries) emits lifecycle events. Bug 2: Hard 600s per-prompt timeout ignores runTimeoutSeconds=0 - Problem: When runTimeoutSeconds=0 (meaning 'no timeout'), the code returned the default 600s timeout instead of respecting the 0 setting - Fix: Modified resolveAgentTimeoutMs() to treat 0 as 'no timeout' and return a very large timeout value (30 days) instead of the default. This avoids setTimeout issues with Infinity while effectively providing unlimited time for long-running tasks. * fix: emit lifecycle:error for CLI failures (#6621) (thanks @tyler6204) * chore: satisfy format/lint gates (#6621) (thanks @tyler6204) * fix: restore build after upstream type changes (#6621) (thanks @tyler6204) * test: fix createSystemPromptOverride tests to match new return type (#6621) (thanks @tyler6204)
47 lines
1.5 KiB
TypeScript
47 lines
1.5 KiB
TypeScript
import type { OpenClawConfig } from "../config/config.js";
|
|
|
|
const DEFAULT_AGENT_TIMEOUT_SECONDS = 600;
|
|
|
|
const normalizeNumber = (value: unknown): number | undefined =>
|
|
typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : undefined;
|
|
|
|
export function resolveAgentTimeoutSeconds(cfg?: OpenClawConfig): number {
|
|
const raw = normalizeNumber(cfg?.agents?.defaults?.timeoutSeconds);
|
|
const seconds = raw ?? DEFAULT_AGENT_TIMEOUT_SECONDS;
|
|
return Math.max(seconds, 1);
|
|
}
|
|
|
|
export function resolveAgentTimeoutMs(opts: {
|
|
cfg?: OpenClawConfig;
|
|
overrideMs?: number | null;
|
|
overrideSeconds?: number | null;
|
|
minMs?: number;
|
|
}): number {
|
|
const minMs = Math.max(normalizeNumber(opts.minMs) ?? 1, 1);
|
|
const defaultMs = resolveAgentTimeoutSeconds(opts.cfg) * 1000;
|
|
// Use a very large timeout value (30 days) to represent "no timeout"
|
|
// when explicitly set to 0. This avoids setTimeout issues with Infinity.
|
|
const NO_TIMEOUT_MS = 30 * 24 * 60 * 60 * 1000;
|
|
const overrideMs = normalizeNumber(opts.overrideMs);
|
|
if (overrideMs !== undefined) {
|
|
if (overrideMs === 0) {
|
|
return NO_TIMEOUT_MS;
|
|
}
|
|
if (overrideMs < 0) {
|
|
return defaultMs;
|
|
}
|
|
return Math.max(overrideMs, minMs);
|
|
}
|
|
const overrideSeconds = normalizeNumber(opts.overrideSeconds);
|
|
if (overrideSeconds !== undefined) {
|
|
if (overrideSeconds === 0) {
|
|
return NO_TIMEOUT_MS;
|
|
}
|
|
if (overrideSeconds < 0) {
|
|
return defaultMs;
|
|
}
|
|
return Math.max(overrideSeconds * 1000, minMs);
|
|
}
|
|
return Math.max(defaultMs, minMs);
|
|
}
|