Skip to content

Commit 26bf8f0

Browse files
committed
fix(voice-call): cap CLI gateway timeouts
1 parent 3fbd243 commit 26bf8f0

2 files changed

Lines changed: 56 additions & 6 deletions

File tree

extensions/voice-call/src/cli.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime";
12
import { describe, expect, it } from "vitest";
23
import { testing } from "./cli.js";
34

@@ -35,3 +36,35 @@ describe("parseVoiceCallIntOption", () => {
3536
).toThrow("Invalid numeric value for --port: 65536");
3637
});
3738
});
39+
40+
describe("voice-call CLI timeout helpers", () => {
41+
it("caps gateway operation timeout grace", () => {
42+
expect(testing.resolveGatewayOperationTimeoutMs({ ringTimeoutMs: 10_000 } as never)).toBe(
43+
30_000,
44+
);
45+
expect(testing.resolveGatewayOperationTimeoutMs({ ringTimeoutMs: 60_000 } as never)).toBe(
46+
65_000,
47+
);
48+
expect(
49+
testing.resolveGatewayOperationTimeoutMs({ ringTimeoutMs: Number.MAX_SAFE_INTEGER } as never),
50+
).toBe(MAX_TIMER_TIMEOUT_MS);
51+
});
52+
53+
it("caps gateway continue timeout totals", () => {
54+
expect(testing.resolveGatewayContinueTimeoutMs({ transcriptTimeoutMs: 180_000 } as never)).toBe(
55+
220_000,
56+
);
57+
expect(
58+
testing.resolveGatewayContinueTimeoutMs({
59+
transcriptTimeoutMs: Number.MAX_SAFE_INTEGER,
60+
} as never),
61+
).toBe(MAX_TIMER_TIMEOUT_MS);
62+
});
63+
64+
it("caps gateway polling deadlines", () => {
65+
expect(testing.resolveVoiceCallDeadlineMs(5_000, 10_000)).toBe(15_000);
66+
expect(testing.resolveVoiceCallDeadlineMs(Number.MAX_SAFE_INTEGER, 10_000)).toBe(
67+
10_000 + MAX_TIMER_TIMEOUT_MS,
68+
);
69+
});
70+
});

extensions/voice-call/src/cli.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import { format } from "node:util";
55
import type { Command } from "commander";
66
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
77
import { callGatewayFromCli } from "openclaw/plugin-sdk/gateway-runtime";
8-
import { MAX_TCP_PORT, parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
8+
import {
9+
clampTimerTimeoutMs,
10+
MAX_TIMER_TIMEOUT_MS,
11+
MAX_TCP_PORT,
12+
parseStrictNonNegativeInteger,
13+
} from "openclaw/plugin-sdk/number-runtime";
914
import {
1015
isRecord,
1116
normalizeOptionalLowercaseString,
@@ -66,6 +71,9 @@ export const testing = {
6671
},
6772
isGatewayUnavailableForLocalFallback,
6873
parseVoiceCallIntOption,
74+
resolveGatewayContinueTimeoutMs,
75+
resolveGatewayOperationTimeoutMs,
76+
resolveVoiceCallDeadlineMs,
6977
};
7078

7179
function writeStdoutLine(...values: unknown[]): void {
@@ -128,17 +136,26 @@ async function callVoiceCallGateway(
128136
}
129137

130138
function resolveGatewayOperationTimeoutMs(config: VoiceCallConfig): number {
131-
return Math.max(VOICE_CALL_GATEWAY_OPERATION_TIMEOUT_MS, config.ringTimeoutMs + 5000);
139+
return Math.max(
140+
VOICE_CALL_GATEWAY_OPERATION_TIMEOUT_MS,
141+
clampTimerTimeoutMs(config.ringTimeoutMs + 5000) ?? 1,
142+
);
132143
}
133144

134145
function resolveGatewayContinueTimeoutMs(config: VoiceCallConfig): number {
135146
return (
136-
config.transcriptTimeoutMs +
137-
VOICE_CALL_GATEWAY_OPERATION_TIMEOUT_MS +
138-
VOICE_CALL_GATEWAY_TRANSCRIPT_BUFFER_MS
147+
clampTimerTimeoutMs(
148+
config.transcriptTimeoutMs +
149+
VOICE_CALL_GATEWAY_OPERATION_TIMEOUT_MS +
150+
VOICE_CALL_GATEWAY_TRANSCRIPT_BUFFER_MS,
151+
) ?? 1
139152
);
140153
}
141154

155+
function resolveVoiceCallDeadlineMs(timeoutMs: number, nowMs = Date.now()): number {
156+
return nowMs + (clampTimerTimeoutMs(timeoutMs) ?? MAX_TIMER_TIMEOUT_MS);
157+
}
158+
142159
function isUnknownGatewayMethod(err: unknown, method: VoiceCallGatewayMethod): boolean {
143160
return formatErrorMessage(err).includes(`unknown method: ${method}`);
144161
}
@@ -185,7 +202,7 @@ async function pollVoiceCallContinueGateway(params: {
185202
operationId: string;
186203
timeoutMs: number;
187204
}): Promise<unknown> {
188-
const deadlineMs = Date.now() + params.timeoutMs;
205+
const deadlineMs = resolveVoiceCallDeadlineMs(params.timeoutMs);
189206

190207
while (Date.now() <= deadlineMs) {
191208
const gateway = await callVoiceCallGateway(

0 commit comments

Comments
 (0)