diff --git a/src/agent/local-agents.ts b/src/agent/local-agents.ts index fa6dd2e..f6de5ec 100644 --- a/src/agent/local-agents.ts +++ b/src/agent/local-agents.ts @@ -243,6 +243,11 @@ function agentFailureOutput(output: string): string | null { return null; } +function envTimeoutMs(name: string, fallback: number): number { + const parsed = Number(process.env[name]); + return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; +} + /** * 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). @@ -256,7 +261,7 @@ export function runLocalAgent( const t0 = Date.now(); if (!spec) return Promise.resolve({ ok: false, latencyMs: 0, output: '', error: `unknown agent: ${id}` }); const args = spec.oneShot(prompt, opts.model); - const timeoutMs = opts.timeoutMs ?? 120000; + const timeoutMs = opts.timeoutMs ?? envTimeoutMs('T3MP3ST_LOCAL_AGENT_TIMEOUT_MS', 600000); const maxChars = opts.maxChars ?? 4000; // child env: provider keys stripped + HOME pinned to the real agent home (see childEnv). const env = childEnv(); @@ -306,7 +311,7 @@ export function localAgentChat(id: string, prompt: string, opts: { model?: strin // child env: provider keys stripped + HOME pinned to the real agent home (see childEnv). const env = childEnv(); const model = opts.model && opts.model !== 'codex-default' && opts.model !== id ? opts.model : undefined; - const timeoutMs = opts.timeoutMs ?? 240000; + const timeoutMs = opts.timeoutMs ?? envTimeoutMs('T3MP3ST_LOCAL_AGENT_TIMEOUT_MS', 600000); let args: string[]; let viaStdin = true; diff --git a/src/index.ts b/src/index.ts index 665817e..e365562 100755 --- a/src/index.ts +++ b/src/index.ts @@ -208,6 +208,7 @@ import type { TempestConfig, LLMConfig, RuntimeHooks, + LLMProvider, OperatorArchetype, CommandEvents, Finding, @@ -325,6 +326,7 @@ export class TempestCommand extends EventEmitter { private tickInterval: NodeJS.Timeout | null = null; private tickCount: number = 0; private hooks: RuntimeHooks; + private readonly taskTimeoutMs: number; /** * White-box source context (security-prioritized code excerpt), set by the @@ -359,6 +361,7 @@ export class TempestCommand extends EventEmitter { super(); this.name = config.name; this.hooks = config.hooks || {}; + this.taskTimeoutMs = TempestCommand.resolveTaskTimeoutMs(config.llm.provider); // Initialize LLM backbone this.llm = new LLMBackbone(config.llm); @@ -822,20 +825,19 @@ export class TempestCommand extends EventEmitter { * legitimately-slow-but-working LLM task (e.g. opus planning ~165s); it only fires * on truly-hung dispatches. Override via T3MP3ST_TASK_TIMEOUT_MS. */ - private readonly taskTimeoutMs: number = TempestCommand.resolveTaskTimeoutMs(); - /** * Resolve the dispatch timeout from the environment, falling back to the 5-minute * default. Guards against a non-numeric / non-positive override. */ - private static resolveTaskTimeoutMs(): number { + private static resolveTaskTimeoutMs(provider?: LLMProvider): number { const DEFAULT_TASK_TIMEOUT_MS = 300000; // 5 minutes — generous backstop, not a deadline + const LOCAL_AGENT_TASK_TIMEOUT_MS = 1800000; // local CLI agents can need multiple slow turns const raw = process.env.T3MP3ST_TASK_TIMEOUT_MS; if (raw != null && raw.trim() !== '') { const parsed = Number(raw); if (Number.isFinite(parsed) && parsed > 0) return parsed; } - return DEFAULT_TASK_TIMEOUT_MS; + return provider === 'local-agent' ? LOCAL_AGENT_TASK_TIMEOUT_MS : DEFAULT_TASK_TIMEOUT_MS; } /** Track whether we've seeded initial tasks for the current mission */