cursor-opencode-provider 0.8.0 → 0.8.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.
Files changed (132) hide show
  1. package/CHANGELOG.md +174 -0
  2. package/README.md +230 -44
  3. package/SECURITY.md +60 -12
  4. package/dist/activity.d.ts +73 -1
  5. package/dist/activity.js +231 -5
  6. package/dist/agent-url.js +37 -9
  7. package/dist/auth-renewal.d.ts +1 -1
  8. package/dist/auth-renewal.js +58 -26
  9. package/dist/billing.d.ts +105 -0
  10. package/dist/billing.js +212 -0
  11. package/dist/classic-tool-results.d.ts +1 -0
  12. package/dist/classic-tool-results.js +42 -0
  13. package/dist/context/build.d.ts +23 -4
  14. package/dist/context/build.js +65 -10
  15. package/dist/context/dynamic-catalog.js +6 -1
  16. package/dist/context/env.js +27 -3
  17. package/dist/context/epoch.js +9 -5
  18. package/dist/context/frozen.js +6 -2
  19. package/dist/context/git.d.ts +5 -2
  20. package/dist/context/git.js +17 -7
  21. package/dist/context/index.d.ts +2 -0
  22. package/dist/context/index.js +2 -0
  23. package/dist/context/paths.d.ts +45 -18
  24. package/dist/context/paths.js +72 -22
  25. package/dist/context/skills-bridge.d.ts +22 -0
  26. package/dist/context/skills-bridge.js +20 -0
  27. package/dist/context/skills.d.ts +34 -0
  28. package/dist/context/skills.js +211 -0
  29. package/dist/debug.js +13 -2
  30. package/dist/early-tool-rows.d.ts +60 -0
  31. package/dist/early-tool-rows.js +96 -0
  32. package/dist/errors.d.ts +49 -7
  33. package/dist/errors.js +163 -8
  34. package/dist/host-agent-mode.d.ts +53 -4
  35. package/dist/host-agent-mode.js +117 -13
  36. package/dist/host-interrupt.d.ts +12 -0
  37. package/dist/host-interrupt.js +18 -0
  38. package/dist/host-plan-file.d.ts +28 -0
  39. package/dist/host-plan-file.js +76 -0
  40. package/dist/host-steer.d.ts +71 -0
  41. package/dist/host-steer.js +232 -0
  42. package/dist/image-input.d.ts +18 -0
  43. package/dist/image-input.js +122 -30
  44. package/dist/image-save.d.ts +10 -2
  45. package/dist/image-save.js +33 -21
  46. package/dist/index.d.ts +2 -2
  47. package/dist/language-model.d.ts +149 -30
  48. package/dist/language-model.js +3127 -673
  49. package/dist/lost-request.d.ts +39 -0
  50. package/dist/lost-request.js +111 -0
  51. package/dist/model-config.d.ts +12 -1
  52. package/dist/model-config.js +78 -30
  53. package/dist/models.d.ts +26 -3
  54. package/dist/models.js +101 -45
  55. package/dist/opencode2/catalog.d.ts +1 -0
  56. package/dist/opencode2/catalog.js +2 -0
  57. package/dist/opencode2/image-save-tool.d.ts +8 -0
  58. package/dist/opencode2/image-save-tool.js +84 -0
  59. package/dist/opencode2/tool-results.d.ts +18 -0
  60. package/dist/opencode2/tool-results.js +66 -0
  61. package/dist/opencode2/types.d.ts +15 -0
  62. package/dist/parallel-step.d.ts +65 -0
  63. package/dist/parallel-step.js +102 -0
  64. package/dist/plugin-opencode2.js +233 -65
  65. package/dist/plugin.js +97 -23
  66. package/dist/pricing-data.d.ts +40 -3
  67. package/dist/pricing-data.js +40 -3
  68. package/dist/pricing.d.ts +1 -1
  69. package/dist/pricing.js +7 -4
  70. package/dist/process-shared.d.ts +14 -0
  71. package/dist/process-shared.js +18 -0
  72. package/dist/protocol/apply-patch.d.ts +25 -13
  73. package/dist/protocol/apply-patch.js +79 -30
  74. package/dist/protocol/ask-question.d.ts +12 -1
  75. package/dist/protocol/ask-question.js +77 -3
  76. package/dist/protocol/blob-store.d.ts +5 -1
  77. package/dist/protocol/blob-store.js +10 -6
  78. package/dist/protocol/client-version.js +6 -4
  79. package/dist/protocol/conversation-cache-header.d.ts +23 -0
  80. package/dist/protocol/conversation-cache-header.js +149 -0
  81. package/dist/protocol/conversation-persistence.d.ts +27 -1
  82. package/dist/protocol/conversation-persistence.js +628 -126
  83. package/dist/protocol/conversation-state.d.ts +8 -1
  84. package/dist/protocol/conversation-state.js +28 -7
  85. package/dist/protocol/create-plan.d.ts +47 -25
  86. package/dist/protocol/create-plan.js +33 -23
  87. package/dist/protocol/host-notes.d.ts +31 -0
  88. package/dist/protocol/host-notes.js +85 -0
  89. package/dist/protocol/interactions.d.ts +26 -17
  90. package/dist/protocol/interactions.js +80 -31
  91. package/dist/protocol/messages.js +164 -21
  92. package/dist/protocol/request.d.ts +40 -19
  93. package/dist/protocol/request.js +55 -28
  94. package/dist/protocol/sent-history-images.d.ts +49 -0
  95. package/dist/protocol/sent-history-images.js +102 -0
  96. package/dist/protocol/switch-mode.d.ts +66 -27
  97. package/dist/protocol/switch-mode.js +147 -73
  98. package/dist/protocol/token-details.d.ts +2 -0
  99. package/dist/protocol/token-details.js +11 -1
  100. package/dist/protocol/tool-call-bridge.d.ts +6 -0
  101. package/dist/protocol/tool-call-bridge.js +24 -0
  102. package/dist/protocol/tools.d.ts +90 -9
  103. package/dist/protocol/tools.js +543 -151
  104. package/dist/protocol/turn-provenance.d.ts +19 -4
  105. package/dist/protocol/turn-provenance.js +77 -20
  106. package/dist/replay-safety.d.ts +22 -4
  107. package/dist/replay-safety.js +86 -27
  108. package/dist/retry-budget.d.ts +21 -0
  109. package/dist/retry-budget.js +94 -0
  110. package/dist/runtime-files.d.ts +23 -0
  111. package/dist/runtime-files.js +581 -0
  112. package/dist/session.d.ts +129 -10
  113. package/dist/session.js +198 -35
  114. package/dist/shared.d.ts +1 -1
  115. package/dist/shared.js +1 -1
  116. package/dist/shell-timeout.d.ts +54 -10
  117. package/dist/shell-timeout.js +362 -92
  118. package/dist/stream-write.d.ts +20 -0
  119. package/dist/stream-write.js +87 -0
  120. package/dist/terminal-runner.d.ts +15 -0
  121. package/dist/terminal-runner.js +528 -0
  122. package/dist/todo-store.d.ts +2 -0
  123. package/dist/todo-store.js +19 -0
  124. package/dist/transport/connect.d.ts +10 -1
  125. package/dist/transport/connect.js +124 -27
  126. package/dist/usage.d.ts +37 -17
  127. package/dist/usage.js +85 -34
  128. package/package.json +5 -3
  129. package/dist/plan-execution-kickoff.d.ts +0 -57
  130. package/dist/plan-execution-kickoff.js +0 -186
  131. package/dist/protocol/thinking.d.ts +0 -15
  132. package/dist/protocol/thinking.js +0 -17
package/dist/plugin.js CHANGED
@@ -4,16 +4,18 @@ import { pollForTokens, exchangeApiKey, isExpiringSoon, generatePkceParams, gene
4
4
  import { renewSessionIfDue, resolveApiKeyToken, } from "./auth-renewal.js";
5
5
  import { CursorAuthError } from "./errors.js";
6
6
  import { errorMessage, trace } from "./debug.js";
7
+ import { normalizeClassicInterruptedResults } from "./classic-tool-results.js";
7
8
  import { readCache, discoverModels, isCacheFresh } from "./models.js";
8
9
  import { modelsToConfig } from "./model-config.js";
9
10
  import { loadClassicTools } from "./classic-tools.js";
10
11
  import { opencodeGlobalCacheDir } from "./context/paths.js";
11
12
  import { readStoredAuth } from "./context/auth-store.js";
12
13
  import { resolveAgentUrl } from "./agent-url.js";
13
- import { captureCursorShellResult, cursorShellEnvForCall, cursorShellOriginalCommand, prepareCursorShellArgs, releaseCursorShellEnv, sanitizeRegisteredCursorShellOutput, setCursorShellPath, } from "./shell-timeout.js";
14
- import { sessionActivity } from "./activity.js";
15
- import { createPlanExecutionKickoffText, setPlanExecutionKickoff, } from "./plan-execution-kickoff.js";
14
+ import { captureCursorShellResult, cursorShellEnvForCall, cursorShellOriginalCommand, cursorShellTimeoutMs, prepareCursorShellArgs, releaseCursorShellEnv, sanitizeRegisteredCursorShellOutput, setCursorShellPath, resolveCursorShellPath, } from "./shell-timeout.js";
15
+ import { applyHostPromptEvent, applyHostToolEvent, sessionActivity } from "./activity.js";
16
16
  import { dispatchHostEventBridge } from "./host-event-bridge.js";
17
+ import { createPromptHostAgentModeSwitch, setHostAgentModeSwitch } from "./host-agent-mode.js";
18
+ import { isHostPlanFileResolved, resolveHostPlanFile } from "./host-plan-file.js";
17
19
  const MODULE_URL = new URL("./index.js", import.meta.url).href;
18
20
  /**
19
21
  * Raw `crsr_` key behind an API-key login: under `metadata.apiKey` (saved at
@@ -41,29 +43,63 @@ function isSameCredential(latest, started) {
41
43
  }
42
44
  return false;
43
45
  }
46
+ /** Names of the host's own user-selectable primary agents, from `app.agents()`. */
47
+ function primaryAgentNames(response) {
48
+ const list = Array.isArray(response)
49
+ ? response
50
+ : Array.isArray(response?.data)
51
+ ? response.data
52
+ : undefined;
53
+ if (!list)
54
+ return undefined;
55
+ const names = new Set();
56
+ for (const item of list) {
57
+ if (typeof item?.name !== "string" || item.mode === "subagent" || item.hidden === true)
58
+ continue;
59
+ names.add(item.name);
60
+ }
61
+ return names;
62
+ }
63
+ /**
64
+ * Install the OpenCode 1.x host-agent switch. Returns the agent-list refresh,
65
+ * which the caller runs once the host serves requests: the list is read from
66
+ * the host's own API, and a failed read leaves SwitchMode provider-owned.
67
+ */
68
+ function installPromptHostAgentModeSwitch(input, sessionClient, promptAsync) {
69
+ const app = input.client?.app;
70
+ const agents = app?.agents;
71
+ // Nothing to install; a switch another entrypoint installed stays in place.
72
+ if (typeof promptAsync !== "function" || typeof agents !== "function")
73
+ return undefined;
74
+ let primary;
75
+ const refresh = () => agents.call(app)
76
+ .then((response) => { primary = primaryAgentNames(response) ?? primary; })
77
+ .catch((error) => trace(`host-agent-mode: agent list unavailable: ${errorMessage(error)}`));
78
+ const { apply, accepts } = createPromptHostAgentModeSwitch(async ({ sessionID, agent, text }) => {
79
+ await promptAsync.call(sessionClient, {
80
+ path: { id: sessionID },
81
+ body: { agent, parts: [{ type: "text", text, synthetic: true }] },
82
+ });
83
+ void refresh();
84
+ }, () => primary);
85
+ setHostAgentModeSwitch(apply, { resumesTurn: true, accepts });
86
+ return refresh;
87
+ }
44
88
  export async function CursorPlugin(input) {
45
89
  const cacheDir = opencodeGlobalCacheDir();
46
90
  const apiBaseURL = cursorApiBaseURL();
47
91
  const classicTools = await loadClassicTools();
48
- // Install the OpenCode plan-exit-shaped kickoff only when the OpenCode client
49
- // exposes that structural API. Without it, no synthetic kickoff is registered.
50
92
  const sessionClient = input.client?.session;
51
93
  const promptAsync = sessionClient?.promptAsync;
52
- setPlanExecutionKickoff(typeof promptAsync === "function"
53
- ? async ({ sessionID, planPath }) => {
54
- await promptAsync.call(sessionClient, {
55
- path: { id: sessionID },
56
- body: {
57
- agent: "build",
58
- parts: [{
59
- type: "text",
60
- text: createPlanExecutionKickoffText(planPath),
61
- synthetic: true,
62
- }],
63
- },
64
- });
65
- }
66
- : undefined);
94
+ // Cursor SwitchMode without an advertised plan_enter selects the host's
95
+ // primary agent the OpenCode 1.x way once its Run has ended. The OpenCode 2.0
96
+ // entrypoint replaces this with `session.switchAgent` in its own setup().
97
+ const refreshHostAgents = installPromptHostAgentModeSwitch(input, sessionClient, promptAsync);
98
+ let hostAgentsLoaded;
99
+ const sessionGet = sessionClient?.get;
100
+ const getSession = typeof sessionGet === "function"
101
+ ? (sessionID) => sessionGet.call(sessionClient, { path: { id: sessionID } })
102
+ : undefined;
67
103
  let lastPersistAttempt;
68
104
  async function persistAuth(body) {
69
105
  await input.client.auth.set({
@@ -215,6 +251,9 @@ export async function CursorPlugin(input) {
215
251
  cursor_image_save: classicTools.imageSave,
216
252
  },
217
253
  async event({ event }) {
254
+ // The SDK's event union predates these prompt events; read them untyped.
255
+ applyHostPromptEvent(sessionActivity, event.type, event.properties);
256
+ applyHostToolEvent(sessionActivity, event.type, event.properties);
218
257
  switch (event.type) {
219
258
  case "session.created":
220
259
  sessionActivity.linkSession(event.properties.info.id, event.properties.info.parentID);
@@ -231,6 +270,11 @@ export async function CursorPlugin(input) {
231
270
  break;
232
271
  case "message.part.updated":
233
272
  sessionActivity.recordActivity(event.properties.part.sessionID);
273
+ // Rejection/abort can stop the classic host before a continuation or
274
+ // execute.after. Its persisted tool state is still authoritative.
275
+ if (event.properties.part.type === "tool" && event.properties.part.state.status === "error") {
276
+ releaseCursorShellEnv(event.properties.part.callID);
277
+ }
234
278
  break;
235
279
  }
236
280
  await dispatchHostEventBridge({
@@ -240,13 +284,22 @@ export async function CursorPlugin(input) {
240
284
  serverUrl: input.serverUrl,
241
285
  });
242
286
  },
287
+ async "experimental.chat.messages.transform"(_hookInput, output) {
288
+ // The classic host retains the original array and discards hook output.
289
+ // Replace entries with request-only clones, preserving array identity.
290
+ const normalized = normalizeClassicInterruptedResults(output.messages);
291
+ for (let index = 0; index < output.messages.length; index++)
292
+ output.messages[index] = normalized[index];
293
+ },
243
294
  async "tool.execute.before"(hookInput, output) {
244
295
  if (hookInput.tool !== "bash")
245
296
  return;
246
297
  // bash/zsh retain the original display/permission command and wrap via
247
- // shell.env. sh/dash need a short wrapper-file command because their
298
+ // shell.env. sh/dash disclose the managed launcher inline because their
248
299
  // non-interactive `-c` path ignores BASH_ENV / ZDOTDIR.
249
- prepareCursorShellArgs(hookInput.callID, output.args);
300
+ prepareCursorShellArgs(hookInput.callID, output.args, {
301
+ shellPath: resolveCursorShellPath(undefined, input.directory),
302
+ });
250
303
  },
251
304
  async "shell.env"(hookInput, output) {
252
305
  const env = cursorShellEnvForCall(hookInput.callID);
@@ -267,6 +320,11 @@ export async function CursorPlugin(input) {
267
320
  if (typeof metadata.output === "string") {
268
321
  metadata.output = sanitizeRegisteredCursorShellOutput(hookInput.callID, metadata.output);
269
322
  }
323
+ // The stored output keeps only a display notice, and the saved
324
+ // outcome does not survive consumption or a restart.
325
+ const timeoutMs = cursorShellTimeoutMs(hookInput.callID);
326
+ if (timeoutMs !== undefined)
327
+ metadata.cursorShellTimeoutMs = timeoutMs;
270
328
  }
271
329
  }
272
330
  finally {
@@ -280,6 +338,22 @@ export async function CursorPlugin(input) {
280
338
  // Carry the canonical OpenCode id so an incompatible Cursor checkpoint
281
339
  // is rotated instead of resuming the prior agent's prompt.
282
340
  output.options[CURSOR_HOST_AGENT_OPTION] = hookInput.agent;
341
+ // First Cursor request: the host now serves its API, so read its agents
342
+ // before any Run can raise SwitchMode. Concurrent first requests share
343
+ // the one read; later requests find it settled.
344
+ hostAgentsLoaded ??= refreshHostAgents?.();
345
+ await hostAgentsLoaded;
346
+ // Know the session's own plan file (OpenCode's `Session.plan`) before the
347
+ // Run starts, so CreatePlan records the plan where the plan agent and
348
+ // plan_exit read it. It is fixed per session, so resolve it once.
349
+ if (hookInput.agent !== "compaction" && getSession && !isHostPlanFileResolved(hookInput.sessionID)) {
350
+ await resolveHostPlanFile({
351
+ sessionID: hookInput.sessionID,
352
+ getSession,
353
+ worktree: input.worktree || input.project?.worktree,
354
+ vcs: !!input.project?.vcs,
355
+ });
356
+ }
283
357
  // OpenCode's compaction pipeline invokes the LLM with agent="compaction".
284
358
  // Carry that stable runtime fact into LanguageModelV3 providerOptions so
285
359
  // the provider never has to guess from an empty tool list.
@@ -288,7 +362,7 @@ export async function CursorPlugin(input) {
288
362
  }
289
363
  },
290
364
  async config(cfg) {
291
- setCursorShellPath(cfg.shell);
365
+ setCursorShellPath(cfg.shell, input.directory);
292
366
  cfg.provider ??= {};
293
367
  const models = await loadModels();
294
368
  const existing = cfg.provider[CURSOR_PROVIDER_ID];
@@ -25,6 +25,12 @@ export declare const CURSOR_MODEL_COSTS: {
25
25
  readonly cache_read: 0.1;
26
26
  readonly cache_write: 1.25;
27
27
  };
28
+ readonly "claude-haiku-5-5": {
29
+ readonly input: 0.1;
30
+ readonly output: 0.5;
31
+ readonly cache_read: 0.01;
32
+ readonly cache_write: 0.125;
33
+ };
28
34
  readonly "claude-opus-4-5": {
29
35
  readonly input: 5;
30
36
  readonly output: 25;
@@ -94,7 +100,7 @@ export declare const CURSOR_MODEL_COSTS: {
94
100
  readonly "claude-sonnet-5-5": {
95
101
  readonly input: 2;
96
102
  readonly output: 10;
97
- readonly cache_read: 0.2;
103
+ readonly cache_read: 0.1;
98
104
  readonly cache_write: 2.5;
99
105
  };
100
106
  readonly "composer-2.5": {
@@ -134,14 +140,19 @@ export declare const CURSOR_MODEL_COSTS: {
134
140
  };
135
141
  readonly "gemini-3.7-flash": {
136
142
  readonly input: 0.75;
137
- readonly output: 3.5;
143
+ readonly output: 3.75;
138
144
  readonly cache_read: 0.075;
139
145
  };
140
146
  readonly "gemini-3.8-flash": {
141
147
  readonly input: 0.75;
142
- readonly output: 3.5;
148
+ readonly output: 3.75;
143
149
  readonly cache_read: 0.075;
144
150
  };
151
+ readonly "gemini-4-argon": {
152
+ readonly input: 2;
153
+ readonly output: 10;
154
+ readonly cache_read: 0.1;
155
+ };
145
156
  readonly "glm-5.2": {
146
157
  readonly input: 1.4;
147
158
  readonly output: 4.4;
@@ -298,6 +309,11 @@ export declare const CURSOR_MODEL_COSTS: {
298
309
  readonly output: 4.25;
299
310
  readonly cache_read: 0.15;
300
311
  };
312
+ readonly "muse-wonder-1.0": {
313
+ readonly input: 4;
314
+ readonly output: 20;
315
+ readonly cache_read: 0.2;
316
+ };
301
317
  };
302
318
  export declare const CURSOR_MODEL_CONTEXTS: {
303
319
  readonly "claude-fable-5": {
@@ -311,6 +327,10 @@ export declare const CURSOR_MODEL_CONTEXTS: {
311
327
  readonly "claude-haiku-4-5": {
312
328
  readonly maxContext: 200000;
313
329
  };
330
+ readonly "claude-haiku-5-5": {
331
+ readonly maxContext: 300000;
332
+ readonly maxContextForMaxMode: 1000000;
333
+ };
314
334
  readonly "claude-opus-4-5": {
315
335
  readonly maxContext: 200000;
316
336
  readonly maxContextForMaxMode: 200000;
@@ -386,6 +406,10 @@ export declare const CURSOR_MODEL_CONTEXTS: {
386
406
  readonly maxContext: 200000;
387
407
  readonly maxContextForMaxMode: 1000000;
388
408
  };
409
+ readonly "gemini-4-argon": {
410
+ readonly maxContext: 200000;
411
+ readonly maxContextForMaxMode: 1000000;
412
+ };
389
413
  readonly "glm-5.2": {
390
414
  readonly maxContext: 200000;
391
415
  };
@@ -454,6 +478,10 @@ export declare const CURSOR_MODEL_CONTEXTS: {
454
478
  readonly maxContext: 300000;
455
479
  readonly maxContextForMaxMode: 1000000;
456
480
  };
481
+ readonly "muse-wonder-1.0": {
482
+ readonly maxContext: 300000;
483
+ readonly maxContextForMaxMode: 1000000;
484
+ };
457
485
  };
458
486
  export declare const CURSOR_MODEL_CAPABILITIES: {
459
487
  readonly "claude-fable-5": {
@@ -465,6 +493,9 @@ export declare const CURSOR_MODEL_CAPABILITIES: {
465
493
  readonly "claude-haiku-4-5": {
466
494
  readonly supportsImages: true;
467
495
  };
496
+ readonly "claude-haiku-5-5": {
497
+ readonly supportsImages: true;
498
+ };
468
499
  readonly "claude-opus-4-5": {
469
500
  readonly supportsImages: true;
470
501
  };
@@ -522,6 +553,9 @@ export declare const CURSOR_MODEL_CAPABILITIES: {
522
553
  readonly "gemini-3.8-flash": {
523
554
  readonly supportsImages: true;
524
555
  };
556
+ readonly "gemini-4-argon": {
557
+ readonly supportsImages: true;
558
+ };
525
559
  readonly "glm-5.2": {
526
560
  readonly supportsImages: false;
527
561
  };
@@ -582,4 +616,7 @@ export declare const CURSOR_MODEL_CAPABILITIES: {
582
616
  readonly "muse-spark-1.3": {
583
617
  readonly supportsImages: true;
584
618
  };
619
+ readonly "muse-wonder-1.0": {
620
+ readonly supportsImages: true;
621
+ };
585
622
  };
@@ -25,6 +25,12 @@ export const CURSOR_MODEL_COSTS = {
25
25
  "cache_read": 0.1,
26
26
  "cache_write": 1.25
27
27
  },
28
+ "claude-haiku-5-5": {
29
+ "input": 0.1,
30
+ "output": 0.5,
31
+ "cache_read": 0.01,
32
+ "cache_write": 0.125
33
+ },
28
34
  "claude-opus-4-5": {
29
35
  "input": 5,
30
36
  "output": 25,
@@ -94,7 +100,7 @@ export const CURSOR_MODEL_COSTS = {
94
100
  "claude-sonnet-5-5": {
95
101
  "input": 2,
96
102
  "output": 10,
97
- "cache_read": 0.2,
103
+ "cache_read": 0.1,
98
104
  "cache_write": 2.5
99
105
  },
100
106
  "composer-2.5": {
@@ -134,14 +140,19 @@ export const CURSOR_MODEL_COSTS = {
134
140
  },
135
141
  "gemini-3.7-flash": {
136
142
  "input": 0.75,
137
- "output": 3.5,
143
+ "output": 3.75,
138
144
  "cache_read": 0.075
139
145
  },
140
146
  "gemini-3.8-flash": {
141
147
  "input": 0.75,
142
- "output": 3.5,
148
+ "output": 3.75,
143
149
  "cache_read": 0.075
144
150
  },
151
+ "gemini-4-argon": {
152
+ "input": 2,
153
+ "output": 10,
154
+ "cache_read": 0.1
155
+ },
145
156
  "glm-5.2": {
146
157
  "input": 1.4,
147
158
  "output": 4.4,
@@ -297,6 +308,11 @@ export const CURSOR_MODEL_COSTS = {
297
308
  "input": 1.25,
298
309
  "output": 4.25,
299
310
  "cache_read": 0.15
311
+ },
312
+ "muse-wonder-1.0": {
313
+ "input": 4,
314
+ "output": 20,
315
+ "cache_read": 0.2
300
316
  }
301
317
  };
302
318
  export const CURSOR_MODEL_CONTEXTS = {
@@ -311,6 +327,10 @@ export const CURSOR_MODEL_CONTEXTS = {
311
327
  "claude-haiku-4-5": {
312
328
  "maxContext": 200000
313
329
  },
330
+ "claude-haiku-5-5": {
331
+ "maxContext": 300000,
332
+ "maxContextForMaxMode": 1000000
333
+ },
314
334
  "claude-opus-4-5": {
315
335
  "maxContext": 200000,
316
336
  "maxContextForMaxMode": 200000
@@ -386,6 +406,10 @@ export const CURSOR_MODEL_CONTEXTS = {
386
406
  "maxContext": 200000,
387
407
  "maxContextForMaxMode": 1000000
388
408
  },
409
+ "gemini-4-argon": {
410
+ "maxContext": 200000,
411
+ "maxContextForMaxMode": 1000000
412
+ },
389
413
  "glm-5.2": {
390
414
  "maxContext": 200000
391
415
  },
@@ -453,6 +477,10 @@ export const CURSOR_MODEL_CONTEXTS = {
453
477
  "muse-spark-1.3": {
454
478
  "maxContext": 300000,
455
479
  "maxContextForMaxMode": 1000000
480
+ },
481
+ "muse-wonder-1.0": {
482
+ "maxContext": 300000,
483
+ "maxContextForMaxMode": 1000000
456
484
  }
457
485
  };
458
486
  export const CURSOR_MODEL_CAPABILITIES = {
@@ -465,6 +493,9 @@ export const CURSOR_MODEL_CAPABILITIES = {
465
493
  "claude-haiku-4-5": {
466
494
  "supportsImages": true
467
495
  },
496
+ "claude-haiku-5-5": {
497
+ "supportsImages": true
498
+ },
468
499
  "claude-opus-4-5": {
469
500
  "supportsImages": true
470
501
  },
@@ -522,6 +553,9 @@ export const CURSOR_MODEL_CAPABILITIES = {
522
553
  "gemini-3.8-flash": {
523
554
  "supportsImages": true
524
555
  },
556
+ "gemini-4-argon": {
557
+ "supportsImages": true
558
+ },
525
559
  "glm-5.2": {
526
560
  "supportsImages": false
527
561
  },
@@ -581,5 +615,8 @@ export const CURSOR_MODEL_CAPABILITIES = {
581
615
  },
582
616
  "muse-spark-1.3": {
583
617
  "supportsImages": true
618
+ },
619
+ "muse-wonder-1.0": {
620
+ "supportsImages": true
584
621
  }
585
622
  };
package/dist/pricing.d.ts CHANGED
@@ -40,7 +40,7 @@ export declare function validateOpenCodeModelCost(value: unknown, path?: string)
40
40
  errors: string[];
41
41
  };
42
42
  export declare function isOpenCodeModelCost(value: unknown): value is OpenCodeModelCost;
43
- /** Strip synthetic OpenCode suffixes (`…-1m`, `…-1m-2`, `…-1m-fast`, …). */
43
+ /** Strip synthetic context suffixes (`…-1m`, `…-500k`, `…-500k-2-fast`, …). */
44
44
  export declare function wireModelIdForPricing(modelId: string): {
45
45
  baseId: string;
46
46
  longContextEntry: boolean;
package/dist/pricing.js CHANGED
@@ -47,9 +47,9 @@ export function validateOpenCodeModelCost(value, path = "cost") {
47
47
  export function isOpenCodeModelCost(value) {
48
48
  return validateOpenCodeModelCost(value).valid;
49
49
  }
50
- /** Strip synthetic OpenCode suffixes (`…-1m`, `…-1m-2`, `…-1m-fast`, …). */
50
+ /** Strip synthetic context suffixes (`…-1m`, `…-500k`, `…-500k-2-fast`, …). */
51
51
  export function wireModelIdForPricing(modelId) {
52
- const match = /^(.*)-1m(?:-\d+)?(-fast)?$/.exec(modelId);
52
+ const match = /^(.*)-\d+(?:\.\d+)?[km](?:-\d+)?(-fast)?$/.exec(modelId);
53
53
  if (match) {
54
54
  return { baseId: `${match[1]}${match[2] ?? ""}`, longContextEntry: true };
55
55
  }
@@ -79,8 +79,11 @@ function asMutableCost(value) {
79
79
  * Synthetic `-fast` entries are stored as their own keys.
80
80
  */
81
81
  export function getCursorModelCost(modelId) {
82
- const { baseId } = wireModelIdForPricing(modelId);
83
- const found = CURSOR_MODEL_COSTS[baseId];
82
+ const costs = CURSOR_MODEL_COSTS;
83
+ // A priced wire id wins over suffix stripping (`-<n>k` / `-<n>m` may be real).
84
+ const found = Object.hasOwn(costs, modelId)
85
+ ? costs[modelId]
86
+ : costs[wireModelIdForPricing(modelId).baseId];
84
87
  if (!found)
85
88
  return undefined;
86
89
  return asMutableCost(found);
@@ -0,0 +1,14 @@
1
+ /**
2
+ * State shared by every copy of this package in one process.
3
+ *
4
+ * OpenCode 2 imports a separate copy of a plugin's module graph for each
5
+ * location (project directory) it serves, all in one realm. Module-level state
6
+ * then exists once per copy, so per-account work such as credential renewal ran
7
+ * once per location. Keep that state here instead.
8
+ *
9
+ * Store plain data only (maps, strings, numbers, promises): every copy has its
10
+ * own classes, so an instance stored by one copy fails `instanceof` in another.
11
+ * Name keys with a version suffix (`….v1`) and bump it when the stored shape
12
+ * changes, so two package versions in one process never read each other's data.
13
+ */
14
+ export declare function processShared<T>(name: string, create: () => T): T;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * State shared by every copy of this package in one process.
3
+ *
4
+ * OpenCode 2 imports a separate copy of a plugin's module graph for each
5
+ * location (project directory) it serves, all in one realm. Module-level state
6
+ * then exists once per copy, so per-account work such as credential renewal ran
7
+ * once per location. Keep that state here instead.
8
+ *
9
+ * Store plain data only (maps, strings, numbers, promises): every copy has its
10
+ * own classes, so an instance stored by one copy fails `instanceof` in another.
11
+ * Name keys with a version suffix (`….v1`) and bump it when the stored shape
12
+ * changes, so two package versions in one process never read each other's data.
13
+ */
14
+ export function processShared(name, create) {
15
+ const key = Symbol.for(`cursor-opencode-provider.${name}`);
16
+ const registry = globalThis;
17
+ return registry[key] ??= create();
18
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Synthesis of OpenCode `apply_patch` envelopes from Cursor's native write/edit
3
- * exec requests.
2
+ * Synthesis of OpenCode `apply_patch` (2.0: `patch`) envelopes from Cursor's
3
+ * native write/edit exec requests.
4
4
  *
5
5
  * ## Why this exists
6
6
  *
@@ -36,10 +36,12 @@
36
36
  * `ctx.session.context(...)` hook exposing a mutable tool roster, and lists
37
37
  * "Select `apply_patch` versus `edit` + `write`" as work to move out of the
38
38
  * registry. Once that lands, a host-side opt-out replaces this shim.
39
- * - OpenCode 2.0 (`packages/core`) has **no** such gate: `apply_patch`, `edit`
40
- * and `write` are all registered unconditionally in `tool/builtins.ts`. This
41
- * translation is therefore 1.x-only in practice, but it is keyed off the
42
- * advertised set rather than a version check, so it simply never fires on 2.0.
39
+ * - OpenCode 2.0 (`packages/core`) keeps the gate under a new name. The tool is
40
+ * `patch` (still `{ patchText }`, same envelope format), and
41
+ * `tool/plugin/patch.ts` applies the same model-id predicate in its session
42
+ * `context`/`compaction`/`generate` hooks, deleting `edit` and `write` for GPT models and
43
+ * `patch` for everyone else. Both names are recognised here; which one the
44
+ * host offers is read from the advertised set rather than a version check.
43
45
  *
44
46
  * ## Why translation, not advertisement
45
47
  *
@@ -59,7 +61,7 @@
59
61
  * native requests arrive for every model. Those names are absent from
60
62
  * the catalog, so the request is refused before it reaches OpenCode.
61
63
  *
62
- * ## Format notes (verified against `packages/opencode/src/patch/index.ts`)
64
+ * ## Format notes (verified against `packages/opencode/src/patch/index.ts` and 2.0's `packages/util/src/patch.ts`)
63
65
  *
64
66
  * - `*** Add File:` **overwrites an existing file** — the tool's `add` case uses
65
67
  * `oldContent = ""` and never stats the target (upstream test: "adds file
@@ -71,15 +73,25 @@
71
73
  * - Every payload line is prefixed with `+`, `-`, or a space, and the parser
72
74
  * only breaks on *unprefixed* `***` / `@@`. Content lines that themselves
73
75
  * begin with `***` or `@@` therefore survive a round trip unharmed.
74
- * - `Add File` content is rejoined with `\n` and normalized to exactly one
75
- * trailing newline. A write of content with no trailing newline gains one.
76
+ * - `Add File` content is rejoined with `\n`; the tool appends a final newline
77
+ * only when nonempty content lacks one. Keep the trailing empty payload line
78
+ * so blank lines survive. Nonempty unterminated content cannot round-trip.
79
+ * - OpenCode 2's `patch` parser strips a trailing `\r` from every payload
80
+ * line, unlike 1.x `apply_patch`. Refuse that lossy encoding on `patch`.
76
81
  * - Update chunks match **whole lines** via `seekSequence`, unlike OpenCode
77
82
  * `edit`, which is substring-based. Substring edits must be expanded to line
78
83
  * boundaries before they can be expressed as a patch — see `planSubstringEdit`.
79
- * - Renames are rejected by OpenCode 2.0's implementation ("apply_patch moves
80
- * are not supported yet"), so `*** Move to:` is never emitted here.
84
+ * - Cursor's write/edit requests never rename, so `*** Move to:` is never
85
+ * emitted here.
81
86
  */
87
+ /** OpenCode 1.x name for the patch tool. */
82
88
  export declare const APPLY_PATCH_TOOL = "apply_patch";
89
+ /** OpenCode 2.0 name for the same tool (renamed from `apply_patch`). */
90
+ export declare const PATCH_TOOL = "patch";
91
+ export type HostPatchTool = typeof APPLY_PATCH_TOOL | typeof PATCH_TOOL;
92
+ /** The patch tool this host advertises in place of `edit`/`write`, if any. */
93
+ export declare function hostPatchTool(advertised: ReadonlySet<string>): HostPatchTool | undefined;
94
+ export declare function isHostPatchTool(name: string): name is HostPatchTool;
83
95
  /** One `@@` chunk: replace `oldLines` with `newLines`. */
84
96
  export type UpdateChunk = {
85
97
  oldLines: string[];
@@ -89,9 +101,9 @@ export type UpdateChunk = {
89
101
  * Whole-file write as `*** Add File:`. Valid whether or not the target exists —
90
102
  * OpenCode's add case overwrites unconditionally.
91
103
  */
92
- export declare function buildAddFilePatch(filePath: string, content: string): string;
104
+ export declare function buildAddFilePatch(filePath: string, content: string, tool?: HostPatchTool): string;
93
105
  /** Targeted replacement as `*** Update File:` with one `@@` chunk per change. */
94
- export declare function buildUpdateFilePatch(filePath: string, chunks: UpdateChunk[]): string;
106
+ export declare function buildUpdateFilePatch(filePath: string, chunks: UpdateChunk[], tool?: HostPatchTool): string;
95
107
  export type SubstringEditPlan = {
96
108
  ok: true;
97
109
  chunks: UpdateChunk[];