cursor-opencode-provider 0.6.2 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +22 -2
- package/dist/language-model.js +7 -4
- package/dist/models.d.ts +8 -0
- package/dist/models.js +8 -4
- package/dist/pricing-data.d.ts +8 -11
- package/dist/pricing-data.js +8 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -338,7 +338,7 @@ const model = cursor.languageModel("composer-2.5")
|
|
|
338
338
|
// model implements AI SDK LanguageModelV3 (doStream / doGenerate)
|
|
339
339
|
```
|
|
340
340
|
|
|
341
|
-
Pass either `accessToken` (JWT from OAuth or key exchange) or `apiKey` (raw `crsr_...` key). Optional: `apiBaseURL`, `agentBaseURL`, `cacheDir`, `headers`, `telemetryEnabled`, `retry`, and `continuation`. `cacheDir` pins the host cache root for model/version caches and Cursor project metadata; when omitted, the provider uses an injected structural host path bridge or the native OpenCode XDG cache described in [Paths](#paths-host-cache). Transient failures resume from the latest checkpoint produced by that Run, matching Cursor CLI; without an eligible checkpoint, retries remain limited to replay-safe attempts so completed text or tool work is not duplicated. Pending-tool inactivity is renewed by OpenCode activity from the session or its descendants. The older `baseURL` option is still accepted as a legacy alias for `agentBaseURL`.
|
|
341
|
+
Pass either `accessToken` (JWT from OAuth or key exchange) or `apiKey` (raw `crsr_...` key). Optional: `apiBaseURL`, `agentBaseURL`, `cacheDir`, `headers`, `telemetryEnabled`, `retry`, and `continuation`. `cacheDir` pins the host cache root for model/version caches and Cursor project metadata; when omitted, the provider uses an injected structural host path bridge or the native OpenCode XDG cache described in [Paths](#paths-host-cache). Transient failures resume from the latest checkpoint produced by that Run, matching Cursor CLI; without an eligible checkpoint, retries remain limited to replay-safe attempts so completed text or tool work is not duplicated. Once that budget is spent, or replay is unsafe, the terminal error does not re-arm OpenCode's outer retry loop. Pending-tool inactivity is renewed by OpenCode activity from the session or its descendants. The older `baseURL` option is still accepted as a legacy alias for `agentBaseURL`.
|
|
342
342
|
|
|
343
343
|
## Environment variables
|
|
344
344
|
|
package/dist/errors.d.ts
CHANGED
|
@@ -43,6 +43,16 @@ export declare class CursorAuthError extends CursorProviderError {
|
|
|
43
43
|
cause?: unknown;
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* OpenCode's SessionRetry treats several substrings in provider error messages
|
|
48
|
+
* as always-retryable — notably bare "unavailable" and "exhausted", plus a
|
|
49
|
+
* broader regex set (resource_exhausted, service unavailable, …).
|
|
50
|
+
*
|
|
51
|
+
* When this provider has already spent its Run retry budget, or has decided a
|
|
52
|
+
* replay would be unsafe, the host-facing message must not re-arm that loop.
|
|
53
|
+
* Keep the real gRPC/HTTP code on structured fields (`code`, `grpcStatus`).
|
|
54
|
+
*/
|
|
55
|
+
export declare function sanitizeHostTerminalMessage(message: string): string;
|
|
46
56
|
export declare class CursorRetryExhaustedError extends CursorProviderError {
|
|
47
57
|
readonly attempts: number;
|
|
48
58
|
constructor(attempts: number, last: CursorProviderError);
|
package/dist/errors.js
CHANGED
|
@@ -69,10 +69,30 @@ export class CursorAuthError extends CursorProviderError {
|
|
|
69
69
|
this.name = "CursorAuthError";
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* OpenCode's SessionRetry treats several substrings in provider error messages
|
|
74
|
+
* as always-retryable — notably bare "unavailable" and "exhausted", plus a
|
|
75
|
+
* broader regex set (resource_exhausted, service unavailable, …).
|
|
76
|
+
*
|
|
77
|
+
* When this provider has already spent its Run retry budget, or has decided a
|
|
78
|
+
* replay would be unsafe, the host-facing message must not re-arm that loop.
|
|
79
|
+
* Keep the real gRPC/HTTP code on structured fields (`code`, `grpcStatus`).
|
|
80
|
+
*/
|
|
81
|
+
export function sanitizeHostTerminalMessage(message) {
|
|
82
|
+
return message
|
|
83
|
+
.replace(/\bresource[_\s-]?exhausted\b/gi, "capacity_limit")
|
|
84
|
+
.replace(/\bservice[_\s-]?unavailable\b/gi, "capacity_limit")
|
|
85
|
+
.replace(/\bunavailable\b/gi, "capacity_limit")
|
|
86
|
+
.replace(/\boverloaded\b/gi, "capacity_limit")
|
|
87
|
+
.replace(/\bretry exhausted\b/gi, "retries finished")
|
|
88
|
+
.replace(/\bexhausted\b/gi, "finished");
|
|
89
|
+
}
|
|
72
90
|
export class CursorRetryExhaustedError extends CursorProviderError {
|
|
73
91
|
attempts;
|
|
74
92
|
constructor(attempts, last) {
|
|
75
|
-
|
|
93
|
+
// Host-facing text must not contain OpenCode SessionRetry trigger words
|
|
94
|
+
// ("unavailable", "exhausted", …). Structured fields keep the real code.
|
|
95
|
+
super(`Cursor Run failed after ${attempts} attempts: ${sanitizeHostTerminalMessage(last.message)}`, {
|
|
76
96
|
origin: last.origin,
|
|
77
97
|
transient: false,
|
|
78
98
|
replaySafe: false,
|
|
@@ -184,7 +204,7 @@ export function toCursorProviderError(error, options = { replaySafe: false }) {
|
|
|
184
204
|
});
|
|
185
205
|
}
|
|
186
206
|
export function retrySuppressedError(cause, reason, attempt, maxAttempts) {
|
|
187
|
-
const subject = cause.message.replace(/^Cursor Run stream /, "Cursor stream ");
|
|
207
|
+
const subject = sanitizeHostTerminalMessage(cause.message.replace(/^Cursor Run stream /, "Cursor stream "));
|
|
188
208
|
return new CursorProviderError(`${subject} ${reason}; automatic retry unsafe (attempt ${attempt}/${maxAttempts})`, {
|
|
189
209
|
origin: cause.origin,
|
|
190
210
|
transient: false,
|
package/dist/language-model.js
CHANGED
|
@@ -26,7 +26,7 @@ import { clearPersistedConversationState, hydrateConversationState, persistConve
|
|
|
26
26
|
import { initializeConversationPersistence } from "./protocol/conversation-persistence.js";
|
|
27
27
|
import { resolveContinuationPolicy, sessionManager, } from "./session.js";
|
|
28
28
|
import { CursorAuthError, CursorLocalCancellationError, CursorProtocolError, CursorProviderError, CursorRetryExhaustedError, CursorServerError, CursorTransportError, isTransientGrpcStatus, retrySuppressedError, toCursorProviderError, } from "./errors.js";
|
|
29
|
-
import { readCache, cacheFilePath, resolveVariantParameters,
|
|
29
|
+
import { readCache, cacheFilePath, resolveVariantParameters, resolveVariantMaxMode, extractCursorVariantParameters, resolveCursorWireModelId } from "./models.js";
|
|
30
30
|
import { getOrBuildRequestContext } from "./context/frozen.js";
|
|
31
31
|
import { workspaceRootFromRequestContext } from "./context/env.js";
|
|
32
32
|
import { ensureOpencodeProjectDir, opencodeGlobalCacheDir, setHostCacheDirOverride, } from "./context/paths.js";
|
|
@@ -760,9 +760,12 @@ async function startSession(modelId, token, callOptions, options, startOptions)
|
|
|
760
760
|
maxMode: hintMaxMode,
|
|
761
761
|
picked,
|
|
762
762
|
});
|
|
763
|
-
//
|
|
764
|
-
// paramMap does not include
|
|
765
|
-
const maxMode =
|
|
763
|
+
// An explicit pick owns the context tier; otherwise honor the maxMode hint.
|
|
764
|
+
// OpenCode's variant paramMap does not include maxMode when the user selects 1m.
|
|
765
|
+
const maxMode = resolveVariantMaxMode(parameterValues, {
|
|
766
|
+
picked,
|
|
767
|
+
maxMode: hintMaxMode,
|
|
768
|
+
});
|
|
766
769
|
// Do NOT pass callOptions.abortSignal into the h2 Run stream. OpenCode aborts
|
|
767
770
|
// that signal when a turn ends with tool-calls; the Cursor stream must stay
|
|
768
771
|
// open until we write the exec results on the next doStream.
|
package/dist/models.d.ts
CHANGED
|
@@ -47,6 +47,14 @@ export declare function extractCursorVariantParameters(providerOptions: Record<s
|
|
|
47
47
|
export declare function resolveCursorWireModelId(providerOptions: Record<string, unknown> | undefined, fallback: string): string;
|
|
48
48
|
/** True when resolved params select the long-context (1m) tier. */
|
|
49
49
|
export declare function paramsImplyMaxMode(params: ModelParameterValue[]): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the request-level max_mode bit. An explicit variant pick owns the
|
|
52
|
+
* context tier; otherwise a standalone maxMode hint may select max mode.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveVariantMaxMode(params: ModelParameterValue[], opts?: {
|
|
55
|
+
picked?: ModelParameterValue[];
|
|
56
|
+
maxMode?: boolean;
|
|
57
|
+
}): boolean;
|
|
50
58
|
export declare function parseCursorContextLimit(value: unknown): number | undefined;
|
|
51
59
|
export declare function isCacheFresh(cache: ModelCache, ttlMs?: number): boolean;
|
|
52
60
|
export declare function cacheFilePath(cacheDir: string): string;
|
package/dist/models.js
CHANGED
|
@@ -181,6 +181,13 @@ export function paramsImplyMaxMode(params) {
|
|
|
181
181
|
return params.some((parameter) => parameter.id === "context" &&
|
|
182
182
|
parseCursorContextLimit(parameter.value) === 1_000_000);
|
|
183
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Resolve the request-level max_mode bit. An explicit variant pick owns the
|
|
186
|
+
* context tier; otherwise a standalone maxMode hint may select max mode.
|
|
187
|
+
*/
|
|
188
|
+
export function resolveVariantMaxMode(params, opts = {}) {
|
|
189
|
+
return paramsImplyMaxMode(params) || (opts.picked === undefined && opts.maxMode === true);
|
|
190
|
+
}
|
|
184
191
|
// Cursor encodes a model's context window as a variant parameter `id: "context"`
|
|
185
192
|
// whose value is a tier string — the same 200k / 272k / 300k / 1m the IDE's
|
|
186
193
|
// picker shows. The base tier rides on the default non-max variant; the 1M
|
|
@@ -352,10 +359,7 @@ export function resolveVariantParameters(model, opts = {}) {
|
|
|
352
359
|
pickedById.size === picked.length &&
|
|
353
360
|
v.parameterValues.every((parameter) => pickedById.get(parameter.id) === parameter.value));
|
|
354
361
|
if (exact) {
|
|
355
|
-
return buildRequestedModelParams(exact.parameterValues
|
|
356
|
-
reasoningEffort: opts.reasoningEffort,
|
|
357
|
-
maxMode: wantMax,
|
|
358
|
-
});
|
|
362
|
+
return buildRequestedModelParams(exact.parameterValues);
|
|
359
363
|
}
|
|
360
364
|
throw new CursorVariantSelectionError(`is stale for model ${JSON.stringify(model.id)}: its exact parameter tuple is unavailable`);
|
|
361
365
|
}
|
package/dist/pricing-data.d.ts
CHANGED
|
@@ -171,15 +171,15 @@ export declare const CURSOR_MODEL_COSTS: {
|
|
|
171
171
|
readonly cache_write: 0.25;
|
|
172
172
|
};
|
|
173
173
|
readonly "gpt-5.6-sol": {
|
|
174
|
-
readonly input:
|
|
175
|
-
readonly output:
|
|
176
|
-
readonly cache_read: 0.
|
|
177
|
-
readonly cache_write:
|
|
174
|
+
readonly input: 4;
|
|
175
|
+
readonly output: 20;
|
|
176
|
+
readonly cache_read: 0.4;
|
|
177
|
+
readonly cache_write: 5;
|
|
178
178
|
readonly context_over_200k: {
|
|
179
|
-
readonly input:
|
|
180
|
-
readonly output:
|
|
181
|
-
readonly cache_read:
|
|
182
|
-
readonly cache_write:
|
|
179
|
+
readonly input: 8;
|
|
180
|
+
readonly output: 20;
|
|
181
|
+
readonly cache_read: 0.8;
|
|
182
|
+
readonly cache_write: 10;
|
|
183
183
|
};
|
|
184
184
|
};
|
|
185
185
|
readonly "gpt-5.6-terra": {
|
|
@@ -360,9 +360,6 @@ export declare const CURSOR_MODEL_CAPABILITIES: {
|
|
|
360
360
|
readonly "composer-2.5": {
|
|
361
361
|
readonly supportsImages: true;
|
|
362
362
|
};
|
|
363
|
-
readonly default: {
|
|
364
|
-
readonly supportsImages: false;
|
|
365
|
-
};
|
|
366
363
|
readonly "gemini-2.5-flash": {
|
|
367
364
|
readonly supportsImages: true;
|
|
368
365
|
};
|
package/dist/pricing-data.js
CHANGED
|
@@ -171,15 +171,15 @@ export const CURSOR_MODEL_COSTS = {
|
|
|
171
171
|
"cache_write": 0.25
|
|
172
172
|
},
|
|
173
173
|
"gpt-5.6-sol": {
|
|
174
|
-
"input":
|
|
175
|
-
"output":
|
|
176
|
-
"cache_read": 0.
|
|
177
|
-
"cache_write":
|
|
174
|
+
"input": 4,
|
|
175
|
+
"output": 20,
|
|
176
|
+
"cache_read": 0.4,
|
|
177
|
+
"cache_write": 5,
|
|
178
178
|
"context_over_200k": {
|
|
179
|
-
"input":
|
|
180
|
-
"output":
|
|
181
|
-
"cache_read":
|
|
182
|
-
"cache_write":
|
|
179
|
+
"input": 8,
|
|
180
|
+
"output": 20,
|
|
181
|
+
"cache_read": 0.8,
|
|
182
|
+
"cache_write": 10
|
|
183
183
|
}
|
|
184
184
|
},
|
|
185
185
|
"gpt-5.6-terra": {
|
|
@@ -360,9 +360,6 @@ export const CURSOR_MODEL_CAPABILITIES = {
|
|
|
360
360
|
"composer-2.5": {
|
|
361
361
|
"supportsImages": true
|
|
362
362
|
},
|
|
363
|
-
"default": {
|
|
364
|
-
"supportsImages": false
|
|
365
|
-
},
|
|
366
363
|
"gemini-2.5-flash": {
|
|
367
364
|
"supportsImages": true
|
|
368
365
|
},
|