From f2ef9b35e3b66f3780f24efb376b1bcfdcee8673 Mon Sep 17 00:00:00 2001 From: Nikita Koynov <43469098+nkoynov@users.noreply.github.com> Date: Mon, 5 Oct 2026 19:25:01 +0300 Subject: [PATCH 1/2] fix(server): failed Cursor turns show Cursor's reason Every failed Cursor turn showed "Provider turn failed.", and a delegate_task parent got the same text. The SDK reports the reason, for example "Model Blocked Please ask your admin to enable access to Claude Fable 5." or "AI Model Not Found Invalid parameters for registry model", but makeProviderFailure doesn't read a cause's text, and CursorAgentSdkRunnerError only named the SDK method. A run that ends with an error now fails the turn with the run's own error message. CursorAgentSdkRunnerError's message now adds the SDK error's message, code and HTTP status ("Cursor Agent SDK agent.create failed: Invalid User API Key (HTTP 401)"), and a run that throws fails the turn with that message. Model: Claude Opus 5.5 (1M). Harness: Claude Code in T3 Code. --- .../src/server/CursorAgentSdk.test.ts | 21 +++ .../src/server/CursorAgentSdk.ts | 26 +++- .../src/server/adapter.test.ts | 141 ++++++++++++++++++ .../provider-cursor/src/server/adapter.ts | 28 ++-- 4 files changed, 205 insertions(+), 11 deletions(-) diff --git a/packages/provider-cursor/src/server/CursorAgentSdk.test.ts b/packages/provider-cursor/src/server/CursorAgentSdk.test.ts index 5bd81aea13c95..7b2ec3a76dd2a 100644 --- a/packages/provider-cursor/src/server/CursorAgentSdk.test.ts +++ b/packages/provider-cursor/src/server/CursorAgentSdk.test.ts @@ -304,4 +304,25 @@ describe("CursorAgentSdkRunner", () => { assert.equal(cursorSdkMock.runWait.mock.calls.length, 1); }).pipe(Effect.provide(layerTest)), ); + + it("names the SDK's reason, code and HTTP status in a runner error", () => { + const failed = (cause: unknown) => + new CursorAgentSdk.CursorAgentSdkRunnerError({ method: "agent.create", cause }).message; + assert.equal( + failed(Object.assign(new Error("Too many requests"), { code: "rate_limited", status: 429 })), + "Cursor Agent SDK agent.create failed: Too many requests (rate_limited, HTTP 429)", + ); + // Cursor's messages often start with the code already. + assert.equal( + failed( + Object.assign(new Error("[resource_exhausted] Error"), { code: "resource_exhausted" }), + ), + "Cursor Agent SDK agent.create failed: [resource_exhausted] Error", + ); + assert.equal( + failed(Object.assign(new Error("Invalid User API Key"), { code: "error", status: 401 })), + "Cursor Agent SDK agent.create failed: Invalid User API Key (HTTP 401)", + ); + assert.equal(failed(undefined), "Cursor Agent SDK agent.create failed."); + }); }); diff --git a/packages/provider-cursor/src/server/CursorAgentSdk.ts b/packages/provider-cursor/src/server/CursorAgentSdk.ts index e0162a4d062c2..f81fae76a3003 100644 --- a/packages/provider-cursor/src/server/CursorAgentSdk.ts +++ b/packages/provider-cursor/src/server/CursorAgentSdk.ts @@ -32,11 +32,33 @@ export class CursorAgentSdkRunnerError extends Schema.TaggedError { ), ); + it.effect.each([ + { + reason: "the error Cursor's run ended with", + wait: Effect.succeed({ + id: "native-cursor-run", + requestId: "native-request", + status: "error" as const, + error: { + message: "Model Blocked Please ask your admin to enable access to Claude Fable 5.", + }, + model: { id: "claude-fable-5" }, + durationMs: 1, + }), + failure: { + class: "provider_error", + message: "Model Blocked Please ask your admin to enable access to Claude Fable 5.", + }, + }, + { + reason: "the SDK error that broke the run", + wait: Effect.fail( + new CursorAgentSdk.CursorAgentSdkRunnerError({ + method: "run.wait", + cause: Object.assign(new Error("Too many requests"), { + code: "resource_exhausted", + status: 429, + }), + }), + ), + failure: { + class: "transport_error", + message: + "Cursor Agent SDK run.wait failed: Too many requests (resource_exhausted, HTTP 429)", + }, + }, + ])("fails the turn with $reason", ({ wait, failure }) => + Effect.gen(function* () { + const fileSystem = yield* FileSystem.FileSystem; + const workspace = yield* fileSystem.makeTempDirectoryScoped({ prefix: "cursor-v2-failure-" }); + const instanceId = ProviderInstanceId.make("cursor"); + const threadId = ThreadId.make("cursor-failure-thread"); + const modelSelection = { instanceId, model: "claude-fable-5" }; + const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ + runtimeMode: "full-access", + interactionMode: "default", + cwd: workspace, + }); + const adapter = yield* makeCursorAdapterV2({ + instanceId, + settings: yield* decodeCursorSettings({}), + environment: { HOME: workspace }, + }).pipe( + Effect.provideService(CursorAgentSdk.CursorAgentSdkRunner, { + assertComplete: Effect.void, + open: () => + Effect.succeed({ + agentId: "native-cursor-failure", + listMessages: Effect.succeed([]), + close: Effect.void, + send: () => + Effect.succeed({ + agentId: "native-cursor-failure", + runId: "native-cursor-run", + wait, + cancel: Effect.void, + }), + }), + }), + Effect.provide(TestProviderHost.layer({ cwd: workspace })), + ); + const runtime = yield* adapter.openSession({ + threadId, + providerSessionId: ProviderSessionId.make("cursor-failure-session"), + modelSelection, + runtimePolicy, + }); + const providerThread = yield* runtime.ensureThread({ + threadId, + modelSelection, + runtimePolicy, + }); + const now = yield* DateTime.now; + yield* runtime.startTurn({ + threadId, + providerThread, + modelSelection, + runtimePolicy, + runId: RunId.make("cursor-failure-run"), + runOrdinal: 1, + providerTurnOrdinal: 1, + attemptId: RunAttemptId.make("cursor-failure-attempt"), + rootNodeId: NodeId.make("cursor-failure-root"), + appThread: { + id: threadId, + projectId: ProjectId.make("cursor-failure-project"), + createdBy: "user", + creationSource: "web", + title: "Cursor failure", + providerInstanceId: instanceId, + modelSelection, + runtimeMode: "full-access", + interactionMode: "default", + branch: null, + worktreePath: null, + activeProviderThreadId: providerThread.id, + lineage: { parentThreadId: null, relationshipToParent: null, rootThreadId: threadId }, + forkedFrom: null, + createdAt: now, + updatedAt: now, + archivedAt: null, + settledOverride: null, + settledAt: null, + lastVisitedAt: null, + deletedAt: null, + }, + message: { + messageId: MessageId.make("cursor-failure-message"), + createdBy: "user", + creationSource: "web", + text: "hi", + attachments: [], + }, + }); + const terminal = yield* runtime.events.pipe( + Stream.filter((event) => event.type === "turn.terminal"), + Stream.runHead, + ); + assert.isTrue(Option.isSome(terminal)); + if (Option.isSome(terminal)) { + assert.equal(terminal.value.status, "failed"); + assert.equal(terminal.value.failure?.class, failure.class); + assert.equal(terminal.value.failure?.message, failure.message); + } + }).pipe( + Effect.scoped, + Effect.provide( + Layer.mergeAll(NodeServices.layer, IdAllocator.layer, McpProviderSessions.layer), + ), + ), + ); + it.effect("fails standalone SDK transport diagnostics and sends compaction as /compress", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; diff --git a/packages/provider-cursor/src/server/adapter.ts b/packages/provider-cursor/src/server/adapter.ts index f7cd8005eef5f..cc00bd0a3aac5 100644 --- a/packages/provider-cursor/src/server/adapter.ts +++ b/packages/provider-cursor/src/server/adapter.ts @@ -2299,14 +2299,18 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( status, ...(status === "failed" ? { - failure: makeProviderFailure({ - cause: - transportFailure ?? (result as { readonly error?: unknown }).error, - class: - transportFailure === undefined - ? "provider_error" - : "transport_error", - }), + failure: + transportFailure === undefined + ? makeProviderFailure({ + cause: result.error, + // makeProviderFailure never reads a cause's own text. + message: result.error?.message, + class: "provider_error", + }) + : makeProviderFailure({ + cause: transportFailure, + class: "transport_error", + }), } : {}), }); @@ -2322,7 +2326,13 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( ...(context.interrupted ? {} : { - failure: makeProviderFailure({ cause, class: "transport_error" }), + failure: makeProviderFailure({ + cause, + message: CursorAgentSdk.isCursorAgentSdkRunnerError(cause) + ? cause.message + : undefined, + class: "transport_error", + }), }), }); } From f7c2feed8421ccf2dc10b64ae980d1e3acd03f89 Mon Sep 17 00:00:00 2001 From: Nikita Koynov <43469098+nkoynov@users.noreply.github.com> Date: Sun, 11 Oct 2026 10:06:26 +0300 Subject: [PATCH 2/2] fix(server): add Cursor's reason at the adapter, not in the runner error's message --- .../src/server/CursorAgentSdk.test.ts | 13 +++++++++++-- .../provider-cursor/src/server/CursorAgentSdk.ts | 13 +++++++++---- packages/provider-cursor/src/server/adapter.ts | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/provider-cursor/src/server/CursorAgentSdk.test.ts b/packages/provider-cursor/src/server/CursorAgentSdk.test.ts index 7b2ec3a76dd2a..0527e64f1b8c1 100644 --- a/packages/provider-cursor/src/server/CursorAgentSdk.test.ts +++ b/packages/provider-cursor/src/server/CursorAgentSdk.test.ts @@ -305,9 +305,18 @@ describe("CursorAgentSdkRunner", () => { }).pipe(Effect.provide(layerTest)), ); - it("names the SDK's reason, code and HTTP status in a runner error", () => { + it("names the SDK's reason, code and HTTP status in a runner failure's text", () => { const failed = (cause: unknown) => - new CursorAgentSdk.CursorAgentSdkRunnerError({ method: "agent.create", cause }).message; + CursorAgentSdk.cursorSdkFailureMessage( + new CursorAgentSdk.CursorAgentSdkRunnerError({ method: "agent.create", cause }), + ); + assert.equal( + new CursorAgentSdk.CursorAgentSdkRunnerError({ + method: "agent.create", + cause: new Error("Too many requests"), + }).message, + "Cursor Agent SDK agent.create failed.", + ); assert.equal( failed(Object.assign(new Error("Too many requests"), { code: "rate_limited", status: 429 })), "Cursor Agent SDK agent.create failed: Too many requests (rate_limited, HTTP 429)", diff --git a/packages/provider-cursor/src/server/CursorAgentSdk.ts b/packages/provider-cursor/src/server/CursorAgentSdk.ts index f81fae76a3003..21bc6407ed3d5 100644 --- a/packages/provider-cursor/src/server/CursorAgentSdk.ts +++ b/packages/provider-cursor/src/server/CursorAgentSdk.ts @@ -32,13 +32,18 @@ export class CursorAgentSdkRunnerError extends Schema.TaggedError