cursor-opencode-provider 0.3.1 → 0.4.0

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.
@@ -2,6 +2,7 @@ import { pathToFileURL } from "node:url";
2
2
  import { CURSOR_PROVIDER_ID } from "../shared.js";
3
3
  import { CURSOR_WIRE_MODEL_ID_KEY } from "../models.js";
4
4
  import { modelsToConfig } from "../model-config.js";
5
+ import { toOpenCode2Costs } from "../pricing.js";
5
6
  /**
6
7
  * Catalog registration for the OpenCode 2.0 plugin — the replacement for the
7
8
  * classic plugin's `config` hook.
@@ -57,19 +58,23 @@ function applyModelEntry(draft, id, entry) {
57
58
  model.modelID = wireId;
58
59
  model.providerID = CURSOR_PROVIDER_ID;
59
60
  model.name = entry.name;
61
+ const inputModalities = Array.isArray(entry.modalities?.input)
62
+ ? entry.modalities.input.filter((modality) => typeof modality === "string")
63
+ : ["text"];
64
+ const outputModalities = Array.isArray(entry.modalities?.output)
65
+ ? entry.modalities.output.filter((modality) => typeof modality === "string")
66
+ : ["text"];
60
67
  model.capabilities = {
61
68
  tools: entry.tool_call !== false,
62
- // The provider does not convert file/image prompt parts, so text-only is
63
- // the honest declaration. Revisit alongside Cursor's `supports_images`.
64
- input: ["text"],
65
- output: ["text"],
69
+ input: inputModalities,
70
+ output: outputModalities,
66
71
  };
67
72
  model.limit = { context: entry.limit.context, output: entry.limit.output };
68
73
  model.variants = variants;
69
74
  model.status = "active";
70
75
  model.enabled = true;
71
76
  model.time = { released: 0 };
72
- model.cost = [];
77
+ model.cost = toOpenCode2Costs(entry.cost);
73
78
  // Carries the default variant parameters (and the wire id) through to
74
79
  // doStream as provider options, matching the classic plugin.
75
80
  if (options)
@@ -9,7 +9,7 @@
9
9
  * we touch is more stable than pinning a moving target.
10
10
  *
11
11
  * These mirror `@opencode-ai/plugin@next` `dist/promise/*.d.ts` as of
12
- * `0.0.0-next-16281`. `test/opencode2-conformance.test-d.ts` asserts our
12
+ * `0.0.0-next-17155`. `test/opencode2-conformance.types.ts` asserts our
13
13
  * default export still satisfies the real 2.0 `Plugin` interface.
14
14
  *
15
15
  * Effect `Schema` brands (Provider.ID, Model.ID, …) are modelled as plain
@@ -53,7 +53,19 @@ export type ModelInfo2 = {
53
53
  time: {
54
54
  released: number;
55
55
  };
56
- cost: readonly unknown[];
56
+ /** OpenCode 2.0 tiered $/1M rates; empty when Cursor does not publish a price. */
57
+ cost: readonly {
58
+ tier?: {
59
+ type: "context";
60
+ size: number;
61
+ };
62
+ input: number;
63
+ output: number;
64
+ cache: {
65
+ read: number;
66
+ write: number;
67
+ };
68
+ }[];
57
69
  status: "alpha" | "beta" | "deprecated" | "active";
58
70
  enabled: boolean;
59
71
  limit: {
@@ -216,13 +228,19 @@ export type ToolDefinition = {
216
228
  export type ToolDraft = {
217
229
  add(tool: ToolDefinition): void;
218
230
  };
219
- export type ToolHookBase = {
231
+ export type ToolHookBaseFields = {
220
232
  readonly tool: string;
221
233
  readonly sessionID: string;
222
234
  readonly agent: string;
223
235
  readonly messageID: string;
224
- readonly callID: string;
225
236
  };
237
+ export type ToolHookBase = ToolHookBaseFields & ({
238
+ readonly id: string;
239
+ readonly callID?: string;
240
+ } | {
241
+ readonly id?: string;
242
+ readonly callID: string;
243
+ });
226
244
  export type ToolHooks = {
227
245
  readonly "execute.before": ToolHookBase & {
228
246
  input: unknown;
@@ -9,7 +9,7 @@
9
9
  * we touch is more stable than pinning a moving target.
10
10
  *
11
11
  * These mirror `@opencode-ai/plugin@next` `dist/promise/*.d.ts` as of
12
- * `0.0.0-next-16281`. `test/opencode2-conformance.test-d.ts` asserts our
12
+ * `0.0.0-next-17155`. `test/opencode2-conformance.types.ts` asserts our
13
13
  * default export still satisfies the real 2.0 `Plugin` interface.
14
14
  *
15
15
  * Effect `Schema` brands (Provider.ID, Model.ID, …) are modelled as plain
@@ -35,6 +35,12 @@ async function loadModels(cacheDir, accessToken) {
35
35
  // Preserve offline / stale-cache behavior rather than emptying the picker.
36
36
  return cached?.models ?? [];
37
37
  }
38
+ function toolExecutionID(event) {
39
+ const id = event.id ?? event.callID;
40
+ if (!id)
41
+ throw new Error("OpenCode 2.0 tool hook did not provide an execution id");
42
+ return id;
43
+ }
38
44
  const plugin = {
39
45
  id: "cursor.provider",
40
46
  setup: async (ctx) => {
@@ -142,29 +148,31 @@ const plugin = {
142
148
  await track(ctx.tool.hook("execute.before", (event) => {
143
149
  if (event.tool !== "bash")
144
150
  return;
151
+ const executionID = toolExecutionID(event);
145
152
  // No `shell.env` in 2.0 → always use the wrapper-file command form.
146
- prepareCursorShellArgs(event.callID, event.input, {
153
+ prepareCursorShellArgs(executionID, event.input, {
147
154
  preferWrapperCommand: true,
148
155
  });
149
156
  }));
150
157
  await track(ctx.tool.hook("execute.after", (event) => {
151
158
  if (event.tool !== "bash")
152
159
  return;
160
+ const executionID = toolExecutionID(event);
153
161
  try {
154
162
  if (event.status !== "completed")
155
163
  return;
156
164
  const result = event.result;
157
- result.title = cursorShellOriginalCommand(event.callID) ?? result.title;
158
- result.output = captureCursorShellResult(event.callID, result.output, result.metadata);
165
+ result.title = cursorShellOriginalCommand(executionID) ?? result.title;
166
+ result.output = captureCursorShellResult(executionID, result.output, result.metadata);
159
167
  if (result.metadata && typeof result.metadata === "object") {
160
168
  const metadata = result.metadata;
161
169
  if (typeof metadata.output === "string") {
162
- metadata.output = sanitizeRegisteredCursorShellOutput(event.callID, metadata.output);
170
+ metadata.output = sanitizeRegisteredCursorShellOutput(executionID, metadata.output);
163
171
  }
164
172
  }
165
173
  }
166
174
  finally {
167
- releaseCursorShellEnv(event.callID);
175
+ releaseCursorShellEnv(executionID);
168
176
  }
169
177
  }));
170
178
  // ── Compaction marker + session directory ───────────────────────────────
@@ -0,0 +1,414 @@
1
+ /**
2
+ * AUTO-GENERATED by scripts/generate-cursor-pricing.ts — do not edit by hand.
3
+ * Pricing source: https://cursor.com/docs/models-and-pricing.md
4
+ * Context source: https://cursor.com/docs.md
5
+ * Regenerate: bun run generate:pricing
6
+ */
7
+ export declare const CURSOR_PRICING_SOURCE: "https://cursor.com/docs/models-and-pricing.md";
8
+ export declare const CURSOR_CONTEXT_SOURCE: "https://cursor.com/docs.md";
9
+ export declare const CURSOR_MODEL_COSTS: {
10
+ readonly "claude-fable-5": {
11
+ readonly input: 10;
12
+ readonly output: 50;
13
+ readonly cache_read: 1;
14
+ readonly cache_write: 12.5;
15
+ };
16
+ readonly "claude-haiku-4-5": {
17
+ readonly input: 1;
18
+ readonly output: 5;
19
+ readonly cache_read: 0.1;
20
+ readonly cache_write: 1.25;
21
+ };
22
+ readonly "claude-opus-4-5": {
23
+ readonly input: 5;
24
+ readonly output: 25;
25
+ readonly cache_read: 0.5;
26
+ readonly cache_write: 6.25;
27
+ };
28
+ readonly "claude-opus-4-6": {
29
+ readonly input: 5;
30
+ readonly output: 25;
31
+ readonly cache_read: 0.5;
32
+ readonly cache_write: 6.25;
33
+ };
34
+ readonly "claude-opus-4-7": {
35
+ readonly input: 5;
36
+ readonly output: 25;
37
+ readonly cache_read: 0.5;
38
+ readonly cache_write: 6.25;
39
+ };
40
+ readonly "claude-opus-4-8": {
41
+ readonly input: 5;
42
+ readonly output: 25;
43
+ readonly cache_read: 0.5;
44
+ readonly cache_write: 6.25;
45
+ };
46
+ readonly "claude-opus-5": {
47
+ readonly input: 5;
48
+ readonly output: 25;
49
+ readonly cache_read: 0.5;
50
+ readonly cache_write: 6.25;
51
+ };
52
+ readonly "claude-sonnet-4": {
53
+ readonly input: 3;
54
+ readonly output: 15;
55
+ readonly cache_read: 0.3;
56
+ readonly cache_write: 3.75;
57
+ readonly context_over_200k: {
58
+ readonly input: 6;
59
+ readonly output: 22.5;
60
+ readonly cache_read: 0.6;
61
+ readonly cache_write: 7.5;
62
+ };
63
+ };
64
+ readonly "claude-sonnet-4-5": {
65
+ readonly input: 3;
66
+ readonly output: 15;
67
+ readonly cache_read: 0.3;
68
+ readonly cache_write: 3.75;
69
+ };
70
+ readonly "claude-sonnet-4-6": {
71
+ readonly input: 3;
72
+ readonly output: 15;
73
+ readonly cache_read: 0.3;
74
+ readonly cache_write: 3.75;
75
+ };
76
+ readonly "claude-sonnet-5": {
77
+ readonly input: 3;
78
+ readonly output: 15;
79
+ readonly cache_read: 0.3;
80
+ readonly cache_write: 3.75;
81
+ };
82
+ readonly "gemini-2.5-flash": {
83
+ readonly input: 0.3;
84
+ readonly output: 2.5;
85
+ readonly cache_read: 0.03;
86
+ };
87
+ readonly "gemini-3-flash": {
88
+ readonly input: 0.5;
89
+ readonly output: 3;
90
+ readonly cache_read: 0.05;
91
+ };
92
+ readonly "gemini-3.1-pro": {
93
+ readonly input: 2;
94
+ readonly output: 12;
95
+ readonly cache_read: 0.2;
96
+ };
97
+ readonly "gemini-3.5-flash": {
98
+ readonly input: 1.5;
99
+ readonly output: 9;
100
+ readonly cache_read: 0.15;
101
+ };
102
+ readonly "gemini-3.6-flash": {
103
+ readonly input: 1.5;
104
+ readonly output: 7.5;
105
+ readonly cache_read: 0.15;
106
+ };
107
+ readonly "glm-5.2": {
108
+ readonly input: 1.4;
109
+ readonly output: 4.4;
110
+ readonly cache_read: 0.26;
111
+ };
112
+ readonly "gpt-5-mini": {
113
+ readonly input: 0.25;
114
+ readonly output: 2;
115
+ readonly cache_read: 0.025;
116
+ };
117
+ readonly "gpt-5.1": {
118
+ readonly input: 1.25;
119
+ readonly output: 10;
120
+ readonly cache_read: 0.125;
121
+ };
122
+ readonly "gpt-5.2": {
123
+ readonly input: 1.75;
124
+ readonly output: 14;
125
+ readonly cache_read: 0.175;
126
+ };
127
+ readonly "gpt-5.3-codex": {
128
+ readonly input: 1.75;
129
+ readonly output: 14;
130
+ readonly cache_read: 0.175;
131
+ };
132
+ readonly "gpt-5.4": {
133
+ readonly input: 2.5;
134
+ readonly output: 15;
135
+ readonly cache_read: 0.25;
136
+ readonly context_over_200k: {
137
+ readonly input: 5;
138
+ readonly output: 15;
139
+ readonly cache_read: 0.5;
140
+ };
141
+ };
142
+ readonly "gpt-5.4-mini": {
143
+ readonly input: 0.75;
144
+ readonly output: 4.5;
145
+ readonly cache_read: 0.075;
146
+ };
147
+ readonly "gpt-5.4-nano": {
148
+ readonly input: 0.2;
149
+ readonly output: 1.25;
150
+ readonly cache_read: 0.02;
151
+ };
152
+ readonly "gpt-5.5": {
153
+ readonly input: 5;
154
+ readonly output: 30;
155
+ readonly cache_read: 0.5;
156
+ readonly context_over_200k: {
157
+ readonly input: 10;
158
+ readonly output: 30;
159
+ readonly cache_read: 1;
160
+ };
161
+ };
162
+ readonly "gpt-5.6-luna": {
163
+ readonly input: 0.2;
164
+ readonly output: 1.2;
165
+ readonly cache_read: 0.02;
166
+ readonly cache_write: 0.25;
167
+ };
168
+ readonly "gpt-5.6-sol": {
169
+ readonly input: 5;
170
+ readonly output: 30;
171
+ readonly cache_read: 0.5;
172
+ readonly cache_write: 6.25;
173
+ readonly context_over_200k: {
174
+ readonly input: 10;
175
+ readonly output: 30;
176
+ readonly cache_read: 1;
177
+ readonly cache_write: 12.5;
178
+ };
179
+ };
180
+ readonly "gpt-5.6-terra": {
181
+ readonly input: 2;
182
+ readonly output: 12;
183
+ readonly cache_read: 0.2;
184
+ readonly cache_write: 2.5;
185
+ };
186
+ readonly "kimi-k2.7-code": {
187
+ readonly input: 0.95;
188
+ readonly output: 4;
189
+ readonly cache_read: 0.19;
190
+ };
191
+ readonly "kimi-k3": {
192
+ readonly input: 3;
193
+ readonly output: 15;
194
+ readonly cache_read: 0.3;
195
+ };
196
+ };
197
+ export declare const CURSOR_MODEL_CONTEXTS: {
198
+ readonly "claude-fable-5": {
199
+ readonly maxContext: 300000;
200
+ readonly maxContextForMaxMode: 1000000;
201
+ };
202
+ readonly "claude-haiku-4-5": {
203
+ readonly maxContext: 200000;
204
+ };
205
+ readonly "claude-opus-4-5": {
206
+ readonly maxContext: 200000;
207
+ readonly maxContextForMaxMode: 200000;
208
+ };
209
+ readonly "claude-opus-4-6": {
210
+ readonly maxContext: 200000;
211
+ readonly maxContextForMaxMode: 1000000;
212
+ };
213
+ readonly "claude-opus-4-7": {
214
+ readonly maxContext: 300000;
215
+ readonly maxContextForMaxMode: 1000000;
216
+ };
217
+ readonly "claude-opus-4-8": {
218
+ readonly maxContext: 300000;
219
+ readonly maxContextForMaxMode: 1000000;
220
+ };
221
+ readonly "claude-opus-5": {
222
+ readonly maxContext: 300000;
223
+ readonly maxContextForMaxMode: 1000000;
224
+ };
225
+ readonly "claude-sonnet-4": {
226
+ readonly maxContext: 200000;
227
+ readonly maxContextForMaxMode: 1000000;
228
+ };
229
+ readonly "claude-sonnet-4-5": {
230
+ readonly maxContext: 200000;
231
+ readonly maxContextForMaxMode: 1000000;
232
+ };
233
+ readonly "claude-sonnet-4-6": {
234
+ readonly maxContext: 200000;
235
+ readonly maxContextForMaxMode: 1000000;
236
+ };
237
+ readonly "claude-sonnet-5": {
238
+ readonly maxContext: 200000;
239
+ readonly maxContextForMaxMode: 1000000;
240
+ };
241
+ readonly "composer-2.5": {
242
+ readonly maxContext: 200000;
243
+ };
244
+ readonly "gemini-2.5-flash": {
245
+ readonly maxContext: 200000;
246
+ readonly maxContextForMaxMode: 1000000;
247
+ };
248
+ readonly "gemini-3-flash": {
249
+ readonly maxContext: 200000;
250
+ readonly maxContextForMaxMode: 1000000;
251
+ };
252
+ readonly "gemini-3.1-pro": {
253
+ readonly maxContext: 200000;
254
+ readonly maxContextForMaxMode: 1000000;
255
+ };
256
+ readonly "gemini-3.5-flash": {
257
+ readonly maxContext: 200000;
258
+ readonly maxContextForMaxMode: 1000000;
259
+ };
260
+ readonly "gemini-3.6-flash": {
261
+ readonly maxContext: 200000;
262
+ readonly maxContextForMaxMode: 1000000;
263
+ };
264
+ readonly "glm-5.2": {
265
+ readonly maxContext: 200000;
266
+ };
267
+ readonly "gpt-5-mini": {
268
+ readonly maxContext: 272000;
269
+ };
270
+ readonly "gpt-5.1": {
271
+ readonly maxContext: 272000;
272
+ };
273
+ readonly "gpt-5.2": {
274
+ readonly maxContext: 272000;
275
+ };
276
+ readonly "gpt-5.3-codex": {
277
+ readonly maxContext: 272000;
278
+ };
279
+ readonly "gpt-5.4": {
280
+ readonly maxContext: 272000;
281
+ readonly maxContextForMaxMode: 1000000;
282
+ };
283
+ readonly "gpt-5.4-mini": {
284
+ readonly maxContext: 272000;
285
+ };
286
+ readonly "gpt-5.4-nano": {
287
+ readonly maxContext: 272000;
288
+ };
289
+ readonly "gpt-5.5": {
290
+ readonly maxContext: 272000;
291
+ readonly maxContextForMaxMode: 1000000;
292
+ };
293
+ readonly "gpt-5.6-luna": {
294
+ readonly maxContext: 272000;
295
+ };
296
+ readonly "gpt-5.6-sol": {
297
+ readonly maxContext: 272000;
298
+ readonly maxContextForMaxMode: 1000000;
299
+ };
300
+ readonly "gpt-5.6-terra": {
301
+ readonly maxContext: 272000;
302
+ };
303
+ readonly "grok-4.5": {
304
+ readonly maxContext: 256000;
305
+ };
306
+ readonly "kimi-k2.7-code": {
307
+ readonly maxContext: 262000;
308
+ };
309
+ readonly "kimi-k3": {
310
+ readonly maxContext: 200000;
311
+ readonly maxContextForMaxMode: 1000000;
312
+ };
313
+ };
314
+ export declare const CURSOR_MODEL_CAPABILITIES: {
315
+ readonly "claude-fable-5": {
316
+ readonly supportsImages: true;
317
+ };
318
+ readonly "claude-haiku-4-5": {
319
+ readonly supportsImages: true;
320
+ };
321
+ readonly "claude-opus-4-5": {
322
+ readonly supportsImages: true;
323
+ };
324
+ readonly "claude-opus-4-6": {
325
+ readonly supportsImages: true;
326
+ };
327
+ readonly "claude-opus-4-7": {
328
+ readonly supportsImages: true;
329
+ };
330
+ readonly "claude-opus-4-8": {
331
+ readonly supportsImages: true;
332
+ };
333
+ readonly "claude-opus-5": {
334
+ readonly supportsImages: true;
335
+ };
336
+ readonly "claude-sonnet-4": {
337
+ readonly supportsImages: true;
338
+ };
339
+ readonly "claude-sonnet-4-5": {
340
+ readonly supportsImages: true;
341
+ };
342
+ readonly "claude-sonnet-4-6": {
343
+ readonly supportsImages: true;
344
+ };
345
+ readonly "claude-sonnet-5": {
346
+ readonly supportsImages: true;
347
+ };
348
+ readonly "composer-2.5": {
349
+ readonly supportsImages: true;
350
+ };
351
+ readonly default: {
352
+ readonly supportsImages: false;
353
+ };
354
+ readonly "gemini-2.5-flash": {
355
+ readonly supportsImages: true;
356
+ };
357
+ readonly "gemini-3-flash": {
358
+ readonly supportsImages: true;
359
+ };
360
+ readonly "gemini-3.1-pro": {
361
+ readonly supportsImages: true;
362
+ };
363
+ readonly "gemini-3.5-flash": {
364
+ readonly supportsImages: true;
365
+ };
366
+ readonly "gemini-3.6-flash": {
367
+ readonly supportsImages: true;
368
+ };
369
+ readonly "glm-5.2": {
370
+ readonly supportsImages: false;
371
+ };
372
+ readonly "gpt-5-mini": {
373
+ readonly supportsImages: true;
374
+ };
375
+ readonly "gpt-5.1": {
376
+ readonly supportsImages: true;
377
+ };
378
+ readonly "gpt-5.2": {
379
+ readonly supportsImages: true;
380
+ };
381
+ readonly "gpt-5.3-codex": {
382
+ readonly supportsImages: true;
383
+ };
384
+ readonly "gpt-5.4": {
385
+ readonly supportsImages: true;
386
+ };
387
+ readonly "gpt-5.4-mini": {
388
+ readonly supportsImages: true;
389
+ };
390
+ readonly "gpt-5.4-nano": {
391
+ readonly supportsImages: true;
392
+ };
393
+ readonly "gpt-5.5": {
394
+ readonly supportsImages: true;
395
+ };
396
+ readonly "gpt-5.6-luna": {
397
+ readonly supportsImages: true;
398
+ };
399
+ readonly "gpt-5.6-sol": {
400
+ readonly supportsImages: true;
401
+ };
402
+ readonly "gpt-5.6-terra": {
403
+ readonly supportsImages: true;
404
+ };
405
+ readonly "grok-4.5": {
406
+ readonly supportsImages: false;
407
+ };
408
+ readonly "kimi-k2.7-code": {
409
+ readonly supportsImages: true;
410
+ };
411
+ readonly "kimi-k3": {
412
+ readonly supportsImages: true;
413
+ };
414
+ };