From 2cbfb910f2767546b44d0741461209109d288805 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 30 May 2026 16:47:07 -0400 Subject: [PATCH] fix(commands): clamp gateway agent timeout --- src/commands/agent-via-gateway.test.ts | 7 +++++++ src/commands/agent-via-gateway.ts | 14 ++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/commands/agent-via-gateway.test.ts b/src/commands/agent-via-gateway.test.ts index 77eb7cf2ce08..af8e1b872176 100644 --- a/src/commands/agent-via-gateway.test.ts +++ b/src/commands/agent-via-gateway.test.ts @@ -5,6 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { loggingState } from "../logging/state.js"; import type { RuntimeEnv } from "../runtime.js"; +import { MAX_TIMER_TIMEOUT_MS } from "../shared/number-coercion.js"; import { agentCliCommand, agentViaGatewayTesting } from "./agent-via-gateway.js"; import type { agentCommand as AgentCommand } from "./agent.js"; @@ -245,6 +246,12 @@ describe("agentCliCommand", () => { }); }); + it("clamps oversized gateway timeout seconds", () => { + expect(agentViaGatewayTesting.resolveGatewayAgentTimeoutMs(Number.MAX_SAFE_INTEGER)).toBe( + MAX_TIMER_TIMEOUT_MS, + ); + }); + it("rejects partial gateway timeout values", async () => { await withTempStore(async () => { await expect( diff --git a/src/commands/agent-via-gateway.ts b/src/commands/agent-via-gateway.ts index ac22d2a4b4d2..2cd4837e239b 100644 --- a/src/commands/agent-via-gateway.ts +++ b/src/commands/agent-via-gateway.ts @@ -1,4 +1,5 @@ import { randomUUID } from "node:crypto"; +import { resolveTimerTimeoutMs } from "@openclaw/normalization-core/number-coercion"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload"; import { @@ -129,6 +130,7 @@ export const agentViaGatewayTesting = { agentSessionModulePromise = undefined; agentSessionModuleLoader = loader; }, + resolveGatewayAgentTimeoutMs, }; function protectJsonStdout(opts: Pick): void { @@ -150,6 +152,13 @@ function parseTimeoutSeconds(opts: { cfg: OpenClawConfig; timeout?: string }) { return raw; } +function resolveGatewayAgentTimeoutMs(timeoutSeconds: number): number { + if (timeoutSeconds === 0) { + return NO_GATEWAY_TIMEOUT_MS; + } + return resolveTimerTimeoutMs((timeoutSeconds + 30) * 1000, 10_000, 10_000); +} + function getGatewayDispatchConfig(): OpenClawConfig { // Scoped gateway turns need core agent/session/gateway fields only. The // running gateway owns plugin validation and plugin metadata freshness. @@ -580,10 +589,7 @@ async function agentViaGatewayCommand( } } const timeoutSeconds = parseTimeoutSeconds({ cfg, timeout: opts.timeout }); - const gatewayTimeoutMs = - timeoutSeconds === 0 - ? NO_GATEWAY_TIMEOUT_MS // no timeout (timer-safe max) - : Math.max(10_000, (timeoutSeconds + 30) * 1000); + const gatewayTimeoutMs = resolveGatewayAgentTimeoutMs(timeoutSeconds); const sessionKey = classifySessionKeyShape(explicitSessionKey) === "agent"