From f6f2d02b5d8bde843cf3dd8f21989f1ce404c159 Mon Sep 17 00:00:00 2001 From: Dotta Date: Wed, 2 Sep 2026 22:14:14 -0500 Subject: [PATCH] fix(runner): classify ACPX session ensure failures --- .../runner-core/src/acpx_sidecar_transport.rs | 11 +++++ .../acpx/codex-runtime-adapter.test.ts | 1 + .../src/drivers/acpx/codex-runtime-adapter.ts | 48 +++++++++++++------ 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs index d6744506bd..660c2956e0 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs @@ -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", diff --git a/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts b/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts index 02839c31b9..391af18bd4 100644 --- a/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts @@ -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({ diff --git a/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts b/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts index 9502df5eb7..0c9834f94a 100644 --- a/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts +++ b/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts @@ -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 | 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; + 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( handshake: Promise, signal: AbortSignal,