cursor-opencode-provider 0.6.8 → 0.7.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.
- package/README.md +50 -180
- package/dist/compaction-marker.d.ts +3 -2
- package/dist/compaction-marker.js +3 -2
- package/dist/host-agent-mode.d.ts +32 -0
- package/dist/host-agent-mode.js +92 -0
- package/dist/language-model.d.ts +6 -1
- package/dist/language-model.js +170 -34
- package/dist/models.d.ts +12 -1
- package/dist/models.js +16 -6
- package/dist/opencode2/catalog.d.ts +46 -11
- package/dist/opencode2/catalog.js +60 -45
- package/dist/opencode2/todo-tools.d.ts +64 -0
- package/dist/opencode2/todo-tools.js +165 -0
- package/dist/opencode2/types.d.ts +138 -83
- package/dist/opencode2/types.js +8 -10
- package/dist/plan-execution-kickoff.d.ts +2 -0
- package/dist/plan-execution-kickoff.js +25 -1
- package/dist/plugin-opencode2.d.ts +4 -1
- package/dist/plugin-opencode2.js +335 -154
- package/dist/plugin.js +5 -1
- package/dist/protocol/conversation-persistence.d.ts +2 -0
- package/dist/protocol/conversation-persistence.js +19 -1
- package/dist/protocol/conversation-state.d.ts +4 -0
- package/dist/protocol/conversation-state.js +4 -0
- package/dist/protocol/switch-mode.d.ts +5 -4
- package/dist/protocol/switch-mode.js +12 -4
- package/dist/protocol/tool-call-bridge.js +1 -1
- package/dist/protocol/tools.d.ts +30 -4
- package/dist/protocol/tools.js +185 -58
- package/dist/session.d.ts +7 -1
- package/dist/shared.d.ts +3 -1
- package/dist/shared.js +3 -1
- package/dist/shell-timeout.d.ts +13 -0
- package/dist/shell-timeout.js +38 -0
- package/dist/todo-store.d.ts +29 -0
- package/dist/todo-store.js +78 -0
- package/dist/web-tools.d.ts +13 -0
- package/dist/web-tools.js +83 -5
- package/package.json +6 -3
package/dist/plugin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
|
-
import { CURSOR_COMPACTION_OPTION, CURSOR_PROVIDER_ID, CURSOR_WEBSITE_HOST,
|
|
3
|
+
import { CURSOR_API_HOST, CURSOR_COMPACTION_OPTION, CURSOR_HOST_AGENT_OPTION, CURSOR_PROVIDER_ID, CURSOR_WEBSITE_HOST, } from "./shared.js";
|
|
4
4
|
import { cursorApiBaseURL, cursorGetServerConfigTelemetryEnabled } from "./plugin-core.js";
|
|
5
5
|
import { pollForTokens, exchangeApiKey, refreshAccessToken, isExpiringSoon, generatePkceParams, generatePkceChallenge, buildLoginUrl, decodeJwtExpiryMs } from "./auth.js";
|
|
6
6
|
import { readCache, discoverModels, isCacheFresh } from "./models.js";
|
|
@@ -254,6 +254,10 @@ export async function CursorPlugin(input) {
|
|
|
254
254
|
async "chat.params"(hookInput, output) {
|
|
255
255
|
if (hookInput.model.providerID !== CURSOR_PROVIDER_ID)
|
|
256
256
|
return;
|
|
257
|
+
// Agent changes can replace the host system prompt and mode contract.
|
|
258
|
+
// Carry the canonical OpenCode id so an incompatible Cursor checkpoint
|
|
259
|
+
// is rotated instead of resuming the prior agent's prompt.
|
|
260
|
+
output.options[CURSOR_HOST_AGENT_OPTION] = hookInput.agent;
|
|
257
261
|
// OpenCode's compaction pipeline invokes the LLM with agent="compaction".
|
|
258
262
|
// Carry that stable runtime fact into LanguageModelV3 providerOptions so
|
|
259
263
|
// the provider never has to guess from an empty tool list.
|
|
@@ -9,6 +9,8 @@ export type PersistedConversation = {
|
|
|
9
9
|
requestContext: Record<string, unknown>;
|
|
10
10
|
toolCatalog: OpencodeToolDef[];
|
|
11
11
|
postCompactionRebase: boolean;
|
|
12
|
+
hostAgent?: string;
|
|
13
|
+
systemPromptHash?: string;
|
|
12
14
|
};
|
|
13
15
|
export type ConversationLoadStatus = "restored" | "missing" | "invalid" | "expired";
|
|
14
16
|
export type PersistedConversationLoad = {
|
|
@@ -47,7 +47,7 @@ function cloneConversation(value) {
|
|
|
47
47
|
*
|
|
48
48
|
* ConversationCache: schema_version=1, session_key=2, conversation_id=3,
|
|
49
49
|
* updated_at=4, checkpoint=5, blobs=6, request_context=7, tool_catalog=8,
|
|
50
|
-
* post_compaction_rebase=9.
|
|
50
|
+
* post_compaction_rebase=9, host_agent=10, system_prompt_hash=11.
|
|
51
51
|
* Blob: id=1, data=2. Tool: name=1, description=2,
|
|
52
52
|
* input_schema_json=3, source_name=4.
|
|
53
53
|
*
|
|
@@ -172,6 +172,10 @@ function encodeCacheFile(value) {
|
|
|
172
172
|
}
|
|
173
173
|
if (value.postCompactionRebase)
|
|
174
174
|
writer.uint32(fieldTag(9, 0)).bool(true);
|
|
175
|
+
if (value.hostAgent)
|
|
176
|
+
writer.uint32(fieldTag(10, 2)).string(value.hostAgent);
|
|
177
|
+
if (value.systemPromptHash)
|
|
178
|
+
writer.uint32(fieldTag(11, 2)).string(value.systemPromptHash);
|
|
175
179
|
return { protobufBytes: writer.finish(), requestContextBytes: requestContext.length };
|
|
176
180
|
}
|
|
177
181
|
function decodeProtobuf(data) {
|
|
@@ -185,6 +189,8 @@ function decodeProtobuf(data) {
|
|
|
185
189
|
let requestContextBytes;
|
|
186
190
|
const toolCatalog = [];
|
|
187
191
|
let postCompactionRebase = false;
|
|
192
|
+
let hostAgent;
|
|
193
|
+
let systemPromptHash;
|
|
188
194
|
while (reader.pos < reader.len) {
|
|
189
195
|
const tag = reader.uint32();
|
|
190
196
|
const wireType = tag & 7;
|
|
@@ -234,6 +240,16 @@ function decodeProtobuf(data) {
|
|
|
234
240
|
throw new Error("invalid compaction marker");
|
|
235
241
|
postCompactionRebase = reader.bool();
|
|
236
242
|
break;
|
|
243
|
+
case 10:
|
|
244
|
+
if (wireType !== 2)
|
|
245
|
+
throw new Error("invalid host agent");
|
|
246
|
+
hostAgent = reader.string();
|
|
247
|
+
break;
|
|
248
|
+
case 11:
|
|
249
|
+
if (wireType !== 2)
|
|
250
|
+
throw new Error("invalid system prompt hash");
|
|
251
|
+
systemPromptHash = reader.string();
|
|
252
|
+
break;
|
|
237
253
|
default:
|
|
238
254
|
reader.skipType(wireType);
|
|
239
255
|
}
|
|
@@ -252,6 +268,8 @@ function decodeProtobuf(data) {
|
|
|
252
268
|
requestContext,
|
|
253
269
|
toolCatalog,
|
|
254
270
|
postCompactionRebase,
|
|
271
|
+
...(hostAgent ? { hostAgent } : {}),
|
|
272
|
+
...(systemPromptHash ? { systemPromptHash } : {}),
|
|
255
273
|
};
|
|
256
274
|
}
|
|
257
275
|
function decodeCacheFile(compressed, expectedSessionKey) {
|
|
@@ -4,6 +4,8 @@ export declare function hydrateConversationState(cacheDir: string, sessionKey: s
|
|
|
4
4
|
conversationId: string;
|
|
5
5
|
postCompactionRebase: boolean;
|
|
6
6
|
toolCatalog: OpencodeToolDef[];
|
|
7
|
+
hostAgent?: string;
|
|
8
|
+
systemPromptHash?: string;
|
|
7
9
|
} | undefined>;
|
|
8
10
|
/** Persist the complete resumable state only after Cursor confirms TurnEnded. */
|
|
9
11
|
export declare function persistConversationState(cacheDir: string, input: {
|
|
@@ -12,5 +14,7 @@ export declare function persistConversationState(cacheDir: string, input: {
|
|
|
12
14
|
requestContext: Record<string, unknown>;
|
|
13
15
|
toolCatalog?: OpencodeToolDef[];
|
|
14
16
|
postCompactionRebase?: boolean;
|
|
17
|
+
hostAgent?: string;
|
|
18
|
+
systemPromptHash?: string;
|
|
15
19
|
}): Promise<void>;
|
|
16
20
|
export declare function clearPersistedConversationState(cacheDir: string, sessionKey: string, expectedConversationId?: string): Promise<void>;
|
|
@@ -27,6 +27,8 @@ export async function hydrateConversationState(cacheDir, sessionKey) {
|
|
|
27
27
|
conversationId: persisted.conversationId,
|
|
28
28
|
postCompactionRebase: persisted.postCompactionRebase,
|
|
29
29
|
toolCatalog: structuredClone(persisted.toolCatalog),
|
|
30
|
+
...(persisted.hostAgent ? { hostAgent: persisted.hostAgent } : {}),
|
|
31
|
+
...(persisted.systemPromptHash ? { systemPromptHash: persisted.systemPromptHash } : {}),
|
|
30
32
|
};
|
|
31
33
|
}
|
|
32
34
|
/** Persist the complete resumable state only after Cursor confirms TurnEnded. */
|
|
@@ -50,6 +52,8 @@ export async function persistConversationState(cacheDir, input) {
|
|
|
50
52
|
requestContext,
|
|
51
53
|
toolCatalog: structuredClone(input.toolCatalog ?? []),
|
|
52
54
|
postCompactionRebase: input.postCompactionRebase,
|
|
55
|
+
hostAgent: input.hostAgent,
|
|
56
|
+
systemPromptHash: input.systemPromptHash,
|
|
53
57
|
});
|
|
54
58
|
trace(`conversation persistence: saved sessionKey=${input.sessionKey} ` +
|
|
55
59
|
`conversationId=${input.conversationId} checkpoint=${checkpoint?.length ?? 0}B ` +
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Display `switch_mode_tool_call` (#25) is a separate transcript record and is
|
|
9
9
|
* not replayed as a host tool.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* OpenCode 1.x mapping (advertisement-gated):
|
|
12
12
|
* - plan, spec → plan_enter
|
|
13
13
|
* - every other non-empty target → plan_exit (leave plan for OpenCode build),
|
|
14
14
|
* then inject the Cursor CLI-shaped mode reminder for that target
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
* `setAgentModel` primary-agent switch (opencode `tool/plan.ts`,
|
|
20
20
|
* `session/prompt.ts`). A provider cannot reach `setAgentModel`, but the two
|
|
21
21
|
* observable halves — the approval gate and the behavioural contract — are both
|
|
22
|
-
* reachable, so the bridge degrades instead of refusing when the host
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* reachable, so the protocol bridge degrades instead of refusing when the host
|
|
23
|
+
* tool is missing. OpenCode 2 additionally exposes `session.switchAgent`; its
|
|
24
|
+
* entrypoint installs a structural callback that selects the native `plan` or
|
|
25
|
+
* `build` primary agent after the owning Cursor Run becomes terminal:
|
|
25
26
|
*
|
|
26
27
|
* - entering plan/spec needs no host tool at all — approve immediately and let
|
|
27
28
|
* the injected <system_reminder> carry the contract, exactly as Cursor CLI's
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Display `switch_mode_tool_call` (#25) is a separate transcript record and is
|
|
9
9
|
* not replayed as a host tool.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* OpenCode 1.x mapping (advertisement-gated):
|
|
12
12
|
* - plan, spec → plan_enter
|
|
13
13
|
* - every other non-empty target → plan_exit (leave plan for OpenCode build),
|
|
14
14
|
* then inject the Cursor CLI-shaped mode reminder for that target
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
* `setAgentModel` primary-agent switch (opencode `tool/plan.ts`,
|
|
20
20
|
* `session/prompt.ts`). A provider cannot reach `setAgentModel`, but the two
|
|
21
21
|
* observable halves — the approval gate and the behavioural contract — are both
|
|
22
|
-
* reachable, so the bridge degrades instead of refusing when the host
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* reachable, so the protocol bridge degrades instead of refusing when the host
|
|
23
|
+
* tool is missing. OpenCode 2 additionally exposes `session.switchAgent`; its
|
|
24
|
+
* entrypoint installs a structural callback that selects the native `plan` or
|
|
25
|
+
* `build` primary agent after the owning Cursor Run becomes terminal:
|
|
25
26
|
*
|
|
26
27
|
* - entering plan/spec needs no host tool at all — approve immediately and let
|
|
27
28
|
* the injected <system_reminder> carry the contract, exactly as Cursor CLI's
|
|
@@ -68,6 +69,7 @@ export const SWITCH_MODE_EXIT_HEADER = "Build Agent";
|
|
|
68
69
|
const SWITCH_MODE_EXIT_YES = "Yes";
|
|
69
70
|
const SWITCH_MODE_EXIT_NO = "No";
|
|
70
71
|
const activeCursorModeBySession = new Map();
|
|
72
|
+
const MAX_ACTIVE_CURSOR_MODES = 256;
|
|
71
73
|
function asRecord(value) {
|
|
72
74
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
73
75
|
? value
|
|
@@ -248,6 +250,12 @@ export function setActiveCursorMode(sessionKey, targetModeId, options = {}) {
|
|
|
248
250
|
firstTurn: true,
|
|
249
251
|
bridgedPlanEntered: options.bridgedPlanEntered === true,
|
|
250
252
|
});
|
|
253
|
+
while (activeCursorModeBySession.size > MAX_ACTIVE_CURSOR_MODES) {
|
|
254
|
+
const oldest = activeCursorModeBySession.keys().next().value;
|
|
255
|
+
if (!oldest)
|
|
256
|
+
break;
|
|
257
|
+
activeCursorModeBySession.delete(oldest);
|
|
258
|
+
}
|
|
251
259
|
}
|
|
252
260
|
export function getActiveCursorMode(sessionKey) {
|
|
253
261
|
if (!sessionKey)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mapCursorArgsToOpencode, mapCursorSubagentTypeToOpenCode, mcpRealToolName } from "./tools.js";
|
|
1
|
+
import { mapCursorArgsToOpencode, mapCursorSubagentTypeToOpenCode, mcpRealToolName, } from "./tools.js";
|
|
2
2
|
import { decodeStructEntriesToJson } from "./struct.js";
|
|
3
3
|
import { mapSwitchModeTarget } from "./switch-mode.js";
|
|
4
4
|
const TODO_STATUS = {
|
package/dist/protocol/tools.d.ts
CHANGED
|
@@ -8,6 +8,25 @@ export type OpencodeToolDef = {
|
|
|
8
8
|
/** Original flattened identity when `name` is a Cursor-facing alias. */
|
|
9
9
|
sourceName?: string;
|
|
10
10
|
};
|
|
11
|
+
/** Host file-tool argument key. OpenCode 1.x uses `filePath`; 2.0 uses `path`. */
|
|
12
|
+
export type HostFilePathKey = "path" | "filePath";
|
|
13
|
+
/** Host shell tool id. OpenCode 1.x uses `bash`; 2.0 uses `shell`. */
|
|
14
|
+
export type HostShellTool = "bash" | "shell";
|
|
15
|
+
export type HostToolDialect = {
|
|
16
|
+
filePathKey: HostFilePathKey;
|
|
17
|
+
shellTool: HostShellTool;
|
|
18
|
+
};
|
|
19
|
+
export declare const OPENCODE_1_TOOL_DIALECT: HostToolDialect;
|
|
20
|
+
/** Prefer `filePath`, then `path` / `file_path` — Cursor and both OpenCode majors. */
|
|
21
|
+
export declare function opencodePathArg(args: Record<string, unknown> | undefined): string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Infer the host tool dialect from advertised AI SDK schemas.
|
|
24
|
+
* OpenCode 2.0 read/edit/write require `path` and rename bash → `shell`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function hostToolDialectFromTools(tools: readonly {
|
|
27
|
+
name?: string;
|
|
28
|
+
inputSchema?: unknown;
|
|
29
|
+
}[]): HostToolDialect;
|
|
11
30
|
export declare const CUSTOM_WEBSEARCH_TOOL = "custom_websearch";
|
|
12
31
|
export declare const CUSTOM_WEBFETCH_TOOL = "custom_webfetch";
|
|
13
32
|
export declare const CUSTOM_LIST_MCP_RESOURCES_TOOL = "custom_list_mcp_resources";
|
|
@@ -72,9 +91,9 @@ export type HostSubagentDefinition = {
|
|
|
72
91
|
description?: string;
|
|
73
92
|
};
|
|
74
93
|
export type HostSubagentCatalog = {
|
|
75
|
-
executor?: "task";
|
|
94
|
+
executor?: "task" | "subagent";
|
|
76
95
|
agents: HostSubagentDefinition[];
|
|
77
|
-
/** True when the OpenCode task tool supplied its complete, permission-filtered catalog. */
|
|
96
|
+
/** True when the OpenCode task/subagent tool supplied its complete, permission-filtered catalog. */
|
|
78
97
|
complete: boolean;
|
|
79
98
|
};
|
|
80
99
|
/** Extract the current host's permission-filtered Task/Actor recipient catalog. */
|
|
@@ -143,13 +162,13 @@ export type ParsedExecRequest = {
|
|
|
143
162
|
* this provider's warning text.
|
|
144
163
|
*/
|
|
145
164
|
export declare function rejectPartialReadMutation(parsed: ParsedExecRequest): void;
|
|
146
|
-
export declare function parseExecServerMessage(msg: Record<string, unknown
|
|
165
|
+
export declare function parseExecServerMessage(msg: Record<string, unknown>, dialect?: HostToolDialect): ParsedExecRequest | undefined;
|
|
147
166
|
/**
|
|
148
167
|
* Remap Cursor exec / MCP arg shapes onto OpenCode's Effect Schema keys.
|
|
149
168
|
* Without this, OpenCode rejects calls with InvalidArgumentsError
|
|
150
169
|
* (e.g. `path` instead of `filePath`, `file_text` instead of `content`).
|
|
151
170
|
*/
|
|
152
|
-
export declare function mapCursorArgsToOpencode(toolName: string, raw: Record<string, unknown>, execVariant?: string): {
|
|
171
|
+
export declare function mapCursorArgsToOpencode(toolName: string, raw: Record<string, unknown>, execVariant?: string, dialect?: HostToolDialect): {
|
|
153
172
|
toolName: string;
|
|
154
173
|
args: Record<string, unknown>;
|
|
155
174
|
binaryBytes?: Uint8Array;
|
|
@@ -174,6 +193,13 @@ export declare function binaryWritePayload(parsed: ParsedExecRequest): {
|
|
|
174
193
|
path: string;
|
|
175
194
|
data: Uint8Array;
|
|
176
195
|
} | undefined;
|
|
196
|
+
/**
|
|
197
|
+
* Replace the prompt-derived task title with Cursor's TaskToolCall description
|
|
198
|
+
* when the display call is correlated via tool_call_id. SubagentArgs itself has
|
|
199
|
+
* no description field, so without this OpenCode shows a cut-off first-five-words
|
|
200
|
+
* slice of the prompt.
|
|
201
|
+
*/
|
|
202
|
+
export declare function preferCorrelatedTaskDescription(parsed: ParsedExecRequest, description: string | undefined): void;
|
|
177
203
|
/**
|
|
178
204
|
* Map Cursor McpArgs back to the OpenCode tool id.
|
|
179
205
|
* Prefers provider_identifier + bare tool_name (github + create_pull_request
|