cursor-opencode-provider 0.6.4 → 0.6.7

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.
@@ -1,5 +1,5 @@
1
1
  import { CURSOR_VARIANT_PARAMETERS_KEY, CURSOR_WIRE_MODEL_ID_KEY, parseCursorContextLimit, } from "./models.js";
2
- import { applyCursorModelCost } from "./pricing.js";
2
+ import { applyCursorModelCost, hasCursorFastPricing } from "./pricing.js";
3
3
  import { getDocumentedCursorModelContext, resolveCursorModelSupportsImages, } from "./model-metadata.js";
4
4
  /**
5
5
  * Cursor `ModelInfo` → OpenCode model-config mapping.
@@ -109,6 +109,9 @@ function modelInfoVariants(mi, variants) {
109
109
  function isLongContextVariant(v) {
110
110
  return v.parameterValues.some((p) => p.id === "context" && parseCursorContextLimit(p.value) === 1_000_000);
111
111
  }
112
+ function isFastVariant(v) {
113
+ return v.parameterValues.some((p) => p.id === "fast" && p.value === "true");
114
+ }
112
115
  function variantsForTier(mi, tier) {
113
116
  return mi.variants.filter((v) => isLongContextVariant(v) === (tier === "long"));
114
117
  }
@@ -139,10 +142,12 @@ export function thinkingSuffixBaseNames(models) {
139
142
  }
140
143
  export function modelInfoToConfig(mi, options = {}) {
141
144
  const contextTier = options.contextTier ?? "base";
142
- const variants = variantsForTier(mi, contextTier);
145
+ const variants = options.variants ?? variantsForTier(mi, contextTier);
143
146
  let name = baseName(mi);
144
147
  if (options.thinkingSuffix)
145
148
  name += " Thinking";
149
+ if (options.fast)
150
+ name += " Fast";
146
151
  if (contextTier === "long")
147
152
  name += " 1M";
148
153
  // OpenCode's context limit is static per model entry, while Cursor's context
@@ -176,8 +181,10 @@ export function modelInfoToConfig(mi, options = {}) {
176
181
  const variantConfig = modelInfoVariants(mi, variants);
177
182
  if (variantConfig)
178
183
  config.variants = variantConfig;
179
- if (contextTier === "long") {
180
- const defaultVariant = variants.find((v) => v.isDefaultMax) ?? variants[0];
184
+ if (contextTier === "long" || options.fast) {
185
+ const defaultVariant = contextTier === "long"
186
+ ? (variants.find((v) => v.isDefaultMax) ?? variants[0])
187
+ : (variants.find((v) => v.isDefaultNonMax) ?? variants[0]);
181
188
  if (defaultVariant) {
182
189
  config.options = {
183
190
  [CURSOR_WIRE_MODEL_ID_KEY]: mi.id,
@@ -187,30 +194,87 @@ export function modelInfoToConfig(mi, options = {}) {
187
194
  }
188
195
  return config;
189
196
  }
197
+ function speedGroups(variants, splitFast) {
198
+ if (!splitFast)
199
+ return variants.length > 0 ? [{ fast: false, variants }] : [];
200
+ const slow = variants.filter((variant) => !isFastVariant(variant));
201
+ const fast = variants.filter((variant) => isFastVariant(variant));
202
+ const groups = [];
203
+ if (slow.length > 0)
204
+ groups.push({ fast: false, variants: slow });
205
+ if (fast.length > 0)
206
+ groups.push({ fast: true, variants: fast });
207
+ return groups;
208
+ }
209
+ function uniqueCatalogId(usedIds, modelId, suffix) {
210
+ let id = `${modelId}${suffix}`;
211
+ if (!usedIds.has(id)) {
212
+ usedIds.add(id);
213
+ return id;
214
+ }
215
+ let n = 2;
216
+ while (true) {
217
+ const candidate = suffix === "-fast"
218
+ ? `${modelId}-fast-${n}`
219
+ : suffix === "-1m-fast"
220
+ ? `${modelId}-1m-${n}-fast`
221
+ : `${modelId}-1m-${n}`;
222
+ if (!usedIds.has(candidate)) {
223
+ usedIds.add(candidate);
224
+ return candidate;
225
+ }
226
+ n++;
227
+ }
228
+ }
190
229
  export function modelsToConfig(models) {
191
230
  const ambiguous = thinkingSuffixBaseNames(models);
192
231
  const out = {};
193
232
  const usedIds = new Set(models.map((m) => m.id));
194
233
  for (const m of models) {
195
234
  const thinkingSuffix = !!m.supportsThinking && ambiguous.has(baseName(m));
196
- const baseVariants = variantsForTier(m, "base");
197
- const longVariants = variantsForTier(m, "long");
198
- if (baseVariants.length > 0 || longVariants.length === 0) {
199
- out[m.id] = applyCursorModelCost(m.id, modelInfoToConfig(m, { thinkingSuffix, contextTier: "base" }));
235
+ const splitFast = hasCursorFastPricing(m.id);
236
+ const groups = [];
237
+ for (const contextTier of ["base", "long"]) {
238
+ const suffixFor = (fast) => contextTier === "long" ? (fast ? "-1m-fast" : "-1m") : (fast ? "-fast" : "");
239
+ for (const group of speedGroups(variantsForTier(m, contextTier), splitFast)) {
240
+ groups.push({
241
+ contextTier,
242
+ fast: group.fast,
243
+ variants: group.variants,
244
+ suffix: suffixFor(group.fast),
245
+ });
246
+ }
200
247
  }
201
- if (longVariants.length === 0)
202
- continue;
203
- if (baseVariants.length === 0) {
204
- // No separate base tier — the wire id itself is the long-context entry.
205
- out[m.id] = applyCursorModelCost(`${m.id}-1m`, modelInfoToConfig(m, { thinkingSuffix, contextTier: "long" }));
248
+ if (groups.length === 0) {
249
+ out[m.id] = applyCursorModelCost(m.id, modelInfoToConfig(m, { thinkingSuffix, contextTier: "base" }));
206
250
  continue;
207
251
  }
208
- let longId = `${m.id}-1m`;
209
- let suffix = 2;
210
- while (usedIds.has(longId))
211
- longId = `${m.id}-1m-${suffix++}`;
212
- usedIds.add(longId);
213
- out[longId] = applyCursorModelCost(longId, modelInfoToConfig(m, { thinkingSuffix, contextTier: "long" }));
252
+ const primaryIndex = groups.findIndex((group) => group.suffix === "");
253
+ const primary = primaryIndex >= 0 ? primaryIndex : 0;
254
+ for (const [index, group] of groups.entries()) {
255
+ const catalogId = index === primary
256
+ ? m.id
257
+ : uniqueCatalogId(usedIds, m.id, group.suffix || "-fast");
258
+ const pricingId = group.fast ? `${m.id}-fast` : (group.contextTier === "long" ? `${m.id}-1m` : m.id);
259
+ const config = modelInfoToConfig(m, {
260
+ thinkingSuffix,
261
+ contextTier: group.contextTier,
262
+ variants: group.variants,
263
+ fast: group.fast,
264
+ });
265
+ if (catalogId !== m.id && !config.options) {
266
+ const defaultVariant = (group.contextTier === "long"
267
+ ? group.variants.find((v) => v.isDefaultMax)
268
+ : group.variants.find((v) => v.isDefaultNonMax)) ?? group.variants[0];
269
+ if (defaultVariant) {
270
+ config.options = {
271
+ [CURSOR_WIRE_MODEL_ID_KEY]: m.id,
272
+ [CURSOR_VARIANT_PARAMETERS_KEY]: defaultVariant.parameterValues.map((p) => ({ ...p })),
273
+ };
274
+ }
275
+ }
276
+ out[catalogId] = applyCursorModelCost(pricingId, config);
277
+ }
214
278
  }
215
279
  return out;
216
280
  }
@@ -46,9 +46,10 @@ export function applyCursorProvider(draft) {
46
46
  /** Translate one `modelsToConfig` entry into the 2.0 `Model.Info` shape. */
47
47
  function applyModelEntry(draft, id, entry) {
48
48
  const options = entry.options;
49
- // Long-context entries get a synthetic OpenCode id (`<id>-1m`) while still
50
- // addressing the same Cursor model on the wire. V1 smuggled that through
51
- // provider options; 2.0 has a first-class `modelID` for exactly this.
49
+ // Long-context and Fast entries get synthetic OpenCode ids (`<id>-1m`,
50
+ // `<id>-fast`, `<id>-1m-fast`) while still addressing the same Cursor model
51
+ // on the wire. V1 smuggled that through provider options; 2.0 has a
52
+ // first-class `modelID` for exactly this.
52
53
  const wireId = typeof options?.[CURSOR_WIRE_MODEL_ID_KEY] === "string"
53
54
  ? options[CURSOR_WIRE_MODEL_ID_KEY]
54
55
  : id;
@@ -13,6 +13,12 @@ export declare const CURSOR_MODEL_COSTS: {
13
13
  readonly cache_read: 1;
14
14
  readonly cache_write: 12.5;
15
15
  };
16
+ readonly "claude-fable-5-1": {
17
+ readonly input: 10;
18
+ readonly output: 50;
19
+ readonly cache_read: 0.25;
20
+ readonly cache_write: 12.5;
21
+ };
16
22
  readonly "claude-haiku-4-5": {
17
23
  readonly input: 1;
18
24
  readonly output: 5;
@@ -79,6 +85,16 @@ export declare const CURSOR_MODEL_COSTS: {
79
85
  readonly cache_read: 0.2;
80
86
  readonly cache_write: 2.5;
81
87
  };
88
+ readonly "composer-2.5": {
89
+ readonly input: 0.5;
90
+ readonly output: 2.5;
91
+ readonly cache_read: 0.2;
92
+ };
93
+ readonly "composer-2.5-fast": {
94
+ readonly input: 3;
95
+ readonly output: 15;
96
+ readonly cache_read: 0.5;
97
+ };
82
98
  readonly "gemini-2.5-flash": {
83
99
  readonly input: 0.3;
84
100
  readonly output: 2.5;
@@ -109,6 +125,11 @@ export declare const CURSOR_MODEL_COSTS: {
109
125
  readonly output: 3.5;
110
126
  readonly cache_read: 0.075;
111
127
  };
128
+ readonly "gemini-3.8-flash": {
129
+ readonly input: 0.75;
130
+ readonly output: 3.5;
131
+ readonly cache_read: 0.075;
132
+ };
112
133
  readonly "glm-5.2": {
113
134
  readonly input: 1.4;
114
135
  readonly output: 4.4;
@@ -169,6 +190,12 @@ export declare const CURSOR_MODEL_COSTS: {
169
190
  readonly output: 1.2;
170
191
  readonly cache_read: 0.02;
171
192
  readonly cache_write: 0.25;
193
+ readonly context_over_200k: {
194
+ readonly input: 0.4;
195
+ readonly output: 1.2;
196
+ readonly cache_read: 0.04;
197
+ readonly cache_write: 0.5;
198
+ };
172
199
  };
173
200
  readonly "gpt-5.6-sol": {
174
201
  readonly input: 4;
@@ -187,6 +214,32 @@ export declare const CURSOR_MODEL_COSTS: {
187
214
  readonly output: 12;
188
215
  readonly cache_read: 0.2;
189
216
  readonly cache_write: 2.5;
217
+ readonly context_over_200k: {
218
+ readonly input: 4;
219
+ readonly output: 12;
220
+ readonly cache_read: 0.4;
221
+ readonly cache_write: 5;
222
+ };
223
+ };
224
+ readonly "grok-4.5": {
225
+ readonly input: 2;
226
+ readonly output: 6;
227
+ readonly cache_read: 0.5;
228
+ };
229
+ readonly "grok-4.5-fast": {
230
+ readonly input: 4;
231
+ readonly output: 18;
232
+ readonly cache_read: 1;
233
+ };
234
+ readonly "grok-4.6": {
235
+ readonly input: 2;
236
+ readonly output: 6;
237
+ readonly cache_read: 0.5;
238
+ };
239
+ readonly "grok-4.6-fast": {
240
+ readonly input: 4;
241
+ readonly output: 12;
242
+ readonly cache_read: 1;
190
243
  };
191
244
  readonly "kimi-k2.7-code": {
192
245
  readonly input: 0.95;
@@ -198,12 +251,21 @@ export declare const CURSOR_MODEL_COSTS: {
198
251
  readonly output: 15;
199
252
  readonly cache_read: 0.3;
200
253
  };
254
+ readonly "muse-spark-1.3": {
255
+ readonly input: 1.25;
256
+ readonly output: 4.25;
257
+ readonly cache_read: 0.15;
258
+ };
201
259
  };
202
260
  export declare const CURSOR_MODEL_CONTEXTS: {
203
261
  readonly "claude-fable-5": {
204
262
  readonly maxContext: 300000;
205
263
  readonly maxContextForMaxMode: 1000000;
206
264
  };
265
+ readonly "claude-fable-5-1": {
266
+ readonly maxContext: 300000;
267
+ readonly maxContextForMaxMode: 1000000;
268
+ };
207
269
  readonly "claude-haiku-4-5": {
208
270
  readonly maxContext: 200000;
209
271
  };
@@ -270,6 +332,10 @@ export declare const CURSOR_MODEL_CONTEXTS: {
270
332
  readonly maxContext: 200000;
271
333
  readonly maxContextForMaxMode: 1000000;
272
334
  };
335
+ readonly "gemini-3.8-flash": {
336
+ readonly maxContext: 200000;
337
+ readonly maxContextForMaxMode: 1000000;
338
+ };
273
339
  readonly "glm-5.2": {
274
340
  readonly maxContext: 200000;
275
341
  };
@@ -301,6 +367,7 @@ export declare const CURSOR_MODEL_CONTEXTS: {
301
367
  };
302
368
  readonly "gpt-5.6-luna": {
303
369
  readonly maxContext: 272000;
370
+ readonly maxContextForMaxMode: 1000000;
304
371
  };
305
372
  readonly "gpt-5.6-sol": {
306
373
  readonly maxContext: 272000;
@@ -308,6 +375,7 @@ export declare const CURSOR_MODEL_CONTEXTS: {
308
375
  };
309
376
  readonly "gpt-5.6-terra": {
310
377
  readonly maxContext: 272000;
378
+ readonly maxContextForMaxMode: 1000000;
311
379
  };
312
380
  readonly "grok-4.5": {
313
381
  readonly maxContext: 256000;
@@ -322,11 +390,18 @@ export declare const CURSOR_MODEL_CONTEXTS: {
322
390
  readonly maxContext: 200000;
323
391
  readonly maxContextForMaxMode: 1000000;
324
392
  };
393
+ readonly "muse-spark-1.3": {
394
+ readonly maxContext: 300000;
395
+ readonly maxContextForMaxMode: 1000000;
396
+ };
325
397
  };
326
398
  export declare const CURSOR_MODEL_CAPABILITIES: {
327
399
  readonly "claude-fable-5": {
328
400
  readonly supportsImages: true;
329
401
  };
402
+ readonly "claude-fable-5-1": {
403
+ readonly supportsImages: true;
404
+ };
330
405
  readonly "claude-haiku-4-5": {
331
406
  readonly supportsImages: true;
332
407
  };
@@ -378,6 +453,9 @@ export declare const CURSOR_MODEL_CAPABILITIES: {
378
453
  readonly "gemini-3.7-flash": {
379
454
  readonly supportsImages: true;
380
455
  };
456
+ readonly "gemini-3.8-flash": {
457
+ readonly supportsImages: true;
458
+ };
381
459
  readonly "glm-5.2": {
382
460
  readonly supportsImages: false;
383
461
  };
@@ -426,4 +504,7 @@ export declare const CURSOR_MODEL_CAPABILITIES: {
426
504
  readonly "kimi-k3": {
427
505
  readonly supportsImages: true;
428
506
  };
507
+ readonly "muse-spark-1.3": {
508
+ readonly supportsImages: true;
509
+ };
429
510
  };
@@ -13,6 +13,12 @@ export const CURSOR_MODEL_COSTS = {
13
13
  "cache_read": 1,
14
14
  "cache_write": 12.5
15
15
  },
16
+ "claude-fable-5-1": {
17
+ "input": 10,
18
+ "output": 50,
19
+ "cache_read": 0.25,
20
+ "cache_write": 12.5
21
+ },
16
22
  "claude-haiku-4-5": {
17
23
  "input": 1,
18
24
  "output": 5,
@@ -79,6 +85,16 @@ export const CURSOR_MODEL_COSTS = {
79
85
  "cache_read": 0.2,
80
86
  "cache_write": 2.5
81
87
  },
88
+ "composer-2.5": {
89
+ "input": 0.5,
90
+ "output": 2.5,
91
+ "cache_read": 0.2
92
+ },
93
+ "composer-2.5-fast": {
94
+ "input": 3,
95
+ "output": 15,
96
+ "cache_read": 0.5
97
+ },
82
98
  "gemini-2.5-flash": {
83
99
  "input": 0.3,
84
100
  "output": 2.5,
@@ -109,6 +125,11 @@ export const CURSOR_MODEL_COSTS = {
109
125
  "output": 3.5,
110
126
  "cache_read": 0.075
111
127
  },
128
+ "gemini-3.8-flash": {
129
+ "input": 0.75,
130
+ "output": 3.5,
131
+ "cache_read": 0.075
132
+ },
112
133
  "glm-5.2": {
113
134
  "input": 1.4,
114
135
  "output": 4.4,
@@ -168,7 +189,13 @@ export const CURSOR_MODEL_COSTS = {
168
189
  "input": 0.2,
169
190
  "output": 1.2,
170
191
  "cache_read": 0.02,
171
- "cache_write": 0.25
192
+ "cache_write": 0.25,
193
+ "context_over_200k": {
194
+ "input": 0.4,
195
+ "output": 1.2,
196
+ "cache_read": 0.04,
197
+ "cache_write": 0.5
198
+ }
172
199
  },
173
200
  "gpt-5.6-sol": {
174
201
  "input": 4,
@@ -186,7 +213,33 @@ export const CURSOR_MODEL_COSTS = {
186
213
  "input": 2,
187
214
  "output": 12,
188
215
  "cache_read": 0.2,
189
- "cache_write": 2.5
216
+ "cache_write": 2.5,
217
+ "context_over_200k": {
218
+ "input": 4,
219
+ "output": 12,
220
+ "cache_read": 0.4,
221
+ "cache_write": 5
222
+ }
223
+ },
224
+ "grok-4.5": {
225
+ "input": 2,
226
+ "output": 6,
227
+ "cache_read": 0.5
228
+ },
229
+ "grok-4.5-fast": {
230
+ "input": 4,
231
+ "output": 18,
232
+ "cache_read": 1
233
+ },
234
+ "grok-4.6": {
235
+ "input": 2,
236
+ "output": 6,
237
+ "cache_read": 0.5
238
+ },
239
+ "grok-4.6-fast": {
240
+ "input": 4,
241
+ "output": 12,
242
+ "cache_read": 1
190
243
  },
191
244
  "kimi-k2.7-code": {
192
245
  "input": 0.95,
@@ -197,6 +250,11 @@ export const CURSOR_MODEL_COSTS = {
197
250
  "input": 3,
198
251
  "output": 15,
199
252
  "cache_read": 0.3
253
+ },
254
+ "muse-spark-1.3": {
255
+ "input": 1.25,
256
+ "output": 4.25,
257
+ "cache_read": 0.15
200
258
  }
201
259
  };
202
260
  export const CURSOR_MODEL_CONTEXTS = {
@@ -204,6 +262,10 @@ export const CURSOR_MODEL_CONTEXTS = {
204
262
  "maxContext": 300000,
205
263
  "maxContextForMaxMode": 1000000
206
264
  },
265
+ "claude-fable-5-1": {
266
+ "maxContext": 300000,
267
+ "maxContextForMaxMode": 1000000
268
+ },
207
269
  "claude-haiku-4-5": {
208
270
  "maxContext": 200000
209
271
  },
@@ -270,6 +332,10 @@ export const CURSOR_MODEL_CONTEXTS = {
270
332
  "maxContext": 200000,
271
333
  "maxContextForMaxMode": 1000000
272
334
  },
335
+ "gemini-3.8-flash": {
336
+ "maxContext": 200000,
337
+ "maxContextForMaxMode": 1000000
338
+ },
273
339
  "glm-5.2": {
274
340
  "maxContext": 200000
275
341
  },
@@ -300,14 +366,16 @@ export const CURSOR_MODEL_CONTEXTS = {
300
366
  "maxContextForMaxMode": 1000000
301
367
  },
302
368
  "gpt-5.6-luna": {
303
- "maxContext": 272000
369
+ "maxContext": 272000,
370
+ "maxContextForMaxMode": 1000000
304
371
  },
305
372
  "gpt-5.6-sol": {
306
373
  "maxContext": 272000,
307
374
  "maxContextForMaxMode": 1000000
308
375
  },
309
376
  "gpt-5.6-terra": {
310
- "maxContext": 272000
377
+ "maxContext": 272000,
378
+ "maxContextForMaxMode": 1000000
311
379
  },
312
380
  "grok-4.5": {
313
381
  "maxContext": 256000
@@ -321,12 +389,19 @@ export const CURSOR_MODEL_CONTEXTS = {
321
389
  "kimi-k3": {
322
390
  "maxContext": 200000,
323
391
  "maxContextForMaxMode": 1000000
392
+ },
393
+ "muse-spark-1.3": {
394
+ "maxContext": 300000,
395
+ "maxContextForMaxMode": 1000000
324
396
  }
325
397
  };
326
398
  export const CURSOR_MODEL_CAPABILITIES = {
327
399
  "claude-fable-5": {
328
400
  "supportsImages": true
329
401
  },
402
+ "claude-fable-5-1": {
403
+ "supportsImages": true
404
+ },
330
405
  "claude-haiku-4-5": {
331
406
  "supportsImages": true
332
407
  },
@@ -378,6 +453,9 @@ export const CURSOR_MODEL_CAPABILITIES = {
378
453
  "gemini-3.7-flash": {
379
454
  "supportsImages": true
380
455
  },
456
+ "gemini-3.8-flash": {
457
+ "supportsImages": true
458
+ },
381
459
  "glm-5.2": {
382
460
  "supportsImages": false
383
461
  },
@@ -425,5 +503,8 @@ export const CURSOR_MODEL_CAPABILITIES = {
425
503
  },
426
504
  "kimi-k3": {
427
505
  "supportsImages": true
506
+ },
507
+ "muse-spark-1.3": {
508
+ "supportsImages": true
428
509
  }
429
510
  };
package/dist/pricing.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Cursor model token pricing for OpenCode cost reporting.
3
3
  *
4
- * Rates come from Cursor's public docs (Other Models pool = provider API price).
4
+ * Rates come from Cursor's public docs (Cursor Models + Other Models tables).
5
5
  * Generated data lives in `pricing-data.ts`; regenerate with
6
6
  * `bun run generate:pricing`. Unknown / unpublished models get no `cost`.
7
7
  */
@@ -32,22 +32,28 @@ export type CursorPricingCoverage = {
32
32
  };
33
33
  /**
34
34
  * Models we expose that Cursor does not currently publish numeric rates for
35
- * on the Other Models table (Auto / first-party Cursor Models pool).
35
+ * (Auto is billed at the routed model's list price).
36
36
  */
37
- export declare const CURSOR_UNPRICED_MODEL_IDS: readonly ["default", "composer-2.5", "grok-4.5", "grok-4.6"];
37
+ export declare const CURSOR_UNPRICED_MODEL_IDS: readonly ["default"];
38
38
  export declare function validateOpenCodeModelCost(value: unknown, path?: string): {
39
39
  valid: boolean;
40
40
  errors: string[];
41
41
  };
42
42
  export declare function isOpenCodeModelCost(value: unknown): value is OpenCodeModelCost;
43
- /** Strip synthetic OpenCode long-context suffix (`…-1m`, `…-1m-2`, …). */
43
+ /** Strip synthetic OpenCode suffixes (`…-1m`, `…-1m-2`, `…-1m-fast`, …). */
44
44
  export declare function wireModelIdForPricing(modelId: string): {
45
45
  baseId: string;
46
46
  longContextEntry: boolean;
47
47
  };
48
+ /**
49
+ * True when Cursor publishes a distinct Fast rate for this wire model id.
50
+ * Catalog code uses this to split Fast variants into a `-fast` entry.
51
+ */
52
+ export declare function hasCursorFastPricing(modelId: string): boolean;
48
53
  /**
49
54
  * Look up classic OpenCode cost for a catalog / wire model id.
50
55
  * Synthetic `-1m` entries retain both base and documented long-context rates.
56
+ * Synthetic `-fast` entries are stored as their own keys.
51
57
  */
52
58
  export declare function getCursorModelCost(modelId: string): OpenCodeModelCost | undefined;
53
59
  export declare function applyCursorModelCost<T extends Record<string, unknown>>(modelId: string, entry: T): T & {
package/dist/pricing.js CHANGED
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * Cursor model token pricing for OpenCode cost reporting.
3
3
  *
4
- * Rates come from Cursor's public docs (Other Models pool = provider API price).
4
+ * Rates come from Cursor's public docs (Cursor Models + Other Models tables).
5
5
  * Generated data lives in `pricing-data.ts`; regenerate with
6
6
  * `bun run generate:pricing`. Unknown / unpublished models get no `cost`.
7
7
  */
8
8
  import { CURSOR_MODEL_COSTS } from "./pricing-data.js";
9
9
  /**
10
10
  * Models we expose that Cursor does not currently publish numeric rates for
11
- * on the Other Models table (Auto / first-party Cursor Models pool).
11
+ * (Auto is billed at the routed model's list price).
12
12
  */
13
- export const CURSOR_UNPRICED_MODEL_IDS = ["default", "composer-2.5", "grok-4.5", "grok-4.6"];
13
+ export const CURSOR_UNPRICED_MODEL_IDS = ["default"];
14
14
  const UNPRICED = new Set(CURSOR_UNPRICED_MODEL_IDS);
15
15
  function isRecord(value) {
16
16
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -47,12 +47,20 @@ export function validateOpenCodeModelCost(value, path = "cost") {
47
47
  export function isOpenCodeModelCost(value) {
48
48
  return validateOpenCodeModelCost(value).valid;
49
49
  }
50
- /** Strip synthetic OpenCode long-context suffix (`…-1m`, `…-1m-2`, …). */
50
+ /** Strip synthetic OpenCode suffixes (`…-1m`, `…-1m-2`, `…-1m-fast`, …). */
51
51
  export function wireModelIdForPricing(modelId) {
52
- const match = /^(.*)-1m(?:-(\d+))?$/.exec(modelId);
53
- if (!match)
54
- return { baseId: modelId, longContextEntry: false };
55
- return { baseId: match[1], longContextEntry: true };
52
+ const match = /^(.*)-1m(?:-\d+)?(-fast)?$/.exec(modelId);
53
+ if (match) {
54
+ return { baseId: `${match[1]}${match[2] ?? ""}`, longContextEntry: true };
55
+ }
56
+ return { baseId: modelId, longContextEntry: false };
57
+ }
58
+ /**
59
+ * True when Cursor publishes a distinct Fast rate for this wire model id.
60
+ * Catalog code uses this to split Fast variants into a `-fast` entry.
61
+ */
62
+ export function hasCursorFastPricing(modelId) {
63
+ return Object.prototype.hasOwnProperty.call(CURSOR_MODEL_COSTS, `${modelId}-fast`);
56
64
  }
57
65
  function asMutableCost(value) {
58
66
  const copy = { input: value.input, output: value.output };
@@ -68,6 +76,7 @@ function asMutableCost(value) {
68
76
  /**
69
77
  * Look up classic OpenCode cost for a catalog / wire model id.
70
78
  * Synthetic `-1m` entries retain both base and documented long-context rates.
79
+ * Synthetic `-fast` entries are stored as their own keys.
71
80
  */
72
81
  export function getCursorModelCost(modelId) {
73
82
  const { baseId } = wireModelIdForPricing(modelId);
@@ -22,3 +22,8 @@ export declare const CURSOR_EXEC_VARIANTS: readonly CursorExecVariant[];
22
22
  export declare function cursorExecVariantByRequestField(field: number): CursorExecVariant | undefined;
23
23
  export declare function cursorExecVariantByRequestName(name: string): CursorExecVariant | undefined;
24
24
  export declare function describeCursorExecVariant(field: number | undefined): string;
25
+ /**
26
+ * `ForceBackgroundShellResult` / `ForceBackgroundSubagentResult` status.
27
+ * Cursor CLI `ForceBackgroundStatus`: 0 unspecified, 1 ok, 2 error.
28
+ */
29
+ export declare const FORCE_BACKGROUND_STATUS_ERROR = 2;
@@ -1,10 +1,12 @@
1
1
  export const CURSOR_EXEC_VARIANTS = [
2
- { requestField: 2, requestName: "shell_args", resultField: 2, resultName: "shell_result", handling: "unsupported" },
2
+ { requestField: 2, requestName: "shell_args", resultField: 2, resultName: "shell_result", handling: "opencode-tool" },
3
3
  { requestField: 3, requestName: "write_args", resultField: 3, resultName: "write_result", handling: "opencode-tool" },
4
4
  { requestField: 4, requestName: "delete_args", resultField: 4, resultName: "delete_result", handling: "opencode-tool" },
5
5
  { requestField: 5, requestName: "grep_args", resultField: 5, resultName: "grep_result", handling: "opencode-tool" },
6
6
  { requestField: 7, requestName: "read_args", resultField: 7, resultName: "read_result", handling: "opencode-tool" },
7
7
  { requestField: 8, requestName: "ls_args", resultField: 8, resultName: "ls_result", handling: "opencode-tool" },
8
+ // Dedicated Cursor read_lints (#9) is not planned. OpenCode only presents
9
+ // LSP errors on write/edit/apply_patch output plus GET /lsp status / navigation.
8
10
  { requestField: 9, requestName: "diagnostics_args", resultField: 9, resultName: "diagnostics_result", handling: "unsupported" },
9
11
  { requestField: 10, requestName: "request_context_args", resultField: 10, resultName: "request_context_result", handling: "provider-control" },
10
12
  { requestField: 11, requestName: "mcp_args", resultField: 11, resultName: "mcp_result", handling: "opencode-tool" },
@@ -23,12 +25,13 @@ export const CURSOR_EXEC_VARIANTS = [
23
25
  { requestField: 31, requestName: "force_background_subagent_args", resultField: 31, resultName: "force_background_subagent_result", handling: "unsupported" },
24
26
  { requestField: 36, requestName: "mcp_state_exec_args", resultField: 36, resultName: "mcp_state_exec_result", handling: "provider-control" },
25
27
  { requestField: 37, requestName: "subagent_await_args", resultField: 37, resultName: "subagent_await_result", handling: "unsupported" },
28
+ // OpenCode has no Auto-review / `--auto-review` mode. Not planned: keep the typed soft-deny.
26
29
  { requestField: 38, requestName: "smart_mode_classifier_args", resultField: 38, resultName: "smart_mode_classifier_result", handling: "unsupported" },
27
30
  { requestField: 40, requestName: "canvas_diagnostics_args", resultField: 40, resultName: "canvas_diagnostics_result", handling: "unsupported" },
28
31
  { requestField: 41, requestName: "shell_allowlist_precheck_args", resultField: 41, resultName: "shell_allowlist_precheck_result", handling: "unsupported" },
29
32
  { requestField: 42, requestName: "mcp_allowlist_precheck_args", resultField: 42, resultName: "mcp_allowlist_precheck_result", handling: "unsupported" },
30
33
  { requestField: 43, requestName: "web_fetch_allowlist_precheck_args", resultField: 43, resultName: "web_fetch_allowlist_precheck_result", handling: "unsupported" },
31
- { requestField: 44, requestName: "git_diff_request", resultField: 44, resultName: "git_diff_response", handling: "unsupported" },
34
+ { requestField: 44, requestName: "git_diff_request", resultField: 44, resultName: "git_diff_response", handling: "provider-control" },
32
35
  { requestField: 45, requestName: "pi_read_args", resultField: 46, resultName: "pi_read_result", handling: "opencode-tool" },
33
36
  { requestField: 46, requestName: "pi_bash_args", resultField: 47, resultName: "pi_bash_result", handling: "opencode-tool" },
34
37
  { requestField: 47, requestName: "pi_edit_args", resultField: 48, resultName: "pi_edit_result", handling: "opencode-tool" },
@@ -36,6 +39,13 @@ export const CURSOR_EXEC_VARIANTS = [
36
39
  { requestField: 49, requestName: "pi_grep_args", resultField: 50, resultName: "pi_grep_result", handling: "opencode-tool" },
37
40
  { requestField: 50, requestName: "pi_find_args", resultField: 51, resultName: "pi_find_result", handling: "opencode-tool" },
38
41
  { requestField: 51, requestName: "pi_ls_args", resultField: 52, resultName: "pi_ls_result", handling: "opencode-tool" },
42
+ // Mini-SWE request #52 pairs with ShellResult at #55 (Pi-style offset; #55
43
+ // request is accept_hook_additional_contexts, not a variant).
44
+ // Mini-SWE / SWE-agent bash is not planned (same surface as Auto-review). Keep the typed soft-deny.
45
+ { requestField: 52, requestName: "mini_swe_agent_bash_args", resultField: 55, resultName: "mini_swe_agent_bash_result", handling: "unsupported" },
46
+ { requestField: 53, requestName: "conversation_search_args", resultField: 53, resultName: "conversation_search_result", handling: "unsupported" },
47
+ { requestField: 54, requestName: "agent_store_conflict_args", resultField: 54, resultName: "agent_store_conflict_result", handling: "unsupported" },
48
+ { requestField: 56, requestName: "adopt_args", resultField: 56, resultName: "adopt_result", handling: "unsupported" },
39
49
  ];
40
50
  const BY_REQUEST_FIELD = new Map(CURSOR_EXEC_VARIANTS.map((variant) => [variant.requestField, variant]));
41
51
  const BY_REQUEST_NAME = new Map(CURSOR_EXEC_VARIANTS.map((variant) => [variant.requestName, variant]));
@@ -53,3 +63,8 @@ export function describeCursorExecVariant(field) {
53
63
  return `unknown request field #${field}`;
54
64
  return `${variant.requestName} (request field #${variant.requestField}, expected result ${variant.resultName} field #${variant.resultField}, handling=${variant.handling})`;
55
65
  }
66
+ /**
67
+ * `ForceBackgroundShellResult` / `ForceBackgroundSubagentResult` status.
68
+ * Cursor CLI `ForceBackgroundStatus`: 0 unspecified, 1 ok, 2 error.
69
+ */
70
+ export const FORCE_BACKGROUND_STATUS_ERROR = 2;