mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(exec): skip default timeout for background sessions
This commit is contained in:
@@ -33,6 +33,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Telegram/Discord extensions: propagate trusted `mediaLocalRoots` through extension outbound `sendMedia` options so extension direct-send media paths honor agent-scoped local-media allowlists. (#20029, #21903, #23227)
|
||||
- Exec/Background: stop applying the default exec timeout to background sessions (`background: true` or explicit `yieldMs`) when no explicit timeout is set, so long-running background jobs are no longer terminated at the default timeout boundary. (#23303)
|
||||
- Plugins/Media sandbox: propagate trusted `mediaLocalRoots` through plugin action dispatch (including Discord/Telegram action adapters) so plugin send paths enforce the same agent-scoped local-media sandbox roots as core outbound sends. (#20258, #22718)
|
||||
- Agents/Workspace guard: map sandbox container-workdir file-tool paths (for example `/workspace/...` and `file:///workspace/...`) to host workspace roots before workspace-only validation, preventing false `Path escapes sandbox root` rejections for sandbox file tools. (#9560)
|
||||
- Gateway/Exec approvals: expire approval requests immediately when no approval-capable gateway clients are connected and no forwarding targets are available, avoiding delayed approvals after restarts/offline approver windows. (#22144)
|
||||
|
||||
@@ -267,7 +267,7 @@ export async function runExecProcess(opts: {
|
||||
notifyOnExitEmptySuccess?: boolean;
|
||||
scopeKey?: string;
|
||||
sessionKey?: string;
|
||||
timeoutSec: number;
|
||||
timeoutSec: number | null;
|
||||
onUpdate?: (partialResult: AgentToolResult<ExecToolDetails>) => void;
|
||||
}): Promise<ExecProcessHandle> {
|
||||
const startedAt = Date.now();
|
||||
@@ -504,7 +504,9 @@ export async function runExecProcess(opts: {
|
||||
}
|
||||
const reason =
|
||||
exit.reason === "overall-timeout"
|
||||
? `Command timed out after ${opts.timeoutSec} seconds`
|
||||
? typeof opts.timeoutSec === "number" && opts.timeoutSec > 0
|
||||
? `Command timed out after ${opts.timeoutSec} seconds`
|
||||
: "Command timed out"
|
||||
: exit.reason === "no-output-timeout"
|
||||
? "Command timed out waiting for output"
|
||||
: exit.exitSignal != null
|
||||
|
||||
@@ -142,6 +142,35 @@ test("background exec still times out after tool signal abort", async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("background exec without explicit timeout ignores default timeout", async () => {
|
||||
const tool = createTestExecTool({
|
||||
allowBackground: true,
|
||||
backgroundMs: 0,
|
||||
timeoutSec: BACKGROUND_TIMEOUT_SEC,
|
||||
});
|
||||
const result = await tool.execute("toolcall", { command: BACKGROUND_HOLD_CMD, background: true });
|
||||
expect(result.details.status).toBe("running");
|
||||
const sessionId = (result.details as { sessionId: string }).sessionId;
|
||||
const waitMs = Math.max(ABORT_SETTLE_MS + 120, BACKGROUND_TIMEOUT_SEC * 1000 + 120);
|
||||
|
||||
const startedAt = Date.now();
|
||||
await expect
|
||||
.poll(
|
||||
() => {
|
||||
const running = getSession(sessionId);
|
||||
const finished = getFinishedSession(sessionId);
|
||||
return Date.now() - startedAt >= waitMs && !finished && running?.exited === false;
|
||||
},
|
||||
{
|
||||
timeout: waitMs + ABORT_WAIT_TIMEOUT_MS,
|
||||
interval: POLL_INTERVAL_MS,
|
||||
},
|
||||
)
|
||||
.toBe(true);
|
||||
|
||||
cleanupRunningSession(sessionId);
|
||||
});
|
||||
|
||||
test("yielded background exec is not killed when tool signal aborts", async () => {
|
||||
const tool = createTestExecTool({ allowBackground: true, backgroundMs: 10 });
|
||||
await expectBackgroundSessionSurvivesAbort({
|
||||
|
||||
@@ -442,8 +442,12 @@ export function createExecTool(
|
||||
execCommandOverride = gatewayResult.execCommandOverride;
|
||||
}
|
||||
|
||||
const effectiveTimeout =
|
||||
typeof params.timeout === "number" ? params.timeout : defaultTimeoutSec;
|
||||
const explicitTimeoutSec = typeof params.timeout === "number" ? params.timeout : null;
|
||||
const backgroundTimeoutBypass =
|
||||
allowBackground && explicitTimeoutSec === null && (backgroundRequested || yieldRequested);
|
||||
const effectiveTimeout = backgroundTimeoutBypass
|
||||
? null
|
||||
: (explicitTimeoutSec ?? defaultTimeoutSec);
|
||||
const getWarningText = () => (warnings.length ? `${warnings.join("\n")}\n\n` : "");
|
||||
const usePty = params.pty === true && !sandbox;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user