mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 07:40:21 +00:00
Fix subagent announce failover race (always emit lifecycle end + treat timeout=0 as no-timeout) (#6621)
* 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)
This commit is contained in:
@@ -172,24 +172,27 @@ export async function runAgentTurnWithFallback(params: {
|
||||
},
|
||||
});
|
||||
const cliSessionId = getCliSessionId(params.getActiveSessionEntry(), provider);
|
||||
return runCliAgent({
|
||||
sessionId: params.followupRun.run.sessionId,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionFile: params.followupRun.run.sessionFile,
|
||||
workspaceDir: params.followupRun.run.workspaceDir,
|
||||
config: params.followupRun.run.config,
|
||||
prompt: params.commandBody,
|
||||
provider,
|
||||
model,
|
||||
thinkLevel: params.followupRun.run.thinkLevel,
|
||||
timeoutMs: params.followupRun.run.timeoutMs,
|
||||
runId,
|
||||
extraSystemPrompt: params.followupRun.run.extraSystemPrompt,
|
||||
ownerNumbers: params.followupRun.run.ownerNumbers,
|
||||
cliSessionId,
|
||||
images: params.opts?.images,
|
||||
})
|
||||
.then((result) => {
|
||||
return (async () => {
|
||||
let lifecycleTerminalEmitted = false;
|
||||
try {
|
||||
const result = await runCliAgent({
|
||||
sessionId: params.followupRun.run.sessionId,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionFile: params.followupRun.run.sessionFile,
|
||||
workspaceDir: params.followupRun.run.workspaceDir,
|
||||
config: params.followupRun.run.config,
|
||||
prompt: params.commandBody,
|
||||
provider,
|
||||
model,
|
||||
thinkLevel: params.followupRun.run.thinkLevel,
|
||||
timeoutMs: params.followupRun.run.timeoutMs,
|
||||
runId,
|
||||
extraSystemPrompt: params.followupRun.run.extraSystemPrompt,
|
||||
ownerNumbers: params.followupRun.run.ownerNumbers,
|
||||
cliSessionId,
|
||||
images: params.opts?.images,
|
||||
});
|
||||
|
||||
// CLI backends don't emit streaming assistant events, so we need to
|
||||
// emit one with the final text so server-chat can populate its buffer
|
||||
// and send the response to TUI/WebSocket clients.
|
||||
@@ -201,6 +204,7 @@ export async function runAgentTurnWithFallback(params: {
|
||||
data: { text: cliText },
|
||||
});
|
||||
}
|
||||
|
||||
emitAgentEvent({
|
||||
runId,
|
||||
stream: "lifecycle",
|
||||
@@ -210,9 +214,10 @@ export async function runAgentTurnWithFallback(params: {
|
||||
endedAt: Date.now(),
|
||||
},
|
||||
});
|
||||
lifecycleTerminalEmitted = true;
|
||||
|
||||
return result;
|
||||
})
|
||||
.catch((err) => {
|
||||
} catch (err) {
|
||||
emitAgentEvent({
|
||||
runId,
|
||||
stream: "lifecycle",
|
||||
@@ -220,11 +225,28 @@ export async function runAgentTurnWithFallback(params: {
|
||||
phase: "error",
|
||||
startedAt,
|
||||
endedAt: Date.now(),
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
error: String(err),
|
||||
},
|
||||
});
|
||||
lifecycleTerminalEmitted = true;
|
||||
throw err;
|
||||
});
|
||||
} finally {
|
||||
// Defensive backstop: never let a CLI run complete without a terminal
|
||||
// lifecycle event, otherwise downstream consumers can hang.
|
||||
if (!lifecycleTerminalEmitted) {
|
||||
emitAgentEvent({
|
||||
runId,
|
||||
stream: "lifecycle",
|
||||
data: {
|
||||
phase: "error",
|
||||
startedAt,
|
||||
endedAt: Date.now(),
|
||||
error: "CLI run completed without lifecycle terminal event",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
const authProfileId =
|
||||
provider === params.followupRun.run.provider
|
||||
|
||||
Reference in New Issue
Block a user