fix(runner): classify ACPX session ensure failures

This commit is contained in:
Dotta 2026-09-02 22:14:14 -05:00
parent 21bc0eba68
commit f6f2d02b5d
3 changed files with 46 additions and 14 deletions

View File

@ -619,6 +619,10 @@ fn response_error_classification(error: &ResponseError) -> &'static str {
"SESSION_CONFIG_OPTION_REPLAY_FAILED" => return "session_config_option_replay_failed",
"CLAUDE_ACP_SESSION_CREATE_TIMEOUT" => return "claude_session_create_timeout",
"ACPX_SESSION_HANDSHAKE_TIMEOUT" => return "session_handshake_timeout",
"ACPX_SESSION_ENSURE_FAILED" => return "session_ensure_failed",
"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",
"ACPX_RUNTIME_ADMISSION_VERIFICATION_TIMEOUT" => {
return "runtime_admission_verification_timeout"
}
@ -728,6 +732,13 @@ mod tests {
"ACPX_RUNTIME_ADMISSION_VERIFICATION_TIMEOUT",
"runtime_admission_verification_timeout",
),
("ACPX_SESSION_ENSURE_FAILED", "session_ensure_failed"),
(
"ACPX_SESSION_ENSURE_TYPE_ERROR",
"session_ensure_type_error",
),
("ACPX_SESSION_ENSURE_NON_ERROR", "session_ensure_non_error"),
("ACP_SESSION_INIT_FAILED", "acp_session_init_failed"),
(
"ACPX_SIDECAR_STATUS_READ_TIMEOUT",
"session_status_read_timeout",

View File

@ -2100,6 +2100,7 @@ describe("Codex ACPX runtime adapter", () => {
},
}),
).rejects.toBe(failure);
expect(failure).toMatchObject({ code: "ACPX_SESSION_ENSURE_FAILED" });
expect(runtime.close).toHaveBeenCalledOnce();
const recoveredClose = vi.mocked(runtime.close).mock.calls[0]![0];
expect(recoveredClose).toMatchObject({

View File

@ -288,20 +288,24 @@ export async function openQualifiedAcpxRuntime(
runtimeCloseTimeoutMs,
);
const handshake = Promise.resolve().then(() =>
runtime.ensureSession({
sessionKey: options.providerSessionKey,
agent: options.profile.agent,
mode: "persistent",
cwd: options.cwd,
sessionOptions: {
model: options.profile.qualificationModel,
...(options.systemInstructions
? { systemPrompt: { append: options.systemInstructions } }
: {}),
},
}),
);
const handshake = Promise.resolve()
.then(() =>
runtime.ensureSession({
sessionKey: options.providerSessionKey,
agent: options.profile.agent,
mode: "persistent",
cwd: options.cwd,
sessionOptions: {
model: options.profile.qualificationModel,
...(options.systemInstructions
? { systemPrompt: { append: options.systemInstructions } }
: {}),
},
}),
)
.catch((error: unknown) => {
throw classifySessionEnsureFailure(error);
});
let handle: AcpRuntimeHandle | null = null;
let lateCleanup: Promise<void> | null = null;
try {
@ -393,6 +397,22 @@ export async function openQualifiedAcpxRuntime(
/** Backward-compatible name retained for existing Codex-only consumers. */
export const openCodexAcpxRuntime = openQualifiedAcpxRuntime;
function classifySessionEnsureFailure(error: unknown): Error {
if (error instanceof Error) {
const details = error as Error & Record<string, unknown>;
if (typeof details.code !== "string" || details.code.length === 0) {
details.code =
error instanceof TypeError
? "ACPX_SESSION_ENSURE_TYPE_ERROR"
: "ACPX_SESSION_ENSURE_FAILED";
}
return error;
}
return Object.assign(new Error("ACPX session ensure rejected a non-error"), {
code: "ACPX_SESSION_ENSURE_NON_ERROR",
});
}
function raceRuntimeHandshakeWithAbort<T>(
handshake: Promise<T>,
signal: AbortSignal,