From 1f292ffd683a124acbfe019396ec2cd200c117e8 Mon Sep 17 00:00:00 2001 From: unidesk-code-queue-runner Date: Sat, 23 May 2026 23:45:09 +0000 Subject: [PATCH] fix: align Code Agent heavy request timeouts --- cmd/hwlab-cloud-api/main.mjs | 2 +- cmd/hwlab-edge-proxy/main.mjs | 2 +- deploy/deploy.json | 6 +-- deploy/k8s/base/workloads.yaml | 6 +-- internal/cloud/code-agent-chat.mjs | 2 +- internal/cloud/codex-stdio-session.mjs | 4 +- internal/dev-entrypoint/http.mjs | 2 +- .../artifact-runtime-readiness-guard.test.mjs | 54 ++++++++++++++++--- scripts/dev-artifact-publish.mjs | 2 +- scripts/dev-cloud-workbench-smoke.test.mjs | 2 +- .../src/artifact-runtime-readiness-guard.mjs | 18 ++++--- scripts/src/deploy-contract-plan.mjs | 52 +++++++++++++++--- scripts/src/dev-cloud-workbench-smoke-lib.mjs | 6 +-- scripts/src/rpt004-mvp-e2e-harness.mjs | 2 +- web/hwlab-cloud-web/app.mjs | 2 +- web/hwlab-cloud-web/scripts/check.mjs | 4 +- 16 files changed, 124 insertions(+), 42 deletions(-) diff --git a/cmd/hwlab-cloud-api/main.mjs b/cmd/hwlab-cloud-api/main.mjs index 7dcbd5a4..7cef2721 100644 --- a/cmd/hwlab-cloud-api/main.mjs +++ b/cmd/hwlab-cloud-api/main.mjs @@ -4,7 +4,7 @@ import { createCloudApiServer } from "../../internal/cloud/server.mjs"; const host = process.env.HWLAB_CLOUD_API_HOST || "0.0.0.0"; const port = Number.parseInt(process.env.HWLAB_CLOUD_API_PORT || process.env.PORT || "6667", 10); const commitId = process.env.HWLAB_COMMIT_ID || process.env.HWLAB_GIT_SHA; -const codeAgentTimeoutMs = parseTimeout(process.env.HWLAB_CODE_AGENT_TIMEOUT_MS, 120000, { +const codeAgentTimeoutMs = parseTimeout(process.env.HWLAB_CODE_AGENT_TIMEOUT_MS, 150000, { min: 1000, max: 300000 }); diff --git a/cmd/hwlab-edge-proxy/main.mjs b/cmd/hwlab-edge-proxy/main.mjs index 95216da6..a9051043 100644 --- a/cmd/hwlab-edge-proxy/main.mjs +++ b/cmd/hwlab-edge-proxy/main.mjs @@ -10,7 +10,7 @@ import { createServer } from "node:http"; const serviceId = "hwlab-edge-proxy"; const upstream = process.env.HWLAB_EDGE_UPSTREAM || "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667"; -const proxyTimeoutMs = parseTimeout(process.env.HWLAB_EDGE_PROXY_TIMEOUT_MS, 150000, { +const proxyTimeoutMs = parseTimeout(process.env.HWLAB_EDGE_PROXY_TIMEOUT_MS, 180000, { min: 1000, max: 300000 }); diff --git a/deploy/deploy.json b/deploy/deploy.json index 7bd23db8..5ad4facb 100644 --- a/deploy/deploy.json +++ b/deploy/deploy.json @@ -204,7 +204,7 @@ "HWLAB_M3_PATCH_PANEL_URL": "http://hwlab-patch-panel.hwlab-dev.svc.cluster.local:7301", "HWLAB_CODE_AGENT_PROVIDER": "codex-stdio", "HWLAB_CODE_AGENT_MODEL": "gpt-5.5", - "HWLAB_CODE_AGENT_TIMEOUT_MS": "120000", + "HWLAB_CODE_AGENT_TIMEOUT_MS": "150000", "HWLAB_CODE_AGENT_OPENAI_BASE_URL": "http://172.26.26.227:17680/v1/responses", "HWLAB_CODE_AGENT_HWLAB_API_BASE_URL": "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667", "HWLAB_CODE_AGENT_CODEX_STDIO_ENABLED": "1", @@ -228,7 +228,7 @@ "env": { "HWLAB_ENVIRONMENT": "dev", "HWLAB_API_BASE_URL": "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667", - "HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS": "150000", + "HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS": "180000", "HWLAB_COMMIT_ID": "3df89fe", "HWLAB_IMAGE": "127.0.0.1:5000/hwlab/hwlab-cloud-web:3df89fe", "HWLAB_IMAGE_TAG": "3df89fe" @@ -366,7 +366,7 @@ "env": { "HWLAB_EDGE_LISTEN": "0.0.0.0:6667", "HWLAB_EDGE_UPSTREAM": "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667", - "HWLAB_EDGE_PROXY_TIMEOUT_MS": "150000" + "HWLAB_EDGE_PROXY_TIMEOUT_MS": "180000" } }, { diff --git a/deploy/k8s/base/workloads.yaml b/deploy/k8s/base/workloads.yaml index 3a5d1e36..85086a19 100644 --- a/deploy/k8s/base/workloads.yaml +++ b/deploy/k8s/base/workloads.yaml @@ -115,7 +115,7 @@ }, { "name": "HWLAB_CODE_AGENT_TIMEOUT_MS", - "value": "120000" + "value": "150000" }, { "name": "HWLAB_CODE_AGENT_OPENAI_BASE_URL", @@ -249,7 +249,7 @@ }, { "name": "HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS", - "value": "150000" + "value": "180000" }, { "name": "HWLAB_COMMIT_ID", @@ -826,7 +826,7 @@ }, { "name": "HWLAB_EDGE_PROXY_TIMEOUT_MS", - "value": "150000" + "value": "180000" } ], "readinessProbe": { diff --git a/internal/cloud/code-agent-chat.mjs b/internal/cloud/code-agent-chat.mjs index b9b5426e..0a4a6b53 100644 --- a/internal/cloud/code-agent-chat.mjs +++ b/internal/cloud/code-agent-chat.mjs @@ -38,7 +38,7 @@ import { runM3IoSkillCommand } from "../../skills/hwlab-agent-runtime/scripts/src/m3-io-skill-client.mjs"; -const DEFAULT_CODE_AGENT_TIMEOUT_MS = 120000; +const DEFAULT_CODE_AGENT_TIMEOUT_MS = 150000; const DEFAULT_CODEX_COMMAND = "codex"; const DEFAULT_MODEL = DEV_CODE_AGENT_PROVIDER_CONTRACT.model; const DEFAULT_PROJECT_ID = "prj_hwlab-cloud-workbench"; diff --git a/internal/cloud/codex-stdio-session.mjs b/internal/cloud/codex-stdio-session.mjs index c6c855ab..2ed67035 100644 --- a/internal/cloud/codex-stdio-session.mjs +++ b/internal/cloud/codex-stdio-session.mjs @@ -955,7 +955,7 @@ export function createCodexMcpJsonLineClient({ command = DEFAULT_CODEX_STDIO_COM return Array.isArray(result?.tools) ? result.tools.map((tool) => tool?.name).filter(Boolean) : []; } - async function callTool(name, args = {}, timeoutMs = 120000) { + async function callTool(name, args = {}, timeoutMs = 150000) { return request("tools/call", { name, arguments: args @@ -2365,7 +2365,7 @@ function hasEnvValue(env, name) { } function effectiveTimeout(timeoutMs) { - return Number.isInteger(timeoutMs) && timeoutMs > 0 ? timeoutMs : 120000; + return Number.isInteger(timeoutMs) && timeoutMs > 0 ? timeoutMs : 150000; } function dropEmpty(value) { diff --git a/internal/dev-entrypoint/http.mjs b/internal/dev-entrypoint/http.mjs index ff83684e..6b1c6213 100644 --- a/internal/dev-entrypoint/http.mjs +++ b/internal/dev-entrypoint/http.mjs @@ -3,7 +3,7 @@ import { createServer, request as httpRequest } from "node:http"; import { buildMetadataFromEnv } from "../build-metadata.mjs"; import { DEV_ENDPOINT, ENVIRONMENT_DEV } from "../protocol/index.mjs"; -const DEFAULT_PROXY_TIMEOUT_MS = 150000; +const DEFAULT_PROXY_TIMEOUT_MS = 180000; export function parsePort(value, fallback) { const parsed = Number.parseInt(value ?? "", 10); diff --git a/scripts/artifact-runtime-readiness-guard.test.mjs b/scripts/artifact-runtime-readiness-guard.test.mjs index 9465dec1..1052d690 100644 --- a/scripts/artifact-runtime-readiness-guard.test.mjs +++ b/scripts/artifact-runtime-readiness-guard.test.mjs @@ -258,9 +258,11 @@ test("compact commander summary exposes canPublish/canApply and dry-run devLiveC cloudWebDistFreshness: { status: "pass", mismatches: [] }, codeAgentTimeoutReadiness: { status: "pass", - frontendTimeoutMs: 35000, - backendTimeoutMs: 120000, + frontendTimeoutMs: 180000, + backendTimeoutMs: 150000, requiredFrontendTimeoutMs: 35000, + requiredBackendTimeoutMs: 150000, + transportWaitsForBackend: true, summary: "ready" }, runtimeReport: { @@ -290,9 +292,11 @@ test("compact summary blocks publish on stale Cloud Web source dist", () => { cloudWebDistFreshness: { status: "blocked", mismatches: ["index.html"] }, codeAgentTimeoutReadiness: { status: "pass", - frontendTimeoutMs: 35000, - backendTimeoutMs: 120000, + frontendTimeoutMs: 180000, + backendTimeoutMs: 150000, requiredFrontendTimeoutMs: 35000, + requiredBackendTimeoutMs: 150000, + transportWaitsForBackend: true, summary: "ready" }, runtimeReport: { @@ -322,8 +326,10 @@ test("compact summary separates Code Agent timeout and durable runtime blockers codeAgentTimeoutReadiness: { status: "blocked", frontendTimeoutMs: 4500, - backendTimeoutMs: 120000, + backendTimeoutMs: 150000, requiredFrontendTimeoutMs: 35000, + requiredBackendTimeoutMs: 150000, + transportWaitsForBackend: false, summary: "Code Agent browser timeout is too short." }, runtimeReport: { @@ -357,6 +363,38 @@ test("compact summary separates Code Agent timeout and durable runtime blockers assert.ok(summary.blockedReasons.some((item) => item.id === "runtime-durable:runtime_durable_adapter_query_blocked")); }); +test("compact summary blocks publish when Code Agent transport timeout does not exceed backend timeout", () => { + const result = readiness(); + const summary = buildCommanderReadinessSummary({ + readiness: result, + targetRef: "origin/main", + generatedFromCommit: targetSha, + explicitLive: false, + cloudWebDistFreshness: { status: "pass", mismatches: [] }, + codeAgentTimeoutReadiness: { + status: "blocked", + frontendTimeoutMs: 150000, + backendTimeoutMs: 150000, + requiredFrontendTimeoutMs: 35000, + requiredBackendTimeoutMs: 150000, + transportWaitsForBackend: false, + summary: "Code Agent timeout layering is unsafe." + }, + runtimeReport: { + runtimeIdentity: { + ready: true, + runtime: { durable: true, ready: true }, + readiness: { ready: true, durability: { ready: true } } + } + } + }); + + assert.equal(summary.canPublish, false); + assert.equal(summary.canApply, false); + assert.equal(summary.codeAgentTimeoutReadiness.transportWaitsForBackend, false); + assert.ok(summary.blockedReasons.some((item) => item.id === "code-agent-timeout-readiness")); +}); + test("compact summary keeps provider-secret blockers out of publish but blocks apply", () => { const result = readiness(); const summary = buildCommanderReadinessSummary({ @@ -367,9 +405,11 @@ test("compact summary keeps provider-secret blockers out of publish but blocks a cloudWebDistFreshness: { status: "pass", mismatches: [] }, codeAgentTimeoutReadiness: { status: "pass", - frontendTimeoutMs: 35000, - backendTimeoutMs: 120000, + frontendTimeoutMs: 180000, + backendTimeoutMs: 150000, requiredFrontendTimeoutMs: 35000, + requiredBackendTimeoutMs: 150000, + transportWaitsForBackend: true, summary: "ready" }, edgeReport: { diff --git a/scripts/dev-artifact-publish.mjs b/scripts/dev-artifact-publish.mjs index 98f18a91..f2b9d5d5 100644 --- a/scripts/dev-artifact-publish.mjs +++ b/scripts/dev-artifact-publish.mjs @@ -314,7 +314,7 @@ const runtimeKind = process.env.HWLAB_ARTIFACT_KIND || "health-placeholder"; const entrypoint = process.env.HWLAB_SERVICE_ENTRYPOINT || ""; const port = Number.parseInt(process.env.PORT || process.env.HWLAB_PORT || "8080", 10); const cloudApiBaseUrl = process.env.HWLAB_API_BASE_URL || ""; -const cloudApiProxyTimeoutMs = parseTimeout(process.env.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS, 150000, { +const cloudApiProxyTimeoutMs = parseTimeout(process.env.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS, 180000, { min: 1000, max: 300000 }); diff --git a/scripts/dev-cloud-workbench-smoke.test.mjs b/scripts/dev-cloud-workbench-smoke.test.mjs index 035092db..cbbe1a1c 100644 --- a/scripts/dev-cloud-workbench-smoke.test.mjs +++ b/scripts/dev-cloud-workbench-smoke.test.mjs @@ -89,7 +89,7 @@ test("source/default workbench report cannot claim DEV-LIVE and documents the co }); test("Code Agent browser failure classifier emits Chinese timeout/provider/browser categories with traceId", () => { - const timeout = classifyCodeAgentBrowserFailure(new Error("request timed out after 150000ms"), { traceId: "trc_timeout" }); + const timeout = classifyCodeAgentBrowserFailure(new Error("request timed out after 180000ms"), { traceId: "trc_timeout" }); assert.equal(timeout.category, "timeout"); assert.match(timeout.chineseSummary, /超时/u); assert.match(timeout.chineseSummary, /trc_timeout/u); diff --git a/scripts/src/artifact-runtime-readiness-guard.mjs b/scripts/src/artifact-runtime-readiness-guard.mjs index 03f26159..84e32942 100644 --- a/scripts/src/artifact-runtime-readiness-guard.mjs +++ b/scripts/src/artifact-runtime-readiness-guard.mjs @@ -21,7 +21,8 @@ const expectedBlockedGuardCommand = `${guardCommand} --expect-blocked`; const digestPattern = /^sha256:[a-f0-9]{64}$/u; const commitPattern = /^[a-f0-9]{7,40}$/u; const minCodeAgentFrontendTimeoutMs = 35000; -const defaultCodeAgentBackendTimeoutMs = 120000; +const defaultCodeAgentBackendTimeoutMs = 150000; +const minCodeAgentBackendTimeoutMs = 150000; export function normalizeCommit(value) { if (typeof value !== "string") return "unknown"; @@ -388,6 +389,8 @@ export function buildCommanderReadinessSummary({ frontendTimeoutMs: null, backendTimeoutMs: defaultCodeAgentBackendTimeoutMs, requiredFrontendTimeoutMs: minCodeAgentFrontendTimeoutMs, + requiredBackendTimeoutMs: minCodeAgentBackendTimeoutMs, + transportWaitsForBackend: false, summary: "Code Agent timeout readiness was not inspected." }; const runtimeDurable = classifyRuntimeDurableBlocker(runtimeReport, readiness.runtime); @@ -920,15 +923,18 @@ async function inspectCodeAgentTimeoutReadiness(repoRoot) { const frontendTimeoutMs = numericSourceConstant(appSource, "DEFAULT_CODE_AGENT_TIMEOUT_MS"); const backendTimeoutMs = numericSourceConstant(chatSource, "DEFAULT_CODE_AGENT_TIMEOUT_MS"); const frontendReady = Number.isInteger(frontendTimeoutMs) && frontendTimeoutMs >= minCodeAgentFrontendTimeoutMs; - const backendReady = Number.isInteger(backendTimeoutMs) && backendTimeoutMs >= minCodeAgentFrontendTimeoutMs; + const backendReady = Number.isInteger(backendTimeoutMs) && backendTimeoutMs >= minCodeAgentBackendTimeoutMs; + const transportWaitsForBackend = Number.isInteger(frontendTimeoutMs) && Number.isInteger(backendTimeoutMs) && frontendTimeoutMs > backendTimeoutMs; return { - status: frontendReady && backendReady ? "pass" : "blocked", + status: frontendReady && backendReady && transportWaitsForBackend ? "pass" : "blocked", frontendTimeoutMs, backendTimeoutMs, requiredFrontendTimeoutMs: minCodeAgentFrontendTimeoutMs, - summary: frontendReady && backendReady - ? "Code Agent browser/backend timeout budget is ready for provider latency." - : "Code Agent browser timeout is too short for provider-backed replies; long requests can be misclassified as failed." + requiredBackendTimeoutMs: minCodeAgentBackendTimeoutMs, + transportWaitsForBackend, + summary: frontendReady && backendReady && transportWaitsForBackend + ? "Code Agent browser/backend timeout budget is ready: browser transport waits for the structured backend result." + : "Code Agent timeout layering is unsafe; long requests can be misclassified before cloud-api returns a structured result." }; } diff --git a/scripts/src/deploy-contract-plan.mjs b/scripts/src/deploy-contract-plan.mjs index d80c4a00..d61f921e 100644 --- a/scripts/src/deploy-contract-plan.mjs +++ b/scripts/src/deploy-contract-plan.mjs @@ -13,6 +13,8 @@ import { const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const expectedPorts = Object.freeze({ frontend: 16666, api: 16667 }); +const codeAgentBackendTimeoutMs = "150000"; +const codeAgentTransportTimeoutMs = "180000"; const legacyPublicPorts = new Set([6666, 6667]); const duplicateMappingAllowed = new Set(["hwlab-box-simu", "hwlab-gateway-simu"]); const tomlBareKeyPattern = /^[A-Za-z0-9_-]+$/u; @@ -232,7 +234,7 @@ function validateSource(ctx, manifest) { expectEqual(ctx, cloudApi?.env?.HWLAB_PUBLIC_ENDPOINT, endpoints.api?.url, "$.services.hwlab-cloud-api.env.HWLAB_PUBLIC_ENDPOINT", "cloud API public endpoint env"); validateCloudApiDbSource(ctx, cloudApi?.env ?? {}); validateCloudApiCodeAgentSource(ctx, cloudApi?.env ?? {}); - validateCodeAgentProxyTimeoutSource(ctx, { cloudWeb, edgeProxy }); + validateCodeAgentProxyTimeoutSource(ctx, { cloudApi, cloudWeb, edgeProxy }); validateM3PatchPanelSource(ctx, patchPanel); expectEqual(ctx, cli?.env?.HWLAB_CLI_ENDPOINT, endpoints.api?.url, "$.services.hwlab-cli.env.HWLAB_CLI_ENDPOINT", "CLI endpoint env"); expectEqual(ctx, tunnelClient?.env?.HWLAB_FRP_PUBLIC_PORT, String(expectedPorts.api), "$.services.hwlab-tunnel-client.env.HWLAB_FRP_PUBLIC_PORT", "tunnel API public port env"); @@ -377,7 +379,7 @@ function validateCloudApiCodeAgentSource(ctx, env) { const contract = DEV_CODE_AGENT_PROVIDER_CONTRACT; expectEqual(ctx, env.HWLAB_CODE_AGENT_PROVIDER, contract.codeAgentProvider, "$.services.hwlab-cloud-api.env.HWLAB_CODE_AGENT_PROVIDER", "cloud API Code Agent provider"); expectEqual(ctx, env.HWLAB_CODE_AGENT_MODEL, contract.model, "$.services.hwlab-cloud-api.env.HWLAB_CODE_AGENT_MODEL", "cloud API Code Agent model"); - expectEqual(ctx, env.HWLAB_CODE_AGENT_TIMEOUT_MS, "120000", "$.services.hwlab-cloud-api.env.HWLAB_CODE_AGENT_TIMEOUT_MS", "cloud API Code Agent hard timeout budget"); + expectEqual(ctx, env.HWLAB_CODE_AGENT_TIMEOUT_MS, codeAgentBackendTimeoutMs, "$.services.hwlab-cloud-api.env.HWLAB_CODE_AGENT_TIMEOUT_MS", "cloud API Code Agent hard timeout budget"); expectEqual( ctx, env.HWLAB_CODE_AGENT_OPENAI_BASE_URL, @@ -410,9 +412,26 @@ function validateCloudApiCodeAgentSource(ctx, env) { expectEqual(ctx, env.OPENAI_API_KEY, codeAgentSecretRefPlaceholder(), "$.services.hwlab-cloud-api.env.OPENAI_API_KEY", "cloud API Code Agent OpenAI Secret reference"); } -function validateCodeAgentProxyTimeoutSource(ctx, { cloudWeb, edgeProxy }) { - expectEqual(ctx, cloudWeb?.env?.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS, "150000", "$.services.hwlab-cloud-web.env.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS", "Cloud Web same-origin Code Agent proxy timeout budget"); - expectEqual(ctx, edgeProxy?.env?.HWLAB_EDGE_PROXY_TIMEOUT_MS, "150000", "$.services.hwlab-edge-proxy.env.HWLAB_EDGE_PROXY_TIMEOUT_MS", "edge proxy Code Agent timeout budget"); +function validateCodeAgentProxyTimeoutSource(ctx, { cloudApi, cloudWeb, edgeProxy }) { + expectEqual(ctx, cloudWeb?.env?.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS, codeAgentTransportTimeoutMs, "$.services.hwlab-cloud-web.env.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS", "Cloud Web same-origin Code Agent proxy timeout budget"); + expectEqual(ctx, edgeProxy?.env?.HWLAB_EDGE_PROXY_TIMEOUT_MS, codeAgentTransportTimeoutMs, "$.services.hwlab-edge-proxy.env.HWLAB_EDGE_PROXY_TIMEOUT_MS", "edge proxy Code Agent timeout budget"); + const backendTimeoutMs = cloudApi?.env?.HWLAB_CODE_AGENT_TIMEOUT_MS; + expect( + ctx, + Number.parseInt(cloudWeb?.env?.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS ?? "0", 10) > Number.parseInt(backendTimeoutMs ?? "0", 10), + "code_agent_timeout_layering", + "$.services.hwlab-cloud-web.env.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS", + "Cloud Web proxy must wait longer than cloud-api so structured Code Agent timeout wins", + { expected: `>${backendTimeoutMs}`, actual: cloudWeb?.env?.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS } + ); + expect( + ctx, + Number.parseInt(edgeProxy?.env?.HWLAB_EDGE_PROXY_TIMEOUT_MS ?? "0", 10) > Number.parseInt(backendTimeoutMs ?? "0", 10), + "code_agent_timeout_layering", + "$.services.hwlab-edge-proxy.env.HWLAB_EDGE_PROXY_TIMEOUT_MS", + "Edge proxy must wait longer than cloud-api so structured Code Agent timeout wins", + { expected: `>${backendTimeoutMs}`, actual: edgeProxy?.env?.HWLAB_EDGE_PROXY_TIMEOUT_MS } + ); } function renderPlan(source) { @@ -553,7 +572,7 @@ function validateCloudApiCodeAgentArtifacts(ctx, workloads) { const env = new Map((container?.env ?? []).map((entry) => [entry.name, entry])); expectEqual(ctx, env.get("HWLAB_CODE_AGENT_PROVIDER")?.value, contract.codeAgentProvider, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_PROVIDER", "cloud API workload Code Agent provider"); expectEqual(ctx, env.get("HWLAB_CODE_AGENT_MODEL")?.value, contract.model, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_MODEL", "cloud API workload Code Agent model"); - expectEqual(ctx, env.get("HWLAB_CODE_AGENT_TIMEOUT_MS")?.value, "120000", "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_TIMEOUT_MS", "cloud API workload Code Agent hard timeout budget"); + expectEqual(ctx, env.get("HWLAB_CODE_AGENT_TIMEOUT_MS")?.value, codeAgentBackendTimeoutMs, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_TIMEOUT_MS", "cloud API workload Code Agent hard timeout budget"); expectEqual( ctx, env.get("HWLAB_CODE_AGENT_OPENAI_BASE_URL")?.value, @@ -591,8 +610,25 @@ function validateCodeAgentProxyTimeoutArtifacts(ctx, workloads) { const workloadItems = mapByServiceId(listItems(workloads)); const cloudWebEnv = new Map((workloadItems.get("hwlab-cloud-web")?.spec?.template?.spec?.containers?.[0]?.env ?? []).map((entry) => [entry.name, entry.value])); const edgeProxyEnv = new Map((workloadItems.get("hwlab-edge-proxy")?.spec?.template?.spec?.containers?.[0]?.env ?? []).map((entry) => [entry.name, entry.value])); - expectEqual(ctx, cloudWebEnv.get("HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS"), "150000", "deploy/k8s/base/workloads.yaml.hwlab-cloud-web.env.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS", "Cloud Web workload same-origin Code Agent proxy timeout budget"); - expectEqual(ctx, edgeProxyEnv.get("HWLAB_EDGE_PROXY_TIMEOUT_MS"), "150000", "deploy/k8s/base/workloads.yaml.hwlab-edge-proxy.env.HWLAB_EDGE_PROXY_TIMEOUT_MS", "edge proxy workload Code Agent timeout budget"); + const cloudApiEnv = new Map((workloadItems.get("hwlab-cloud-api")?.spec?.template?.spec?.containers?.[0]?.env ?? []).map((entry) => [entry.name, entry.value])); + expectEqual(ctx, cloudWebEnv.get("HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS"), codeAgentTransportTimeoutMs, "deploy/k8s/base/workloads.yaml.hwlab-cloud-web.env.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS", "Cloud Web workload same-origin Code Agent proxy timeout budget"); + expectEqual(ctx, edgeProxyEnv.get("HWLAB_EDGE_PROXY_TIMEOUT_MS"), codeAgentTransportTimeoutMs, "deploy/k8s/base/workloads.yaml.hwlab-edge-proxy.env.HWLAB_EDGE_PROXY_TIMEOUT_MS", "edge proxy workload Code Agent timeout budget"); + expect( + ctx, + Number.parseInt(cloudWebEnv.get("HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS") ?? "0", 10) > Number.parseInt(cloudApiEnv.get("HWLAB_CODE_AGENT_TIMEOUT_MS") ?? "0", 10), + "code_agent_timeout_layering", + "deploy/k8s/base/workloads.yaml.hwlab-cloud-web.env.HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS", + "Cloud Web workload proxy timeout must exceed cloud-api Code Agent timeout", + { expected: `>${cloudApiEnv.get("HWLAB_CODE_AGENT_TIMEOUT_MS")}`, actual: cloudWebEnv.get("HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS") } + ); + expect( + ctx, + Number.parseInt(edgeProxyEnv.get("HWLAB_EDGE_PROXY_TIMEOUT_MS") ?? "0", 10) > Number.parseInt(cloudApiEnv.get("HWLAB_CODE_AGENT_TIMEOUT_MS") ?? "0", 10), + "code_agent_timeout_layering", + "deploy/k8s/base/workloads.yaml.hwlab-edge-proxy.env.HWLAB_EDGE_PROXY_TIMEOUT_MS", + "Edge workload proxy timeout must exceed cloud-api Code Agent timeout", + { expected: `>${cloudApiEnv.get("HWLAB_CODE_AGENT_TIMEOUT_MS")}`, actual: edgeProxyEnv.get("HWLAB_EDGE_PROXY_TIMEOUT_MS") } + ); } async function buildPlan() { diff --git a/scripts/src/dev-cloud-workbench-smoke-lib.mjs b/scripts/src/dev-cloud-workbench-smoke-lib.mjs index 1c96d29f..8d96da26 100644 --- a/scripts/src/dev-cloud-workbench-smoke-lib.mjs +++ b/scripts/src/dev-cloud-workbench-smoke-lib.mjs @@ -20,7 +20,7 @@ const webRoot = path.join(repoRoot, "web/hwlab-cloud-web"); const defaultLiveUrl = "http://74.48.78.17:16666/"; const helpOwner = "codex_1779444232735_1"; const legacyFailureWindowMs = 4500; -const codeAgentLongTimeoutMs = 150000; +const codeAgentLongTimeoutMs = 180000; const localAgentFixtureDelayMs = 5200; const codeAgentE2ePrompts = Object.freeze([ { @@ -2565,7 +2565,7 @@ function hasCodeAgentLongTimeoutContract(files) { const app = files.app; return ( /DEFAULT_API_TIMEOUT_MS\s*=\s*4500/u.test(app) && - /DEFAULT_CODE_AGENT_TIMEOUT_MS\s*=\s*150000/u.test(app) && + /DEFAULT_CODE_AGENT_TIMEOUT_MS\s*=\s*180000/u.test(app) && /async function sendAgentMessage[\s\S]*?fetchJson\("\/v1\/agent\/chat"[\s\S]*?timeoutMs:\s*CODE_AGENT_TIMEOUT_MS/u.test(app) && /timeoutMs\s*=\s*API_TIMEOUT_MS/u.test(functionBody(app, "fetchJson")) && /timeoutName\s*=\s*path/u.test(functionBody(app, "fetchJson")) && @@ -2619,7 +2619,7 @@ function hasCodeAgentConversationUxStates({ app, styles }) { /字段缺失:证据不足/u.test(app) && /当前不能执行/u.test(app) && /DEFAULT_API_TIMEOUT_MS\s*=\s*4500/u.test(app) && - /DEFAULT_CODE_AGENT_TIMEOUT_MS\s*=\s*150000/u.test(app) && + /DEFAULT_CODE_AGENT_TIMEOUT_MS\s*=\s*180000/u.test(app) && /timeoutMs:\s*CODE_AGENT_TIMEOUT_MS/u.test(app) && /HWLAB_CLOUD_WEB_CONFIG/u.test(app) && /失败时会保留输入供重试/u.test(app) && diff --git a/scripts/src/rpt004-mvp-e2e-harness.mjs b/scripts/src/rpt004-mvp-e2e-harness.mjs index e43dcc6b..70713555 100644 --- a/scripts/src/rpt004-mvp-e2e-harness.mjs +++ b/scripts/src/rpt004-mvp-e2e-harness.mjs @@ -1639,7 +1639,7 @@ function parseTimeout(value) { if (!Number.isInteger(parsed) || parsed <= 0) { throw new Error("--timeout-ms must be a positive integer"); } - return Math.min(parsed, 120000); + return Math.min(parsed, 180000); } function worstStatus(statuses) { diff --git a/web/hwlab-cloud-web/app.mjs b/web/hwlab-cloud-web/app.mjs index 0e7db849..1a7d73b1 100644 --- a/web/hwlab-cloud-web/app.mjs +++ b/web/hwlab-cloud-web/app.mjs @@ -20,7 +20,7 @@ import { } from "./code-agent-m3-evidence.mjs"; const DEFAULT_API_TIMEOUT_MS = 4500; -const DEFAULT_CODE_AGENT_TIMEOUT_MS = 150000; +const DEFAULT_CODE_AGENT_TIMEOUT_MS = 180000; const API_TIMEOUT_MS = resolveTimeoutMs("apiTimeoutMs", DEFAULT_API_TIMEOUT_MS, { min: 500, max: 30000 }); const CODE_AGENT_TIMEOUT_MS = resolveTimeoutMs("codeAgentTimeoutMs", DEFAULT_CODE_AGENT_TIMEOUT_MS, { min: 50, max: 300000 }); const rpcReadMethods = Object.freeze([ diff --git a/web/hwlab-cloud-web/scripts/check.mjs b/web/hwlab-cloud-web/scripts/check.mjs index 0a524da8..de50ed84 100644 --- a/web/hwlab-cloud-web/scripts/check.mjs +++ b/web/hwlab-cloud-web/scripts/check.mjs @@ -383,7 +383,7 @@ assert.match(app, /renderRecords\(state\.liveSurface\)/); assert.match(app, /traceId: error\.traceId \|\| traceId/); assert.match(app, /error\.traceId = (?:traceId|response\.data\?\.traceId \|\| traceId)/); assert.match(app, /DEFAULT_API_TIMEOUT_MS\s*=\s*4500/); -assert.match(app, /DEFAULT_CODE_AGENT_TIMEOUT_MS\s*=\s*150000/); +assert.match(app, /DEFAULT_CODE_AGENT_TIMEOUT_MS\s*=\s*180000/); assert.match(app, /async function sendAgentMessage[\s\S]*?timeoutMs:\s*CODE_AGENT_TIMEOUT_MS/); assert.match(functionBody(app, "fetchJson"), /timeoutMs\s*=\s*API_TIMEOUT_MS/); for (const hardwareTerm of [ @@ -878,7 +878,7 @@ assert.match(app, /createdAt/); assert.match(app, /updatedAt/); assert.match(app, /error\?\.message/); assert.match(app, /DEFAULT_API_TIMEOUT_MS\s*=\s*4500/); -assert.match(app, /DEFAULT_CODE_AGENT_TIMEOUT_MS\s*=\s*150000/); +assert.match(app, /DEFAULT_CODE_AGENT_TIMEOUT_MS\s*=\s*180000/); assert.match(app, /CODE_AGENT_TIMEOUT_MS/); assert.match(app, /timeoutMs:\s*CODE_AGENT_TIMEOUT_MS/); assert.match(app, /HWLAB_CLOUD_WEB_CONFIG/);