fix(runner): preserve ACPX failure detail codes

This commit is contained in:
Dotta 2026-09-02 22:26:09 -05:00
parent f6f2d02b5d
commit 4ac656e8d4
3 changed files with 44 additions and 5 deletions

View File

@ -623,6 +623,12 @@ fn response_error_classification(error: &ResponseError) -> &'static str {
"ACPX_SESSION_ENSURE_TYPE_ERROR" => return "session_ensure_type_error",
"ACPX_SESSION_ENSURE_NON_ERROR" => return "session_ensure_non_error",
"ACP_SESSION_INIT_FAILED" => return "acp_session_init_failed",
"NO_SESSION" => return "acpx_no_session",
"TIMEOUT" => return "acpx_timeout",
"PERMISSION_DENIED" => return "acpx_permission_denied",
"PERMISSION_PROMPT_UNAVAILABLE" => return "acpx_permission_prompt_unavailable",
"RUNTIME" => return "acpx_runtime_failure",
"USAGE" => return "acpx_usage_failure",
"ACPX_RUNTIME_ADMISSION_VERIFICATION_TIMEOUT" => {
return "runtime_admission_verification_timeout"
}
@ -739,6 +745,15 @@ mod tests {
),
("ACPX_SESSION_ENSURE_NON_ERROR", "session_ensure_non_error"),
("ACP_SESSION_INIT_FAILED", "acp_session_init_failed"),
("NO_SESSION", "acpx_no_session"),
("TIMEOUT", "acpx_timeout"),
("PERMISSION_DENIED", "acpx_permission_denied"),
(
"PERMISSION_PROMPT_UNAVAILABLE",
"acpx_permission_prompt_unavailable",
),
("RUNTIME", "acpx_runtime_failure"),
("USAGE", "acpx_usage_failure"),
(
"ACPX_SIDECAR_STATUS_READ_TIMEOUT",
"session_status_read_timeout",

View File

@ -130,6 +130,12 @@ describe("ACPX sidecar input sequencing", () => {
code: "ACP_MODEL_UNSUPPORTED",
detailCode: "AGENT_STARTUP_FAILED",
});
const genericStartup = Object.assign(new Error("provider exited"), {
code: "RUNTIME",
detailCode: "AGENT_STARTUP_FAILED",
stderrSummary: "Error [ERR_MODULE_NOT_FOUND]: package was not found",
exitCode: 1,
});
const nestedHandshake = new AggregateError(
[
Object.assign(new Error("admission deadline"), {
@ -149,6 +155,9 @@ describe("ACPX sidecar input sequencing", () => {
"violet-circuit-4821",
);
expect(acpxSidecarErrorCode(model)).toBe("ACP_MODEL_UNSUPPORTED");
expect(acpxSidecarErrorCode(genericStartup)).toBe(
"AGENT_STARTUP_FAILED.MODULE_NOT_FOUND",
);
expect(acpxSidecarErrorCode(nestedHandshake)).toBe(
"ACPX_SESSION_HANDSHAKE_TIMEOUT",
);

View File

@ -57,12 +57,18 @@ export function acpxSidecarErrorCode(error: Error): string {
function directAcpxSidecarErrorCode(error: Error): string | null {
const details = error as Error & Record<string, unknown>;
const outputCode = typeof details.code === "string" ? details.code : null;
const detailCode =
typeof details.detailCode === "string" ? details.detailCode : null;
// ACPX output errors may carry both a broad presentation code (for example,
// RUNTIME) and the stable operational identity that produced it. Preserve
// the latter across the sidecar boundary; otherwise a provider bootstrap
// failure is reduced to an unclassified generic runtime rejection.
const code =
typeof details.code === "string"
? details.code
: typeof details.detailCode === "string"
? details.detailCode
: null;
detailCode !== null &&
(outputCode === null || GENERIC_ACPX_OUTPUT_CODES.has(outputCode))
? detailCode
: (outputCode ?? detailCode);
if (code === null) {
return error.name === "AcpxSessionHandshakeTimeoutError" ||
error.message === "ACPX session handshake exceeded its admission deadline"
@ -104,3 +110,12 @@ function directAcpxSidecarErrorCode(error: Error): string | null {
}
return "AGENT_STARTUP_FAILED.OTHER";
}
const GENERIC_ACPX_OUTPUT_CODES = new Set([
"NO_SESSION",
"TIMEOUT",
"PERMISSION_DENIED",
"PERMISSION_PROMPT_UNAVAILABLE",
"RUNTIME",
"USAGE",
]);