fix: classify code agent timeout failures
This commit is contained in:
@@ -2,7 +2,17 @@ const trustedLiveProviders = new Set(["openai-responses", "codex-cli", "codex-re
|
||||
const trustedRunnerProviders = new Set(["codex-readonly-runner"]);
|
||||
const readonlySessionCapabilityLevels = new Set(["read-only-tools", "read-only-session-tools"]);
|
||||
const untrustedProviderPattern = /\b(?:echo|mock|fixture|stub|sample)\b/iu;
|
||||
const blockedCodeAgentUiLabels = new Set(["发送失败", "服务受阻", "BLOCKED 凭证缺口"]);
|
||||
const blockedCodeAgentUiLabels = new Set([
|
||||
"发送失败",
|
||||
"服务受阻",
|
||||
"等待超时",
|
||||
"Provider 不可用",
|
||||
"Runner 忙碌",
|
||||
"Session 受阻",
|
||||
"Runner 受阻",
|
||||
"API 错误",
|
||||
"BLOCKED 凭证缺口"
|
||||
]);
|
||||
const timeoutPattern = /\b(?:timeout|timed out|abort|aborted|超时|请求超过)\b/iu;
|
||||
|
||||
export function classifyCodeAgentChatReadiness(payload, { realDevLive = false, httpStatus = null } = {}) {
|
||||
@@ -345,6 +355,7 @@ export function summarizeCodeAgentPayload(payload, { httpStatus = null, traceId
|
||||
|
||||
export function classifyCodeAgentBrowserJourney({ responseSummary, httpOk = false, httpStatus = null, ui = null } = {}) {
|
||||
const providerBlock = classifyCodeAgentProviderBlock(responseSummary, { httpStatus });
|
||||
const runtimeBlock = classifyCodeAgentRuntimeBlock(responseSummary, { httpStatus });
|
||||
const uiCompleted = ui?.agentChatStatus === "DEV-LIVE 回复" || ui?.agentChatStatus === "已回复" || ui?.completedMessageVisible === true;
|
||||
const uiFailed = ui?.failedMessageVisible === true || blockedCodeAgentUiLabels.has(ui?.agentChatStatus);
|
||||
const evidence = inspectSanitizedCompletionEvidence(responseSummary);
|
||||
@@ -363,6 +374,10 @@ export function classifyCodeAgentBrowserJourney({ responseSummary, httpOk = fals
|
||||
};
|
||||
}
|
||||
|
||||
if (runtimeBlock.blocked) {
|
||||
return runtimeBlock;
|
||||
}
|
||||
|
||||
if (providerBlock.blocked) {
|
||||
return {
|
||||
status: "blocked",
|
||||
@@ -387,6 +402,63 @@ export function classifyCodeAgentBrowserJourney({ responseSummary, httpOk = fals
|
||||
};
|
||||
}
|
||||
|
||||
export function classifyCodeAgentRuntimeBlock(payload, { httpStatus = null } = {}) {
|
||||
const errorCode = stringOrNull(payload?.error?.code);
|
||||
const payloadStatus = stringOrNull(payload?.status);
|
||||
|
||||
if (errorCode === "session_busy") {
|
||||
return {
|
||||
blocked: true,
|
||||
status: "blocked",
|
||||
blocker: "runner-busy",
|
||||
category: "runner_busy",
|
||||
reason: "Code Agent read-only runner session is already busy; the UI must keep the input retryable."
|
||||
};
|
||||
}
|
||||
|
||||
if (["session_expired", "session_reuse_conflict"].includes(errorCode)) {
|
||||
return {
|
||||
blocked: true,
|
||||
status: "blocked",
|
||||
blocker: "session-blocked",
|
||||
category: "session_blocked",
|
||||
reason: `Code Agent session is blocked by ${errorCode}; fallback must not be reported as a Codex session.`
|
||||
};
|
||||
}
|
||||
|
||||
if (["runner_unavailable", "tool_unavailable", "skills_unavailable", "security_blocked"].includes(errorCode)) {
|
||||
return {
|
||||
blocked: true,
|
||||
status: "blocked",
|
||||
blocker: "runner-blocked",
|
||||
category: "runner_blocked",
|
||||
reason: `Code Agent read-only runner returned ${errorCode}; this is blocked runner evidence, not a completed fallback.`
|
||||
};
|
||||
}
|
||||
|
||||
if (["invalid_params", "parse_error", "request_failed", "cloud_api_proxy_failed", "upstream_unavailable"].includes(errorCode)) {
|
||||
return {
|
||||
blocked: true,
|
||||
status: "blocked",
|
||||
blocker: "api-error",
|
||||
category: "api_error",
|
||||
reason: `Code Agent API returned ${errorCode}; the request is retryable and must remain distinguishable from provider failure.`
|
||||
};
|
||||
}
|
||||
|
||||
if (payloadStatus === "failed" && errorCode && errorCode !== "provider_unavailable") {
|
||||
return {
|
||||
blocked: true,
|
||||
status: "blocked",
|
||||
blocker: "api-error",
|
||||
category: "api_error",
|
||||
reason: `Code Agent returned failed status with ${errorCode}.`
|
||||
};
|
||||
}
|
||||
|
||||
return { blocked: false };
|
||||
}
|
||||
|
||||
export function classifyCodeAgentBrowserFailure(error, { traceId = null } = {}) {
|
||||
const message = error instanceof Error ? error.message : String(error ?? "");
|
||||
const observedTraceId = traceId ?? error?.traceId ?? null;
|
||||
@@ -399,6 +471,24 @@ export function classifyCodeAgentBrowserFailure(error, { traceId = null } = {})
|
||||
reason: message
|
||||
};
|
||||
}
|
||||
if (/session[_ -]?busy|runner busy|already busy/iu.test(message)) {
|
||||
return {
|
||||
status: "blocked",
|
||||
blocker: "runner-busy",
|
||||
category: "runner_busy",
|
||||
chineseSummary: `Runner 忙碌:Code Agent 正在处理上一轮请求,可稍后重试${observedTraceId ? `;traceId=${observedTraceId}` : ""}。`,
|
||||
reason: message
|
||||
};
|
||||
}
|
||||
if (/session[_ -]?(expired|blocked|reuse_conflict)|conversation.*session/iu.test(message)) {
|
||||
return {
|
||||
status: "blocked",
|
||||
blocker: "session-blocked",
|
||||
category: "session_blocked",
|
||||
chineseSummary: `Session 受阻:Code Agent 会话无法复用或已过期${observedTraceId ? `;traceId=${observedTraceId}` : ""}。`,
|
||||
reason: message
|
||||
};
|
||||
}
|
||||
if (/provider|upstream|openai|codex|502|503|504|429|provider_unavailable/iu.test(message)) {
|
||||
return {
|
||||
status: "blocked",
|
||||
@@ -408,6 +498,15 @@ export function classifyCodeAgentBrowserFailure(error, { traceId = null } = {})
|
||||
reason: message
|
||||
};
|
||||
}
|
||||
if (/api|http \d{3}|invalid_params|parse_error|request_failed|cloud_api_proxy_failed/iu.test(message)) {
|
||||
return {
|
||||
status: "blocked",
|
||||
blocker: "api-error",
|
||||
category: "api_error",
|
||||
chineseSummary: `API 错误:Code Agent 接口返回失败,输入应保留可重试${observedTraceId ? `;traceId=${observedTraceId}` : ""}。`,
|
||||
reason: message
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: "blocked",
|
||||
blocker: "browser-runtime",
|
||||
|
||||
Reference in New Issue
Block a user