fix: block code agent non-2xx readiness
This commit is contained in:
@@ -396,6 +396,9 @@ async function sendAgentMessage(message, conversationId, traceId = nextProtocolI
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
if (isBlockedAgentResponse(response.data, response.status)) {
|
||||
return normalizeBlockedAgentResult(response.data, response.status, traceId, response.error);
|
||||
}
|
||||
const error = new Error(response.error || "Code Agent 请求失败");
|
||||
error.traceId = traceId;
|
||||
throw error;
|
||||
@@ -441,9 +444,27 @@ async function fetchJson(path, options = {}) {
|
||||
signal: controller.signal
|
||||
});
|
||||
const text = await response.text();
|
||||
const data = text ? JSON.parse(text) : null;
|
||||
let data = null;
|
||||
try {
|
||||
data = text ? JSON.parse(text) : null;
|
||||
} catch (error) {
|
||||
if (response.ok) throw error;
|
||||
return {
|
||||
ok: false,
|
||||
path,
|
||||
status: response.status,
|
||||
data: null,
|
||||
error: `${path} HTTP ${response.status}:响应不是 JSON`
|
||||
};
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error(`${path} HTTP ${response.status}:${messageFromPayload(data)}`);
|
||||
return {
|
||||
ok: false,
|
||||
path,
|
||||
status: response.status,
|
||||
data,
|
||||
error: `${path} HTTP ${response.status}:${messageFromPayload(data)}`
|
||||
};
|
||||
}
|
||||
return { ok: true, path, status: response.status, data };
|
||||
} catch (error) {
|
||||
@@ -1456,7 +1477,9 @@ function classifyCodeAgentCompletion(result) {
|
||||
|
||||
function failureMessage(result) {
|
||||
if (result?.error?.code === "provider_unavailable" || result?.availability?.status === "blocked") {
|
||||
const availability = result.availability ?? codeAgentAvailabilityFromResult(result);
|
||||
const availability = result.error?.providerStatus || result.availability?.status !== "blocked"
|
||||
? codeAgentAvailabilityFromResult(result)
|
||||
: result.availability ?? codeAgentAvailabilityFromResult(result);
|
||||
const missing = Array.isArray(result.error?.missingEnv) && result.error.missingEnv.length > 0
|
||||
? ` 当前缺口:${result.error.missingEnv.join("、")}。`
|
||||
: "";
|
||||
@@ -1612,25 +1635,66 @@ function messageFromPayload(payload) {
|
||||
return payload.error?.message || payload.error?.code || payload.message || JSON.stringify(payload).slice(0, 160);
|
||||
}
|
||||
|
||||
function normalizeBlockedAgentResult(payload, httpStatus, traceId, fallbackMessage) {
|
||||
const { reply, ...safePayload } = payload;
|
||||
const providerStatus = payload.error?.providerStatus ?? httpStatus;
|
||||
const error = {
|
||||
...(payload.error && typeof payload.error === "object" ? payload.error : {}),
|
||||
code: payload.error?.code ?? "provider_unavailable",
|
||||
message: payload.error?.message ?? fallbackMessage ?? `Code Agent provider HTTP ${httpStatus}`,
|
||||
providerStatus
|
||||
};
|
||||
return {
|
||||
...safePayload,
|
||||
status: "failed",
|
||||
traceId: payload.traceId || traceId,
|
||||
error,
|
||||
availability: payload.availability ?? codeAgentAvailabilityFromResult({ error })
|
||||
};
|
||||
}
|
||||
|
||||
function isBlockedAgentResponse(payload, httpStatus) {
|
||||
return Boolean(
|
||||
payload &&
|
||||
typeof payload === "object" &&
|
||||
(
|
||||
isHttpNon2xxStatus(httpStatus) ||
|
||||
isHttpNon2xxStatus(payload.error?.providerStatus) ||
|
||||
payload.status === "blocked" ||
|
||||
payload.error?.code === "provider_unavailable" ||
|
||||
payload.availability?.status === "blocked"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function isHttpNon2xxStatus(value) {
|
||||
return typeof value === "number" && Number.isInteger(value) && (value < 200 || value > 299);
|
||||
}
|
||||
|
||||
function codeAgentAvailabilityFrom(live) {
|
||||
return live?.restIndex?.data?.codeAgent ?? live?.healthLive?.data?.codeAgent ?? null;
|
||||
}
|
||||
|
||||
function codeAgentAvailabilityFromResult(result) {
|
||||
const missingEnv = Array.isArray(result?.error?.missingEnv) ? result.error.missingEnv : [];
|
||||
const providerStatus = result?.error?.providerStatus;
|
||||
const credentialBlocked = missingEnv.includes("OPENAI_API_KEY");
|
||||
return {
|
||||
status: "blocked",
|
||||
blocker: "凭证缺口",
|
||||
blocker: credentialBlocked ? "凭证缺口" : "provider_unavailable",
|
||||
reason: result?.error?.code ?? "provider_unavailable",
|
||||
summary: "真实后端已接入,但当前 DEV provider Secret hwlab-code-agent-provider/openai-api-key 未注入,因此不能返回真实回复。",
|
||||
missingEnv: result?.error?.missingEnv ?? [],
|
||||
secretRefs: [
|
||||
summary: providerStatus
|
||||
? `真实后端已接入,但当前 provider 上游返回 HTTP ${providerStatus};本次保持 BLOCKED/provider_unavailable,不会冒充真实 Code Agent 回复。`
|
||||
: "真实后端已接入,但当前 DEV provider Secret hwlab-code-agent-provider/openai-api-key 未注入,因此不能返回真实回复。",
|
||||
missingEnv,
|
||||
secretRefs: credentialBlocked ? [
|
||||
{
|
||||
env: "OPENAI_API_KEY",
|
||||
secretName: "hwlab-code-agent-provider",
|
||||
secretKey: "openai-api-key",
|
||||
redacted: true
|
||||
}
|
||||
]
|
||||
] : []
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -320,6 +320,10 @@ assert.doesNotMatch(html, /主流程[\s\S]*添加草稿/u);
|
||||
assert.match(app, /sendAgentMessage/);
|
||||
assert.match(app, /fetchJson\("\/v1\/agent\/chat"/);
|
||||
assert.match(app, /Code Agent 调用失败/);
|
||||
assert.match(app, /normalizeBlockedAgentResult/);
|
||||
assert.match(app, /isBlockedAgentResponse/);
|
||||
assert.match(app, /isHttpNon2xxStatus/);
|
||||
assert.match(app, /providerStatus/);
|
||||
assert.match(app, /codeAgentAvailability/);
|
||||
assert.match(app, /codeAgentAvailabilityFrom/);
|
||||
assert.match(app, /latestCompletedAgentMessage/);
|
||||
|
||||
Reference in New Issue
Block a user