From 528110eb50b8a8a7c480a651ff1cf88d5df34f05 Mon Sep 17 00:00:00 2001 From: Trevor Edwards <9443483+trevoedwards@users.noreply.github.com> Date: Wed, 4 Nov 2026 04:49:01 +0000 Subject: [PATCH 1/2] fix(cursor): retain SDK failure codes and retryability --- .../src/server/CursorAgentSdk.ts | 11 ++++++++ .../src/server/adapter.test.ts | 26 ++++++++++++++++++- .../provider-cursor/src/server/adapter.ts | 2 ++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/provider-cursor/src/server/CursorAgentSdk.ts b/packages/provider-cursor/src/server/CursorAgentSdk.ts index f48d7baf1b24a..581eb2c414332 100644 --- a/packages/provider-cursor/src/server/CursorAgentSdk.ts +++ b/packages/provider-cursor/src/server/CursorAgentSdk.ts @@ -58,6 +58,17 @@ export function isCursorUsageLimitError(cause: unknown): boolean { ); } +export function cursorSdkFailureMetadata(cause: unknown) { + const error = isCursorAgentSdkRunnerError(cause) ? cause.cause : cause; + if (typeof error !== "object" || error === null) return {}; + const code = Reflect.get(error, "code"); + const retryable = Reflect.get(error, "isRetryable"); + return { + ...(typeof code === "string" ? { code } : {}), + ...(typeof retryable === "boolean" ? { retryable } : {}), + }; +} + const GENERIC_SDK_ERROR_CODES = new Set(["", "error", "unknown"]); function sdkErrorReason(cause: unknown): string | undefined { diff --git a/packages/provider-cursor/src/server/adapter.test.ts b/packages/provider-cursor/src/server/adapter.test.ts index 69fa9cd63a800..0f9afcb86db77 100644 --- a/packages/provider-cursor/src/server/adapter.test.ts +++ b/packages/provider-cursor/src/server/adapter.test.ts @@ -36,7 +36,7 @@ import { import * as CursorAgentSdk from "./CursorAgentSdk.ts"; import { loadCursorSdk } from "./sdk.ts"; -const { ConfigurationError } = loadCursorSdk(); +const { ConfigurationError, NetworkError } = loadCursorSdk(); describe("CursorAdapterV2", () => { it.effect.each([ @@ -279,6 +279,26 @@ describe("CursorAdapterV2", () => { ); it.effect.each([ + ...([true, false] as const).map((isRetryable) => ({ + reason: `the SDK network failure with retryable=${isRetryable}`, + updates: [], + answers: undefined, + wait: Effect.fail( + new CursorAgentSdk.CursorAgentSdkRunnerError({ + method: "run.wait", + cause: new NetworkError("Connection stalled", { + code: "connection_stalled", + isRetryable, + }), + }), + ), + failure: { + class: "transport_error", + message: "Cursor Agent SDK run.wait failed: Connection stalled (connection_stalled)", + code: "connection_stalled", + retryable: isRetryable, + }, + })), { reason: "the SDK's detailed plan usage limit code", wait: Effect.succeed({ @@ -493,6 +513,10 @@ describe("CursorAdapterV2", () => { assert.equal(terminal?.status, failure === null ? "completed" : "failed"); assert.equal(terminal?.failure?.class, failure?.class); assert.equal(terminal?.failure?.message, failure?.message); + if (failure !== null && "retryable" in failure) { + assert.equal(terminal?.failure?.retryable, failure.retryable); + assert.equal(terminal?.failure?.code, failure.code); + } if (answers !== undefined) { assert.deepEqual( events.flatMap((event) => diff --git a/packages/provider-cursor/src/server/adapter.ts b/packages/provider-cursor/src/server/adapter.ts index e5b77e1041d4a..a1ccb3d4ec6b6 100644 --- a/packages/provider-cursor/src/server/adapter.ts +++ b/packages/provider-cursor/src/server/adapter.ts @@ -2520,6 +2520,7 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( transportFailure === undefined ? makeProviderFailure({ cause: result.error, + ...CursorAgentSdk.cursorSdkFailureMetadata(result.error), // makeProviderFailure never reads a cause's own text. message: result.error?.message, class: CursorAgentSdk.isCursorUsageLimitError(result.error) @@ -2547,6 +2548,7 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( : { failure: makeProviderFailure({ cause, + ...CursorAgentSdk.cursorSdkFailureMetadata(cause), message: CursorAgentSdk.isCursorAgentSdkRunnerError(cause) ? CursorAgentSdk.cursorSdkFailureMessage(cause) : undefined, From 7943f470121c7e58622eee1347403d7af4ac830a Mon Sep 17 00:00:00 2001 From: Trevor Edwards <9443483+trevoedwards@users.noreply.github.com> Date: Wed, 4 Nov 2026 05:44:27 +0000 Subject: [PATCH 2/2] test(cursor): cover metadata from returned SDK failures --- .../provider-cursor/src/server/adapter.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/provider-cursor/src/server/adapter.test.ts b/packages/provider-cursor/src/server/adapter.test.ts index 0f9afcb86db77..521bc1bc41308 100644 --- a/packages/provider-cursor/src/server/adapter.test.ts +++ b/packages/provider-cursor/src/server/adapter.test.ts @@ -279,6 +279,22 @@ describe("CursorAdapterV2", () => { ); it.effect.each([ + ...([true, false] as const).map((isRetryable) => ({ + reason: `a returned SDK error with retryable=${isRetryable}`, + updates: [], + answers: undefined, + wait: Effect.succeed({ + id: "native-cursor-run", + status: "error" as const, + error: { message: "Connection stalled", code: "connection_stalled", isRetryable }, + }), + failure: { + class: "provider_error", + message: "Connection stalled", + code: "connection_stalled", + retryable: isRetryable, + }, + })), ...([true, false] as const).map((isRetryable) => ({ reason: `the SDK network failure with retryable=${isRetryable}`, updates: [],