fix(coding-agent): configure HTTP idle timeout (#4759)
This commit is contained in:
55
packages/coding-agent/src/core/http-dispatcher.ts
Normal file
55
packages/coding-agent/src/core/http-dispatcher.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import * as undici from "undici";
|
||||
|
||||
export const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;
|
||||
|
||||
export const HTTP_IDLE_TIMEOUT_CHOICES = [
|
||||
{ label: "30 sec", timeoutMs: 30_000 },
|
||||
{ label: "1 min", timeoutMs: 60_000 },
|
||||
{ label: "2 min", timeoutMs: 120_000 },
|
||||
{ label: "5 min", timeoutMs: 300_000 },
|
||||
{ label: "disabled", timeoutMs: 0 },
|
||||
] as const;
|
||||
|
||||
export function parseHttpIdleTimeoutMs(value: unknown): number | undefined {
|
||||
if (typeof value === "string") {
|
||||
const trimmed = value.trim();
|
||||
if (trimmed.toLowerCase() === "disabled") {
|
||||
return 0;
|
||||
}
|
||||
if (trimmed.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return parseHttpIdleTimeoutMs(Number(trimmed));
|
||||
}
|
||||
|
||||
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
||||
return undefined;
|
||||
}
|
||||
return Math.floor(value);
|
||||
}
|
||||
|
||||
export function formatHttpIdleTimeoutMs(timeoutMs: number): string {
|
||||
const choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.timeoutMs === timeoutMs);
|
||||
if (choice) {
|
||||
return choice.label;
|
||||
}
|
||||
return `${timeoutMs / 1000} sec`;
|
||||
}
|
||||
|
||||
export function configureHttpDispatcher(timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS): void {
|
||||
const normalizedTimeoutMs = parseHttpIdleTimeoutMs(timeoutMs);
|
||||
if (normalizedTimeoutMs === undefined) {
|
||||
throw new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`);
|
||||
}
|
||||
undici.setGlobalDispatcher(
|
||||
new undici.EnvHttpProxyAgent({
|
||||
allowH2: false,
|
||||
bodyTimeout: normalizedTimeoutMs,
|
||||
headersTimeout: normalizedTimeoutMs,
|
||||
}),
|
||||
);
|
||||
// Keep fetch and the dispatcher on the same undici implementation. Node 26.0's
|
||||
// bundled fetch can otherwise consume compressed responses through npm undici's
|
||||
// dispatcher without decompressing them, causing response.json() failures.
|
||||
undici.install?.();
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { homedir } from "os";
|
||||
import { dirname, join } from "path";
|
||||
import lockfile from "proper-lockfile";
|
||||
import { CONFIG_DIR_NAME, getAgentDir } from "../config.ts";
|
||||
import { DEFAULT_HTTP_IDLE_TIMEOUT_MS, parseHttpIdleTimeoutMs } from "./http-dispatcher.ts";
|
||||
|
||||
export interface CompactionSettings {
|
||||
enabled?: boolean; // default: true
|
||||
@@ -110,6 +111,7 @@ export interface Settings {
|
||||
markdown?: MarkdownSettings;
|
||||
warnings?: WarningSettings;
|
||||
sessionDir?: string; // Custom session storage directory (same format as --session-dir CLI flag)
|
||||
httpIdleTimeoutMs?: number; // HTTP header/body idle timeout in milliseconds; 0 disables it
|
||||
}
|
||||
|
||||
/** Deep merge settings: project/overrides take precedence, nested objects merge recursively */
|
||||
@@ -726,6 +728,27 @@ export class SettingsManager {
|
||||
};
|
||||
}
|
||||
|
||||
getHttpIdleTimeoutMs(): number {
|
||||
const value = this.settings.httpIdleTimeoutMs;
|
||||
const timeoutMs = parseHttpIdleTimeoutMs(value);
|
||||
if (timeoutMs !== undefined) {
|
||||
return timeoutMs;
|
||||
}
|
||||
if (value !== undefined) {
|
||||
throw new Error(`Invalid httpIdleTimeoutMs setting: ${String(value)}`);
|
||||
}
|
||||
return DEFAULT_HTTP_IDLE_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
setHttpIdleTimeoutMs(timeoutMs: number): void {
|
||||
if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
|
||||
throw new Error(`Invalid httpIdleTimeoutMs setting: ${String(timeoutMs)}`);
|
||||
}
|
||||
this.globalSettings.httpIdleTimeoutMs = Math.floor(timeoutMs);
|
||||
this.markModified("httpIdleTimeoutMs");
|
||||
this.save();
|
||||
}
|
||||
|
||||
getProviderRetrySettings(): { timeoutMs?: number; maxRetries?: number; maxRetryDelayMs: number } {
|
||||
return {
|
||||
timeoutMs: this.settings.retry?.provider?.timeoutMs,
|
||||
|
||||
Reference in New Issue
Block a user