cursor-opencode-provider 0.6.1 → 0.6.3
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 +4 -1
- package/dist/usage.d.ts +2 -0
- package/dist/usage.js +13 -1
- 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
|
@@ -1707,7 +1707,10 @@ export async function pump(session, controller, ids, abortSignal) {
|
|
|
1707
1707
|
const counters = te ? cursorUsageCountersFromTurnEnded(te) : undefined;
|
|
1708
1708
|
const usage = settledUsage ?? (te
|
|
1709
1709
|
? tokenDetails
|
|
1710
|
-
? buildLanguageModelV3UsageFromCounters(counters, {
|
|
1710
|
+
? buildLanguageModelV3UsageFromCounters(counters, {
|
|
1711
|
+
contextTotalTokens: tokenDetails.usedTokens,
|
|
1712
|
+
priorContextTokens: session.cacheDiagnostics?.priorTokenDetails?.usedTokens,
|
|
1713
|
+
})
|
|
1711
1714
|
: emptyLanguageModelV3Usage()
|
|
1712
1715
|
: emptyLanguageModelV3Usage());
|
|
1713
1716
|
const providerMetadata = te
|
package/dist/usage.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export type CursorUsageCounters = {
|
|
|
10
10
|
export type CursorUsageOptions = {
|
|
11
11
|
/** Cursor checkpoint occupancy, including the current turn's output. */
|
|
12
12
|
contextTotalTokens?: number;
|
|
13
|
+
/** Previous turn's occupancy. When Cursor's cache read covers this window, do not dilute the hit by multi-step TurnEnded aggregates. */
|
|
14
|
+
priorContextTokens?: number;
|
|
13
15
|
};
|
|
14
16
|
export type CursorCacheDiagnosticStats = {
|
|
15
17
|
sessionKey?: string;
|
package/dist/usage.js
CHANGED
|
@@ -32,9 +32,17 @@ export function buildLanguageModelV3UsageFromCounters(counters, options = {}) {
|
|
|
32
32
|
// TurnEnded can aggregate several internal model calls, so its absolute input
|
|
33
33
|
// and cache counts can exceed the final checkpoint occupancy. Preserve the
|
|
34
34
|
// cache proportions while normalizing the partition to Cursor's context total.
|
|
35
|
-
const
|
|
35
|
+
const proportionalRead = rawInput > 0
|
|
36
36
|
? Math.min(input, Math.round(input * rawCacheRead / rawInput))
|
|
37
37
|
: 0;
|
|
38
|
+
const priorContext = options.priorContextTokens;
|
|
39
|
+
const prefixRead = typeof priorContext === "number"
|
|
40
|
+
&& Number.isFinite(priorContext)
|
|
41
|
+
&& priorContext > 0
|
|
42
|
+
&& rawCacheRead >= priorContext
|
|
43
|
+
? Math.min(input, Math.trunc(priorContext))
|
|
44
|
+
: 0;
|
|
45
|
+
const cacheRead = Math.min(input, Math.max(proportionalRead, prefixRead));
|
|
38
46
|
const cacheWrite = rawInput > 0
|
|
39
47
|
? Math.min(input - cacheRead, Math.round(input * rawCacheWrite / rawInput))
|
|
40
48
|
: 0;
|
|
@@ -170,9 +178,13 @@ export function formatTurnUsageValidation(counters, usage, tokenDetails, context
|
|
|
170
178
|
: undefined;
|
|
171
179
|
const rawCached = counters.cacheRead + counters.cacheWrite;
|
|
172
180
|
const sentCached = cacheRead + cacheWrite;
|
|
181
|
+
const proportionalCached = counters.inputTokens > 0 && input > 0
|
|
182
|
+
? Math.round(input * rawCached / counters.inputTokens)
|
|
183
|
+
: 0;
|
|
173
184
|
const cacheRatioMatch = tokenDetails
|
|
174
185
|
? counters.inputTokens > 0 && input > 0
|
|
175
186
|
? Math.abs(rawCached / counters.inputTokens - sentCached / input) <= 1 / input
|
|
187
|
+
|| (sentCached >= proportionalCached && sentCached <= input)
|
|
176
188
|
: rawCached === 0 && sentCached === 0
|
|
177
189
|
: undefined;
|
|
178
190
|
const status = input === inputParts &&
|