cursor-opencode-provider 0.6.4 → 0.6.6

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;
@@ -188,6 +204,26 @@ export declare const CURSOR_MODEL_COSTS: {
188
204
  readonly cache_read: 0.2;
189
205
  readonly cache_write: 2.5;
190
206
  };
207
+ readonly "grok-4.5": {
208
+ readonly input: 2;
209
+ readonly output: 6;
210
+ readonly cache_read: 0.5;
211
+ };
212
+ readonly "grok-4.5-fast": {
213
+ readonly input: 4;
214
+ readonly output: 18;
215
+ readonly cache_read: 1;
216
+ };
217
+ readonly "grok-4.6": {
218
+ readonly input: 2;
219
+ readonly output: 6;
220
+ readonly cache_read: 0.5;
221
+ };
222
+ readonly "grok-4.6-fast": {
223
+ readonly input: 4;
224
+ readonly output: 12;
225
+ readonly cache_read: 1;
226
+ };
191
227
  readonly "kimi-k2.7-code": {
192
228
  readonly input: 0.95;
193
229
  readonly output: 4;
@@ -204,6 +240,10 @@ export declare const CURSOR_MODEL_CONTEXTS: {
204
240
  readonly maxContext: 300000;
205
241
  readonly maxContextForMaxMode: 1000000;
206
242
  };
243
+ readonly "claude-fable-5-1": {
244
+ readonly maxContext: 300000;
245
+ readonly maxContextForMaxMode: 1000000;
246
+ };
207
247
  readonly "claude-haiku-4-5": {
208
248
  readonly maxContext: 200000;
209
249
  };
@@ -327,6 +367,9 @@ export declare const CURSOR_MODEL_CAPABILITIES: {
327
367
  readonly "claude-fable-5": {
328
368
  readonly supportsImages: true;
329
369
  };
370
+ readonly "claude-fable-5-1": {
371
+ readonly supportsImages: true;
372
+ };
330
373
  readonly "claude-haiku-4-5": {
331
374
  readonly supportsImages: true;
332
375
  };
@@ -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,
@@ -188,6 +204,26 @@ export const CURSOR_MODEL_COSTS = {
188
204
  "cache_read": 0.2,
189
205
  "cache_write": 2.5
190
206
  },
207
+ "grok-4.5": {
208
+ "input": 2,
209
+ "output": 6,
210
+ "cache_read": 0.5
211
+ },
212
+ "grok-4.5-fast": {
213
+ "input": 4,
214
+ "output": 18,
215
+ "cache_read": 1
216
+ },
217
+ "grok-4.6": {
218
+ "input": 2,
219
+ "output": 6,
220
+ "cache_read": 0.5
221
+ },
222
+ "grok-4.6-fast": {
223
+ "input": 4,
224
+ "output": 12,
225
+ "cache_read": 1
226
+ },
191
227
  "kimi-k2.7-code": {
192
228
  "input": 0.95,
193
229
  "output": 4,
@@ -204,6 +240,10 @@ export const CURSOR_MODEL_CONTEXTS = {
204
240
  "maxContext": 300000,
205
241
  "maxContextForMaxMode": 1000000
206
242
  },
243
+ "claude-fable-5-1": {
244
+ "maxContext": 300000,
245
+ "maxContextForMaxMode": 1000000
246
+ },
207
247
  "claude-haiku-4-5": {
208
248
  "maxContext": 200000
209
249
  },
@@ -327,6 +367,9 @@ export const CURSOR_MODEL_CAPABILITIES = {
327
367
  "claude-fable-5": {
328
368
  "supportsImages": true
329
369
  },
370
+ "claude-fable-5-1": {
371
+ "supportsImages": true
372
+ },
330
373
  "claude-haiku-4-5": {
331
374
  "supportsImages": true
332
375
  },
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;
@@ -0,0 +1,33 @@
1
+ /** aiserver.v1.GetDiffRequest.OutputFormat */
2
+ export declare const GIT_DIFF_FORMAT_UNSPECIFIED = 0;
3
+ export declare const GIT_DIFF_FORMAT_NAME_STATUS = 1;
4
+ export declare const GIT_DIFF_FORMAT_NAME_STATUS_AND_NUMSTAT = 2;
5
+ export declare const GIT_DIFF_FORMAT_FILE_DIFFS = 3;
6
+ export declare const GIT_DIFF_FORMAT_DIFFS_WITH_BEFORE_AND_AFTER = 4;
7
+ export type GitDiffChunk = {
8
+ content: string;
9
+ lines: string[];
10
+ old_start: number;
11
+ old_lines: number;
12
+ new_start: number;
13
+ new_lines: number;
14
+ };
15
+ export type GitFileDiff = {
16
+ from: string;
17
+ to: string;
18
+ chunks: GitDiffChunk[];
19
+ added: number;
20
+ removed: number;
21
+ };
22
+ /** Parse `git diff --no-color` into aiserver.v1.FileDiff rows (CLI `Oc` / `FR`). */
23
+ export declare function parseUnifiedDiff(text: string): GitFileDiff[];
24
+ export declare function executeGitDiff(request: Record<string, unknown>, workspaceRoot: string): Promise<Record<string, unknown>>;
25
+ /**
26
+ * Answer exec #44. GetDiffResponse has no error oneof — failures use
27
+ * exec_client_control_message.throw, matching Cursor CLI `class Dc`.
28
+ */
29
+ export declare function buildGitDiffExecMessages(input: {
30
+ execId: number;
31
+ request: Record<string, unknown>;
32
+ workspaceRoot: string;
33
+ }): Promise<Uint8Array[]>;