cursor-opencode-provider 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/dist/context/agents.js +1 -1
- package/dist/context/build.d.ts +2 -0
- package/dist/context/build.js +22 -10
- package/dist/context/epoch.d.ts +59 -0
- package/dist/context/epoch.js +224 -0
- package/dist/context/frozen.d.ts +4 -2
- package/dist/context/frozen.js +31 -11
- package/dist/context/index.d.ts +2 -0
- package/dist/context/index.js +2 -0
- package/dist/context/overlay.d.ts +44 -0
- package/dist/context/overlay.js +96 -0
- package/dist/debug.d.ts +9 -1
- package/dist/debug.js +46 -5
- package/dist/host-event-bridge.d.ts +22 -0
- package/dist/host-event-bridge.js +22 -0
- package/dist/image-input.js +36 -11
- package/dist/index.d.ts +3 -0
- package/dist/language-model.d.ts +51 -3
- package/dist/language-model.js +569 -140
- package/dist/model-config.js +5 -1
- package/dist/opencode2/mcp-direct.d.ts +26 -0
- package/dist/opencode2/mcp-direct.js +60 -0
- package/dist/opencode2/types.d.ts +24 -0
- package/dist/plugin-opencode2.js +24 -4
- package/dist/plugin.js +26 -15
- package/dist/pricing-data.d.ts +40 -0
- package/dist/pricing-data.js +40 -0
- package/dist/protocol/ask-question.d.ts +8 -9
- package/dist/protocol/ask-question.js +8 -9
- package/dist/protocol/conversation-bind.js +18 -8
- package/dist/protocol/create-plan.d.ts +6 -7
- package/dist/protocol/create-plan.js +4 -5
- package/dist/protocol/messages.js +16 -1
- package/dist/protocol/struct.js +1 -1
- package/dist/protocol/switch-mode.d.ts +3 -4
- package/dist/protocol/switch-mode.js +21 -12
- package/dist/protocol/tool-call-bridge.d.ts +4 -1
- package/dist/protocol/tool-call-bridge.js +27 -5
- package/dist/protocol/tools.d.ts +4 -2
- package/dist/protocol/tools.js +247 -51
- package/dist/session-directory.d.ts +23 -4
- package/dist/session-directory.js +36 -4
- package/dist/session.d.ts +23 -3
- package/dist/session.js +68 -8
- package/dist/shared.d.ts +2 -0
- package/dist/shared.js +2 -0
- package/dist/transport/connect.js +78 -10
- package/dist/transport/https-proxy.d.ts +56 -0
- package/dist/transport/https-proxy.js +348 -0
- package/dist/usage.d.ts +11 -0
- package/dist/usage.js +33 -7
- package/package.json +1 -1
package/dist/model-config.js
CHANGED
|
@@ -154,9 +154,13 @@ export function modelInfoToConfig(mi, options = {}) {
|
|
|
154
154
|
// tier is a variant parameter. Long-context choices are therefore emitted as
|
|
155
155
|
// separate OpenCode entries by modelsToConfig.
|
|
156
156
|
const documentedContext = getDocumentedCursorModelContext(mi.id);
|
|
157
|
+
// Cursor Auto (`default`) has no static maxContext on the model list, but
|
|
158
|
+
// live checkpoints report 256k for the base tier — the previous 200k
|
|
159
|
+
// fallback made the host context meter disagree with Cursor.
|
|
160
|
+
const fallbackContext = mi.id === "default" ? 256_000 : 200_000;
|
|
157
161
|
const context = contextTier === "long"
|
|
158
162
|
? (mi.maxContextForMaxMode ?? documentedContext?.maxContextForMaxMode ?? 1_000_000)
|
|
159
|
-
: (mi.maxContext ?? documentedContext?.maxContext ??
|
|
163
|
+
: (mi.maxContext ?? documentedContext?.maxContext ?? fallbackContext);
|
|
160
164
|
// OpenCode's overflow/compaction/UI use limit.context; generation and
|
|
161
165
|
// thinking budgets use limit.output. models.dev 1M peers advertise
|
|
162
166
|
// 64k–128k output — a tiny cap makes long-context sessions feel broken
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ToolDraft } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* OpenCode 2 puts an MCP server's tools in Code Mode unless that server's
|
|
4
|
+
* config sets `codemode: false` (`packages/schema/src/mcp.ts`, applied in
|
|
5
|
+
* `packages/core/src/tool/mcp.ts`). Only `options.codemode === false` joins
|
|
6
|
+
* the AI SDK catalog (`packages/core/src/tool.ts`). Cursor can call a tool
|
|
7
|
+
* only when it is in that catalog.
|
|
8
|
+
*
|
|
9
|
+
* `config.codemode` is also the remote-connection switch: while it is not
|
|
10
|
+
* false, OpenCode appends `?codemode=false` so servers that bundle their own
|
|
11
|
+
* Code Mode return individual tools (`packages/core/src/mcp/client.ts`).
|
|
12
|
+
* This module therefore leaves server config alone and clears the tool option
|
|
13
|
+
* instead. An explicit server `codemode: true` stays in Code Mode. OpenCode's
|
|
14
|
+
* own namespaced tools (the `opencode` namespace) are not MCP servers and are
|
|
15
|
+
* left alone.
|
|
16
|
+
*
|
|
17
|
+
* The tool registry is location-scoped, so this placement is shared by every
|
|
18
|
+
* provider in the process. `"codemode": true` on a server is the per-server
|
|
19
|
+
* way to keep that server inside `execute`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function mcpServerNamespace(server: string): string;
|
|
22
|
+
export declare function rememberDirectMcpNamespaces(target: Set<string>, servers: readonly (readonly [string, {
|
|
23
|
+
readonly codemode?: boolean;
|
|
24
|
+
}])[]): void;
|
|
25
|
+
/** Move tools from the recorded MCP namespaces onto the direct catalog. */
|
|
26
|
+
export declare function exposeDirectMcpTools(editor: ToolDraft, namespaces: ReadonlySet<string>): void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode 2 puts an MCP server's tools in Code Mode unless that server's
|
|
3
|
+
* config sets `codemode: false` (`packages/schema/src/mcp.ts`, applied in
|
|
4
|
+
* `packages/core/src/tool/mcp.ts`). Only `options.codemode === false` joins
|
|
5
|
+
* the AI SDK catalog (`packages/core/src/tool.ts`). Cursor can call a tool
|
|
6
|
+
* only when it is in that catalog.
|
|
7
|
+
*
|
|
8
|
+
* `config.codemode` is also the remote-connection switch: while it is not
|
|
9
|
+
* false, OpenCode appends `?codemode=false` so servers that bundle their own
|
|
10
|
+
* Code Mode return individual tools (`packages/core/src/mcp/client.ts`).
|
|
11
|
+
* This module therefore leaves server config alone and clears the tool option
|
|
12
|
+
* instead. An explicit server `codemode: true` stays in Code Mode. OpenCode's
|
|
13
|
+
* own namespaced tools (the `opencode` namespace) are not MCP servers and are
|
|
14
|
+
* left alone.
|
|
15
|
+
*
|
|
16
|
+
* The tool registry is location-scoped, so this placement is shared by every
|
|
17
|
+
* provider in the process. `"codemode": true` on a server is the per-server
|
|
18
|
+
* way to keep that server inside `execute`.
|
|
19
|
+
*/
|
|
20
|
+
export function mcpServerNamespace(server) {
|
|
21
|
+
return server.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
22
|
+
}
|
|
23
|
+
export function rememberDirectMcpNamespaces(target, servers) {
|
|
24
|
+
target.clear();
|
|
25
|
+
// The editor exposes normalized namespaces, not server ownership. Keep
|
|
26
|
+
// ambiguous namespaces in their host-selected placement: otherwise a server
|
|
27
|
+
// named `opencode` moves builtins too, or `my.docs` overrides an explicit
|
|
28
|
+
// Code Mode choice on `my_docs`. Exclusions must win regardless of order.
|
|
29
|
+
const excluded = new Set(["opencode"]);
|
|
30
|
+
for (const [name, config] of servers) {
|
|
31
|
+
const namespace = mcpServerNamespace(name);
|
|
32
|
+
if (config.codemode === true)
|
|
33
|
+
excluded.add(namespace);
|
|
34
|
+
else
|
|
35
|
+
target.add(namespace);
|
|
36
|
+
}
|
|
37
|
+
for (const namespace of excluded)
|
|
38
|
+
target.delete(namespace);
|
|
39
|
+
}
|
|
40
|
+
/** Move tools from the recorded MCP namespaces onto the direct catalog. */
|
|
41
|
+
export function exposeDirectMcpTools(editor, namespaces) {
|
|
42
|
+
if (namespaces.size === 0 || !editor.list || !editor.update)
|
|
43
|
+
return;
|
|
44
|
+
for (const tool of editor.list()) {
|
|
45
|
+
const namespace = tool.options?.namespace;
|
|
46
|
+
if (!namespace || !namespaces.has(namespace))
|
|
47
|
+
continue;
|
|
48
|
+
if (tool.options?.codemode === false)
|
|
49
|
+
continue;
|
|
50
|
+
editor.update(tool.id, (draft) => {
|
|
51
|
+
const options = {};
|
|
52
|
+
if (draft.options?.namespace !== undefined)
|
|
53
|
+
options.namespace = draft.options.namespace;
|
|
54
|
+
if (draft.options?.permission !== undefined)
|
|
55
|
+
options.permission = draft.options.permission;
|
|
56
|
+
options.codemode = false;
|
|
57
|
+
draft.options = options;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -223,6 +223,15 @@ export type ToolDraft = {
|
|
|
223
223
|
list?(): readonly (ToolDefinition & {
|
|
224
224
|
readonly id: string;
|
|
225
225
|
})[];
|
|
226
|
+
/** Host ToolEditor.update — missing ids are ignored. */
|
|
227
|
+
update?(id: string, update: (tool: {
|
|
228
|
+
options?: {
|
|
229
|
+
namespace?: string;
|
|
230
|
+
permission?: string;
|
|
231
|
+
codemode?: boolean;
|
|
232
|
+
pinned?: boolean;
|
|
233
|
+
};
|
|
234
|
+
}) => void): void;
|
|
226
235
|
};
|
|
227
236
|
export type ToolHookBaseFields = {
|
|
228
237
|
readonly tool: string;
|
|
@@ -368,6 +377,20 @@ export type WebSearchDomain = {
|
|
|
368
377
|
readonly transform: Transform<WebSearchEditor>;
|
|
369
378
|
readonly reload: () => Promise<void>;
|
|
370
379
|
};
|
|
380
|
+
/**
|
|
381
|
+
* MCP config reader this plugin uses. Server `codemode` is observed and not
|
|
382
|
+
* written: writing it also changes the remote raw-tool URL. The host
|
|
383
|
+
* `MCPEditor` is a superset.
|
|
384
|
+
*/
|
|
385
|
+
export type McpServerConfig = {
|
|
386
|
+
readonly codemode?: boolean;
|
|
387
|
+
};
|
|
388
|
+
export type McpEditor = {
|
|
389
|
+
list(): readonly (readonly [string, McpServerConfig])[];
|
|
390
|
+
};
|
|
391
|
+
export type McpDomain = {
|
|
392
|
+
readonly transform: Transform<McpEditor>;
|
|
393
|
+
};
|
|
371
394
|
export type PluginLocation = {
|
|
372
395
|
readonly directory: string;
|
|
373
396
|
};
|
|
@@ -381,6 +404,7 @@ export type PluginContext = {
|
|
|
381
404
|
readonly location?: PluginLocation;
|
|
382
405
|
readonly shell?: ShellDomain;
|
|
383
406
|
readonly websearch?: WebSearchDomain;
|
|
407
|
+
readonly mcp?: McpDomain;
|
|
384
408
|
};
|
|
385
409
|
export type Cleanup = () => Promise<void> | void;
|
|
386
410
|
export type Plugin2 = {
|
package/dist/plugin-opencode2.js
CHANGED
|
@@ -8,7 +8,9 @@ import { fetchOpenCodeWebSearchText, parseExaWebSearchResults, } from "./web-too
|
|
|
8
8
|
import { captureCursorShellResult, cursorShellEnvForCommand, prepareCursorShellArgs, releaseCursorShellEnv, sanitizeRegisteredCursorShellOutput, } from "./shell-timeout.js";
|
|
9
9
|
import { applyCursorProviderInventory, CURSOR_INTEGRATION_ID } from "./opencode2/catalog.js";
|
|
10
10
|
import { applyCursorIntegration, resolveCursorAccessToken } from "./opencode2/integration.js";
|
|
11
|
+
import { exposeDirectMcpTools, rememberDirectMcpNamespaces } from "./opencode2/mcp-direct.js";
|
|
11
12
|
import { registerTodoTools } from "./opencode2/todo-tools.js";
|
|
13
|
+
import { OPENCODE_2_TOOL_DIALECT } from "./protocol/tools.js";
|
|
12
14
|
import { clearSessionTodos } from "./todo-store.js";
|
|
13
15
|
import { markCompactionSession } from "./compaction-marker.js";
|
|
14
16
|
import { markSessionDirectory } from "./session-directory.js";
|
|
@@ -213,11 +215,14 @@ const plugin = {
|
|
|
213
215
|
...(token ? { accessToken: token } : {}),
|
|
214
216
|
// Static fallback only. This hook fires once per model/package, not
|
|
215
217
|
// per session, and 2.0 runs one daemon across many projects — the
|
|
216
|
-
// real per-request directory comes from
|
|
217
|
-
// below via `getSessionDirectory
|
|
218
|
+
// real per-request directory comes from `x-opencode-directory` and
|
|
219
|
+
// the session.context hook below via `getSessionDirectory`.
|
|
218
220
|
workspaceRoot,
|
|
219
221
|
cacheDir,
|
|
220
222
|
...event.options,
|
|
223
|
+
// Keep after `event.options` so the OC2 plugin always selects the
|
|
224
|
+
// `path`/`shell` dialect when advertised schemas are opaque.
|
|
225
|
+
defaultDialect: OPENCODE_2_TOOL_DIALECT,
|
|
221
226
|
});
|
|
222
227
|
}));
|
|
223
228
|
await track(ctx.aisdk.hook("language", (event) => {
|
|
@@ -246,12 +251,25 @@ const plugin = {
|
|
|
246
251
|
});
|
|
247
252
|
}));
|
|
248
253
|
}
|
|
254
|
+
// Namespaces whose tools belong on the direct catalog. Filled by the MCP
|
|
255
|
+
// transform (config is not written) and read when tool transforms replay,
|
|
256
|
+
// including after a later MCP discovery reload.
|
|
257
|
+
const directMcpNamespaces = new Set();
|
|
258
|
+
if (ctx.mcp) {
|
|
259
|
+
await track(ctx.mcp.transform((editor) => {
|
|
260
|
+
rememberDirectMcpNamespaces(directMcpNamespaces, editor.list());
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
249
263
|
await track(ctx.tool.transform((draft) => {
|
|
250
264
|
// OpenCode 2 dropped host todowrite/todoread. Off by default
|
|
251
265
|
// (`CURSOR_OPENCODE2_TODOS=1`/`true` force-enables). When on, register
|
|
252
266
|
// them as direct catalog tools (`codemode: false` + output schema) if
|
|
253
267
|
// the host does not already own those names. When off, register none.
|
|
254
268
|
registerTodoTools(draft);
|
|
269
|
+
// MCP tools default into Code Mode. Move every server that did not
|
|
270
|
+
// explicitly opt in onto the direct catalog so Cursor can call them
|
|
271
|
+
// by name. See `opencode2/mcp-direct.ts`.
|
|
272
|
+
exposeDirectMcpTools(draft, directMcpNamespaces);
|
|
255
273
|
}));
|
|
256
274
|
// ── Shell timeout wrapper ────────────────────────────────
|
|
257
275
|
await track(ctx.tool.hook("execute.before", (event) => {
|
|
@@ -324,8 +342,10 @@ const plugin = {
|
|
|
324
342
|
}
|
|
325
343
|
const rememberSessionDirectory = async (sessionID) => {
|
|
326
344
|
try {
|
|
327
|
-
const info = await ctx.session.get({ sessionID });
|
|
328
|
-
|
|
345
|
+
const info = (await ctx.session.get({ sessionID }));
|
|
346
|
+
// OpenCode 2.0 stable exposes a flat `directory`; older shapes nest it
|
|
347
|
+
// under `location.directory`. Prefer the flat field when both exist.
|
|
348
|
+
markSessionDirectory(sessionID, info.directory ?? info.location?.directory);
|
|
329
349
|
}
|
|
330
350
|
catch {
|
|
331
351
|
// Best effort — falls back to the static workspaceRoot above.
|
package/dist/plugin.js
CHANGED
|
@@ -17,6 +17,7 @@ import { sessionActivity } from "./activity.js";
|
|
|
17
17
|
import { createOpenCodeWebSearchTool, openCodeWebSearchTool } from "./web-search-tool.js";
|
|
18
18
|
import { createCursorImageSaveTool, cursorImageSaveTool } from "./image-save-tool.js";
|
|
19
19
|
import { createPlanExecutionKickoffText, setPlanExecutionKickoff, } from "./plan-execution-kickoff.js";
|
|
20
|
+
import { dispatchHostEventBridge } from "./host-event-bridge.js";
|
|
20
21
|
const MODULE_URL = new URL("./index.js", import.meta.url).href;
|
|
21
22
|
export async function loadClassicTools(options = {}) {
|
|
22
23
|
const configDir = options.configDirs?.[0] ?? opencodeGlobalConfigDirs()[0];
|
|
@@ -56,21 +57,25 @@ export async function CursorPlugin(input) {
|
|
|
56
57
|
const cacheDir = opencodeGlobalCacheDir();
|
|
57
58
|
const apiBaseURL = cursorApiBaseURL();
|
|
58
59
|
const classicTools = await loadClassicTools();
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
60
|
+
// Install the OpenCode plan-exit-shaped kickoff only when the OpenCode client
|
|
61
|
+
// exposes that structural API. Without it, no synthetic kickoff is registered.
|
|
62
|
+
const sessionClient = input.client?.session;
|
|
63
|
+
const promptAsync = sessionClient?.promptAsync;
|
|
64
|
+
setPlanExecutionKickoff(typeof promptAsync === "function"
|
|
65
|
+
? async ({ sessionID, planPath }) => {
|
|
66
|
+
await promptAsync.call(sessionClient, {
|
|
67
|
+
path: { id: sessionID },
|
|
68
|
+
body: {
|
|
69
|
+
agent: "build",
|
|
70
|
+
parts: [{
|
|
71
|
+
type: "text",
|
|
72
|
+
text: createPlanExecutionKickoffText(planPath),
|
|
73
|
+
synthetic: true,
|
|
74
|
+
}],
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
: undefined);
|
|
74
79
|
// Last access token successfully resolved in this plugin instance. Config's
|
|
75
80
|
// loadModels can only read OpenCode's durable store (auth.json /
|
|
76
81
|
// OPENCODE_AUTH_CONTENT); auth.loader gets live credentials via getAuth().
|
|
@@ -217,6 +222,12 @@ export async function CursorPlugin(input) {
|
|
|
217
222
|
sessionActivity.recordActivity(event.properties.part.sessionID);
|
|
218
223
|
break;
|
|
219
224
|
}
|
|
225
|
+
await dispatchHostEventBridge({
|
|
226
|
+
event,
|
|
227
|
+
client: input.client,
|
|
228
|
+
directory: input.directory,
|
|
229
|
+
serverUrl: input.serverUrl,
|
|
230
|
+
});
|
|
220
231
|
},
|
|
221
232
|
async "tool.execute.before"(hookInput, output) {
|
|
222
233
|
if (hookInput.tool !== "bash")
|
package/dist/pricing-data.d.ts
CHANGED
|
@@ -55,6 +55,12 @@ export declare const CURSOR_MODEL_COSTS: {
|
|
|
55
55
|
readonly cache_read: 0.5;
|
|
56
56
|
readonly cache_write: 6.25;
|
|
57
57
|
};
|
|
58
|
+
readonly "claude-opus-5-5": {
|
|
59
|
+
readonly input: 4;
|
|
60
|
+
readonly output: 20;
|
|
61
|
+
readonly cache_read: 0.2;
|
|
62
|
+
readonly cache_write: 5;
|
|
63
|
+
};
|
|
58
64
|
readonly "claude-sonnet-4": {
|
|
59
65
|
readonly input: 3;
|
|
60
66
|
readonly output: 15;
|
|
@@ -241,6 +247,26 @@ export declare const CURSOR_MODEL_COSTS: {
|
|
|
241
247
|
readonly output: 12;
|
|
242
248
|
readonly cache_read: 1;
|
|
243
249
|
};
|
|
250
|
+
readonly "grok-4.7": {
|
|
251
|
+
readonly input: 2;
|
|
252
|
+
readonly output: 6;
|
|
253
|
+
readonly cache_read: 0.5;
|
|
254
|
+
readonly context_over_200k: {
|
|
255
|
+
readonly input: 4;
|
|
256
|
+
readonly output: 12;
|
|
257
|
+
readonly cache_read: 1;
|
|
258
|
+
};
|
|
259
|
+
};
|
|
260
|
+
readonly "grok-4.7-fast": {
|
|
261
|
+
readonly input: 4;
|
|
262
|
+
readonly output: 12;
|
|
263
|
+
readonly cache_read: 1;
|
|
264
|
+
readonly context_over_200k: {
|
|
265
|
+
readonly input: 6;
|
|
266
|
+
readonly output: 18;
|
|
267
|
+
readonly cache_read: 1.5;
|
|
268
|
+
};
|
|
269
|
+
};
|
|
244
270
|
readonly "kimi-k2.7-code": {
|
|
245
271
|
readonly input: 0.95;
|
|
246
272
|
readonly output: 4;
|
|
@@ -289,6 +315,10 @@ export declare const CURSOR_MODEL_CONTEXTS: {
|
|
|
289
315
|
readonly maxContext: 300000;
|
|
290
316
|
readonly maxContextForMaxMode: 1000000;
|
|
291
317
|
};
|
|
318
|
+
readonly "claude-opus-5-5": {
|
|
319
|
+
readonly maxContext: 300000;
|
|
320
|
+
readonly maxContextForMaxMode: 1000000;
|
|
321
|
+
};
|
|
292
322
|
readonly "claude-sonnet-4": {
|
|
293
323
|
readonly maxContext: 200000;
|
|
294
324
|
readonly maxContextForMaxMode: 1000000;
|
|
@@ -383,6 +413,10 @@ export declare const CURSOR_MODEL_CONTEXTS: {
|
|
|
383
413
|
readonly "grok-4.6": {
|
|
384
414
|
readonly maxContext: 256000;
|
|
385
415
|
};
|
|
416
|
+
readonly "grok-4.7": {
|
|
417
|
+
readonly maxContext: 256000;
|
|
418
|
+
readonly maxContextForMaxMode: 500000;
|
|
419
|
+
};
|
|
386
420
|
readonly "kimi-k2.7-code": {
|
|
387
421
|
readonly maxContext: 262000;
|
|
388
422
|
};
|
|
@@ -420,6 +454,9 @@ export declare const CURSOR_MODEL_CAPABILITIES: {
|
|
|
420
454
|
readonly "claude-opus-5": {
|
|
421
455
|
readonly supportsImages: true;
|
|
422
456
|
};
|
|
457
|
+
readonly "claude-opus-5-5": {
|
|
458
|
+
readonly supportsImages: true;
|
|
459
|
+
};
|
|
423
460
|
readonly "claude-sonnet-4": {
|
|
424
461
|
readonly supportsImages: true;
|
|
425
462
|
};
|
|
@@ -498,6 +535,9 @@ export declare const CURSOR_MODEL_CAPABILITIES: {
|
|
|
498
535
|
readonly "grok-4.6": {
|
|
499
536
|
readonly supportsImages: false;
|
|
500
537
|
};
|
|
538
|
+
readonly "grok-4.7": {
|
|
539
|
+
readonly supportsImages: false;
|
|
540
|
+
};
|
|
501
541
|
readonly "kimi-k2.7-code": {
|
|
502
542
|
readonly supportsImages: true;
|
|
503
543
|
};
|
package/dist/pricing-data.js
CHANGED
|
@@ -55,6 +55,12 @@ export const CURSOR_MODEL_COSTS = {
|
|
|
55
55
|
"cache_read": 0.5,
|
|
56
56
|
"cache_write": 6.25
|
|
57
57
|
},
|
|
58
|
+
"claude-opus-5-5": {
|
|
59
|
+
"input": 4,
|
|
60
|
+
"output": 20,
|
|
61
|
+
"cache_read": 0.2,
|
|
62
|
+
"cache_write": 5
|
|
63
|
+
},
|
|
58
64
|
"claude-sonnet-4": {
|
|
59
65
|
"input": 3,
|
|
60
66
|
"output": 15,
|
|
@@ -241,6 +247,26 @@ export const CURSOR_MODEL_COSTS = {
|
|
|
241
247
|
"output": 12,
|
|
242
248
|
"cache_read": 1
|
|
243
249
|
},
|
|
250
|
+
"grok-4.7": {
|
|
251
|
+
"input": 2,
|
|
252
|
+
"output": 6,
|
|
253
|
+
"cache_read": 0.5,
|
|
254
|
+
"context_over_200k": {
|
|
255
|
+
"input": 4,
|
|
256
|
+
"output": 12,
|
|
257
|
+
"cache_read": 1
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
"grok-4.7-fast": {
|
|
261
|
+
"input": 4,
|
|
262
|
+
"output": 12,
|
|
263
|
+
"cache_read": 1,
|
|
264
|
+
"context_over_200k": {
|
|
265
|
+
"input": 6,
|
|
266
|
+
"output": 18,
|
|
267
|
+
"cache_read": 1.5
|
|
268
|
+
}
|
|
269
|
+
},
|
|
244
270
|
"kimi-k2.7-code": {
|
|
245
271
|
"input": 0.95,
|
|
246
272
|
"output": 4,
|
|
@@ -289,6 +315,10 @@ export const CURSOR_MODEL_CONTEXTS = {
|
|
|
289
315
|
"maxContext": 300000,
|
|
290
316
|
"maxContextForMaxMode": 1000000
|
|
291
317
|
},
|
|
318
|
+
"claude-opus-5-5": {
|
|
319
|
+
"maxContext": 300000,
|
|
320
|
+
"maxContextForMaxMode": 1000000
|
|
321
|
+
},
|
|
292
322
|
"claude-sonnet-4": {
|
|
293
323
|
"maxContext": 200000,
|
|
294
324
|
"maxContextForMaxMode": 1000000
|
|
@@ -383,6 +413,10 @@ export const CURSOR_MODEL_CONTEXTS = {
|
|
|
383
413
|
"grok-4.6": {
|
|
384
414
|
"maxContext": 256000
|
|
385
415
|
},
|
|
416
|
+
"grok-4.7": {
|
|
417
|
+
"maxContext": 256000,
|
|
418
|
+
"maxContextForMaxMode": 500000
|
|
419
|
+
},
|
|
386
420
|
"kimi-k2.7-code": {
|
|
387
421
|
"maxContext": 262000
|
|
388
422
|
},
|
|
@@ -420,6 +454,9 @@ export const CURSOR_MODEL_CAPABILITIES = {
|
|
|
420
454
|
"claude-opus-5": {
|
|
421
455
|
"supportsImages": true
|
|
422
456
|
},
|
|
457
|
+
"claude-opus-5-5": {
|
|
458
|
+
"supportsImages": true
|
|
459
|
+
},
|
|
423
460
|
"claude-sonnet-4": {
|
|
424
461
|
"supportsImages": true
|
|
425
462
|
},
|
|
@@ -498,6 +535,9 @@ export const CURSOR_MODEL_CAPABILITIES = {
|
|
|
498
535
|
"grok-4.6": {
|
|
499
536
|
"supportsImages": false
|
|
500
537
|
},
|
|
538
|
+
"grok-4.7": {
|
|
539
|
+
"supportsImages": false
|
|
540
|
+
},
|
|
501
541
|
"kimi-k2.7-code": {
|
|
502
542
|
"supportsImages": true
|
|
503
543
|
},
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* and fall back to prose instead of ever calling the advertised host tool.
|
|
10
10
|
*
|
|
11
11
|
* Every rule below mirrors Cursor CLI, decompiled at
|
|
12
|
-
*
|
|
12
|
+
* `<cursor-cli-checkout>/cursor/cli{,-local}`:
|
|
13
13
|
*
|
|
14
14
|
* - `src/utils/interaction-utils.ts` — `BU` (freeform option id), `Q7` (display
|
|
15
15
|
* options), `iX` (selection → success), `N$` (rejection + default reason).
|
|
@@ -98,19 +98,18 @@ export declare function rejectedResult(reason?: string): AskQuestionResultMessag
|
|
|
98
98
|
/** `AskQuestionResult{async}` — the immediate reply for `run_async` queries. */
|
|
99
99
|
export declare function asyncResult(): AskQuestionResultMessage;
|
|
100
100
|
/**
|
|
101
|
-
* Pull each question's answer text out of
|
|
101
|
+
* Pull each question's answer text out of a host `question` tool output.
|
|
102
102
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
103
|
+
* OpenCode returns prose (`metadata.answers` does not cross the AI SDK
|
|
104
|
+
* boundary):
|
|
105
105
|
*
|
|
106
106
|
* User has answered your questions: "<q1>"="<a, b>", "<q2>"="Unanswered". You
|
|
107
107
|
* can now continue with the user's answers in mind.
|
|
108
108
|
*
|
|
109
|
-
* The
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* unanswered rather than guessed at.
|
|
109
|
+
* The prose is located by its `"<question>"="` anchor — robust
|
|
110
|
+
* against commas, quotes and `"="` inside question or answer text, and against
|
|
111
|
+
* duplicate questions. Returns undefined for a question whose anchor is
|
|
112
|
+
* absent, which is treated as unanswered rather than guessed at.
|
|
114
113
|
*/
|
|
115
114
|
export declare function parseAnswerSegments(questions: readonly CursorAskQuestionItem[], output: string): Array<string | undefined>;
|
|
116
115
|
/**
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* and fall back to prose instead of ever calling the advertised host tool.
|
|
10
10
|
*
|
|
11
11
|
* Every rule below mirrors Cursor CLI, decompiled at
|
|
12
|
-
*
|
|
12
|
+
* `<cursor-cli-checkout>/cursor/cli{,-local}`:
|
|
13
13
|
*
|
|
14
14
|
* - `src/utils/interaction-utils.ts` — `BU` (freeform option id), `Q7` (display
|
|
15
15
|
* options), `iX` (selection → success), `N$` (rejection + default reason).
|
|
@@ -218,19 +218,18 @@ export function asyncResult() {
|
|
|
218
218
|
return { async: {} };
|
|
219
219
|
}
|
|
220
220
|
/**
|
|
221
|
-
* Pull each question's answer text out of
|
|
221
|
+
* Pull each question's answer text out of a host `question` tool output.
|
|
222
222
|
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
223
|
+
* OpenCode returns prose (`metadata.answers` does not cross the AI SDK
|
|
224
|
+
* boundary):
|
|
225
225
|
*
|
|
226
226
|
* User has answered your questions: "<q1>"="<a, b>", "<q2>"="Unanswered". You
|
|
227
227
|
* can now continue with the user's answers in mind.
|
|
228
228
|
*
|
|
229
|
-
* The
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* unanswered rather than guessed at.
|
|
229
|
+
* The prose is located by its `"<question>"="` anchor — robust
|
|
230
|
+
* against commas, quotes and `"="` inside question or answer text, and against
|
|
231
|
+
* duplicate questions. Returns undefined for a question whose anchor is
|
|
232
|
+
* absent, which is treated as unanswered rather than guessed at.
|
|
234
233
|
*/
|
|
235
234
|
export function parseAnswerSegments(questions, output) {
|
|
236
235
|
const answers = [];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { clearCheckpoint } from "./checkpoint.js";
|
|
3
3
|
import { clearConversationBlobs } from "./blob-store.js";
|
|
4
|
-
import {
|
|
4
|
+
import { transferFrozenRequestContext, } from "../context/frozen.js";
|
|
5
5
|
/**
|
|
6
6
|
* OpenCode session key → active Cursor conversation_id.
|
|
7
7
|
*
|
|
@@ -11,8 +11,15 @@ import { clearFrozenRequestContext, transferFrozenRequestContext, } from "../con
|
|
|
11
11
|
* cached conversation against OpenCode's newly compacted prompt.
|
|
12
12
|
*/
|
|
13
13
|
const activeBySession = new Map();
|
|
14
|
+
// A compaction id cannot be reconstructed from its host session key. Keep its
|
|
15
|
+
// tiny binding even when the recent-session index soft-evicts that session.
|
|
16
|
+
const remintedBySession = new Map();
|
|
14
17
|
export const MAX_ACTIVE_CONVERSATION_BINDINGS = 256;
|
|
15
18
|
function rememberConversation(sessionKey, conversationId) {
|
|
19
|
+
if (conversationId === sessionIdToUuid(sessionKey))
|
|
20
|
+
remintedBySession.delete(sessionKey);
|
|
21
|
+
else
|
|
22
|
+
remintedBySession.set(sessionKey, conversationId);
|
|
16
23
|
// Map insertion order is the LRU order. Refresh existing sessions on use.
|
|
17
24
|
activeBySession.delete(sessionKey);
|
|
18
25
|
activeBySession.set(sessionKey, conversationId);
|
|
@@ -21,19 +28,21 @@ function rememberConversation(sessionKey, conversationId) {
|
|
|
21
28
|
if (!oldest)
|
|
22
29
|
break;
|
|
23
30
|
activeBySession.delete(oldest[0]);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
// Evict only the lookup pointer. The checkpoint may still be needed by a
|
|
32
|
+
// held Run, and a returning session can hydrate its durable binding before
|
|
33
|
+
// resolving another turn. Compaction/reset owns deletion of opaque state.
|
|
27
34
|
}
|
|
28
35
|
}
|
|
29
36
|
export function resetConversationBindingsForTests() {
|
|
30
37
|
activeBySession.clear();
|
|
38
|
+
remintedBySession.clear();
|
|
31
39
|
}
|
|
32
40
|
export function hasConversationBinding(sessionKey) {
|
|
33
|
-
return activeBySession.has(sessionKey);
|
|
41
|
+
return activeBySession.has(sessionKey) || remintedBySession.has(sessionKey);
|
|
34
42
|
}
|
|
35
43
|
export function isActiveConversationBinding(sessionKey, conversationId) {
|
|
36
|
-
return activeBySession.get(sessionKey)
|
|
44
|
+
return (activeBySession.get(sessionKey) ?? remintedBySession.get(sessionKey)
|
|
45
|
+
?? sessionIdToUuid(sessionKey)) === conversationId;
|
|
37
46
|
}
|
|
38
47
|
/** Restore a validated durable binding before resolving the next Run. */
|
|
39
48
|
export function restoreConversationBinding(sessionKey, conversationId) {
|
|
@@ -63,7 +72,7 @@ export function resolveConversationGroupId(sessionKey, conversationId) {
|
|
|
63
72
|
}
|
|
64
73
|
/** Current Cursor conversation_id for an OpenCode session key, creating the default binding if needed. */
|
|
65
74
|
export function peekConversationId(sessionKey) {
|
|
66
|
-
let id = activeBySession.get(sessionKey);
|
|
75
|
+
let id = activeBySession.get(sessionKey) ?? remintedBySession.get(sessionKey);
|
|
67
76
|
if (!id) {
|
|
68
77
|
id = sessionIdToUuid(sessionKey);
|
|
69
78
|
}
|
|
@@ -83,7 +92,8 @@ export function bindConversationId(sessionKey, opts) {
|
|
|
83
92
|
return { conversationId: crypto.randomUUID(), reset: !!opts?.reset };
|
|
84
93
|
}
|
|
85
94
|
if (opts?.reset) {
|
|
86
|
-
const previousId = activeBySession.get(sessionKey) ??
|
|
95
|
+
const previousId = activeBySession.get(sessionKey) ?? remintedBySession.get(sessionKey)
|
|
96
|
+
?? sessionIdToUuid(sessionKey);
|
|
87
97
|
const conversationId = crypto.randomUUID();
|
|
88
98
|
clearCheckpoint(previousId);
|
|
89
99
|
clearConversationBlobs(previousId);
|
|
@@ -54,9 +54,10 @@ export type CursorPlanStageInput = {
|
|
|
54
54
|
* How a CreatePlan query is satisfied this turn. Keyed only on the advertised
|
|
55
55
|
* catalog and the provider's own plan-mode state — never on host identity.
|
|
56
56
|
*
|
|
57
|
-
* - `stage` a host plan-stage tool is advertised; the host writes the plan
|
|
58
|
-
*
|
|
59
|
-
*
|
|
57
|
+
* - `stage` a host plan-stage tool is advertised; the host writes the plan.
|
|
58
|
+
* Its tool result is the approval outcome (success = approved,
|
|
59
|
+
* error = not approved). The result may name the host's own
|
|
60
|
+
* follow-up approval call. This provider does not invent that prompt.
|
|
60
61
|
* - `approve` no such tool, but the host advertises `question`: the provider
|
|
61
62
|
* writes the plan itself and then asks the same thing with upstream
|
|
62
63
|
* `PlanExitTool`'s own prompt.
|
|
@@ -95,10 +96,8 @@ export declare function createPlanApprovalQuestionInput(planLabel: string): Open
|
|
|
95
96
|
/**
|
|
96
97
|
* True when the emulated approval prompt came back as an explicit "Yes".
|
|
97
98
|
* An unanswered, dismissed, or failed prompt keeps the model planning rather
|
|
98
|
-
* than silently starting execution.
|
|
99
|
-
*
|
|
100
|
-
* `question` must be the exact prompt that was asked — the host echoes it back
|
|
101
|
-
* verbatim, and it is the anchor the answer is parsed from.
|
|
99
|
+
* than silently starting execution. OpenCode prose is anchored on the exact
|
|
100
|
+
* prompt text. Every other result shape fails closed.
|
|
102
101
|
*/
|
|
103
102
|
export declare function createPlanApproved(output: string, isError: boolean, question: string): boolean;
|
|
104
103
|
/** Decode a `create_plan_request_query` body, or undefined when unusable. */
|
|
@@ -156,16 +156,15 @@ export function createPlanApprovalQuestionInput(planLabel) {
|
|
|
156
156
|
/**
|
|
157
157
|
* True when the emulated approval prompt came back as an explicit "Yes".
|
|
158
158
|
* An unanswered, dismissed, or failed prompt keeps the model planning rather
|
|
159
|
-
* than silently starting execution.
|
|
160
|
-
*
|
|
161
|
-
* `question` must be the exact prompt that was asked — the host echoes it back
|
|
162
|
-
* verbatim, and it is the anchor the answer is parsed from.
|
|
159
|
+
* than silently starting execution. OpenCode prose is anchored on the exact
|
|
160
|
+
* prompt text. Every other result shape fails closed.
|
|
163
161
|
*/
|
|
164
162
|
export function createPlanApproved(output, isError, question) {
|
|
165
163
|
if (isError)
|
|
166
164
|
return false;
|
|
167
165
|
const [segment] = parseAnswerSegments([createPlanApprovalItem(question)], output);
|
|
168
|
-
|
|
166
|
+
const answer = (segment ?? "").trim().toLowerCase();
|
|
167
|
+
return answer === CREATE_PLAN_APPROVAL_YES.toLowerCase();
|
|
169
168
|
}
|
|
170
169
|
function asRecord(value) {
|
|
171
170
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
@@ -287,7 +287,22 @@ export function createMessageTypes() {
|
|
|
287
287
|
{ id: 3, name: "pattern", type: "string" },
|
|
288
288
|
{ id: 4, name: "tool_call_id", type: "string" },
|
|
289
289
|
]);
|
|
290
|
-
|
|
290
|
+
// agent.v1 GetMcpToolsToolCall is args + result. Cursor's agent fills result
|
|
291
|
+
// locally (GetDynamicTools / GetMcpTools); large catalogs spill through
|
|
292
|
+
// write_args and put the spill path on GetMcpToolsSuccess.output_file_path.
|
|
293
|
+
addType(root, "GetMcpToolsSuccess", [
|
|
294
|
+
{ id: 1, name: "content", type: "string" },
|
|
295
|
+
{ id: 2, name: "output_file_path", type: "string" },
|
|
296
|
+
]);
|
|
297
|
+
addType(root, "GetMcpToolsError", [{ id: 1, name: "error", type: "string" }]);
|
|
298
|
+
addType(root, "GetMcpToolsAgentResult", [
|
|
299
|
+
{ id: 1, name: "success", type: "GetMcpToolsSuccess" },
|
|
300
|
+
{ id: 2, name: "error", type: "GetMcpToolsError" },
|
|
301
|
+
], [{ name: "result", fields: ["success", "error"] }]);
|
|
302
|
+
addType(root, "GetMcpToolsToolCall", [
|
|
303
|
+
{ id: 1, name: "args", type: "GetMcpToolsArgs" },
|
|
304
|
+
{ id: 2, name: "result", type: "GetMcpToolsAgentResult" },
|
|
305
|
+
]);
|
|
291
306
|
addType(root, "GenerateImageToolArgs", [
|
|
292
307
|
{ id: 1, name: "description", type: "string" },
|
|
293
308
|
{ id: 2, name: "file_path", type: "string" },
|