diff --git a/deploy/deploy.yaml b/deploy/deploy.yaml index 9f713457..2e473800 100644 --- a/deploy/deploy.yaml +++ b/deploy/deploy.yaml @@ -291,7 +291,8 @@ lanes: - skills/hwlab-agent-runtime/ env: HWLAB_METRICS_NAMESPACE: hwlab-v03 - HWLAB_OBSERVABILITY_CONFIG_REVISION: PJ2026-01060505-20260617-rollout-scope + HWLAB_OBSERVABILITY_CONFIG_REVISION: PJ2026-01060505-20260617-turn-status-budget + HWLAB_CODE_AGENT_TURN_STATUS_REFRESH_TIMEOUT_MS: "2500" observable: true hwlab-user-billing: runtimeKind: go-service diff --git a/internal/cloud/server-code-agent-http.ts b/internal/cloud/server-code-agent-http.ts index 683e621d..6bd27f0e 100644 --- a/internal/cloud/server-code-agent-http.ts +++ b/internal/cloud/server-code-agent-http.ts @@ -35,6 +35,7 @@ import { const DEFAULT_CODE_AGENT_RESULT_TRACE_EVENT_LIMIT = 120; const DEFAULT_CODE_AGENT_TRACE_PAGE_LIMIT = 100; const MAX_CODE_AGENT_TRACE_PAGE_LIMIT = 100; +const DEFAULT_CODE_AGENT_TURN_STATUS_REFRESH_TIMEOUT_MS = 2500; const DEFAULT_CODE_AGENT_PROJECT_ID = "prj_hwpod_workbench"; const DEFAULT_CODE_AGENT_PROVIDER_PROFILE = "deepseek"; const CODE_AGENT_TERMINAL_STATUSES = new Set(["completed", "failed", "blocked", "timeout", "cancelled", "canceled"]); @@ -1201,6 +1202,7 @@ export async function handleCodeAgentTurnHttp(request, response, url, options) { async function resolveCodeAgentTurnStatusSnapshot(traceId, options) { const traceStore = options.traceStore ?? defaultCodeAgentTraceStore; + const refreshOptions = codeAgentTurnStatusRefreshOptions(options); let result = options.codeAgentChatResults?.get(traceId) ?? null; if (result && !canAccessOwnedResult(result, options.actor)) return forbiddenTurnSnapshot(traceId); @@ -1208,7 +1210,7 @@ async function resolveCodeAgentTurnStatusSnapshot(traceId, options) { let resultPollError = null; if (adapterEnabled && (result?.agentRun?.runId || !result)) { try { - const synced = await syncAgentRunChatResult({ traceId, currentResult: result, options, traceStore }); + const synced = await syncAgentRunChatResult({ traceId, currentResult: result, options: refreshOptions, traceStore }); result = synced.result ?? result; if (result && !canAccessOwnedResult(result, options.actor)) return forbiddenTurnSnapshot(traceId); if (result && isTraceCommandTerminalStatus(result.status)) { @@ -1248,10 +1250,10 @@ async function resolveCodeAgentTurnStatusSnapshot(traceId, options) { let refreshError = null; if (agentRunResult?.agentRun) { try { - const refreshedTrace = await refreshAgentRunTrace({ traceId, result: agentRunResult, options, traceStore }); + const refreshedTrace = await refreshAgentRunTrace({ traceId, result: agentRunResult, options: refreshOptions, traceStore }); agentRunResult = options.codeAgentChatResults?.get?.(traceId) ?? agentRunResult; if (traceNeedsCommandResultSync(agentRunResult, refreshedTrace)) { - const synced = await syncAgentRunChatResult({ traceId, currentResult: agentRunResult, options, traceStore, appendResultEvent: false, refreshEvents: false, forceResultSync: true }); + const synced = await syncAgentRunChatResult({ traceId, currentResult: agentRunResult, options: refreshOptions, traceStore, appendResultEvent: false, refreshEvents: false, forceResultSync: true }); agentRunResult = synced.result ?? agentRunResult; } if (isTraceCommandTerminalStatus(agentRunResult?.status)) { @@ -1269,6 +1271,21 @@ async function resolveCodeAgentTurnStatusSnapshot(traceId, options) { return { statusCode: body.ok ? 200 : 404, body }; } +function codeAgentTurnStatusRefreshOptions(options = {}) { + const env = options.env ?? process.env; + const configuredBudgetMs = parsePositiveInteger(env.HWLAB_CODE_AGENT_TURN_STATUS_REFRESH_TIMEOUT_MS, DEFAULT_CODE_AGENT_TURN_STATUS_REFRESH_TIMEOUT_MS); + const upstreamTimeoutMs = parsePositiveInteger(env.HWLAB_CODE_AGENT_AGENTRUN_HTTP_TIMEOUT_MS, configuredBudgetMs); + const timeoutMs = Math.max(1, Math.min(configuredBudgetMs, upstreamTimeoutMs)); + return { + ...options, + env: { + ...env, + HWLAB_CODE_AGENT_AGENTRUN_HTTP_TIMEOUT_MS: String(timeoutMs), + HWLAB_CODE_AGENT_TURN_STATUS_REFRESH_TIMEOUT_MS: String(timeoutMs) + } + }; +} + function forbiddenTurnSnapshot(traceId) { return { statusCode: 403,