cursor-opencode-provider 0.5.0 → 0.6.1
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 +22 -13
- package/dist/context/agents.d.ts +1 -1
- package/dist/context/agents.js +1 -1
- package/dist/context/auth-store.js +2 -2
- package/dist/context/build.d.ts +3 -1
- package/dist/context/build.js +4 -2
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.js +1 -1
- package/dist/context/paths.d.ts +34 -53
- package/dist/context/paths.js +54 -118
- package/dist/context/plugins.js +1 -0
- package/dist/context/rules.js +2 -1
- package/dist/context/skills.js +1 -0
- package/dist/image-save-tool.d.ts +19 -0
- package/dist/image-save-tool.js +25 -0
- package/dist/image-save.d.ts +58 -0
- package/dist/image-save.js +189 -0
- package/dist/image-staging.d.ts +59 -0
- package/dist/image-staging.js +91 -0
- package/dist/index.d.ts +1 -1
- package/dist/language-model.d.ts +3 -2
- package/dist/language-model.js +589 -41
- package/dist/plan-execution-kickoff.d.ts +55 -0
- package/dist/plan-execution-kickoff.js +162 -0
- package/dist/plugin-opencode2.js +52 -3
- package/dist/plugin-v2.d.ts +11 -9
- package/dist/plugin-v2.js +6 -13
- package/dist/plugin.d.ts +9 -0
- package/dist/plugin.js +61 -5
- package/dist/pricing-data.d.ts +16 -4
- package/dist/pricing-data.js +16 -4
- package/dist/protocol/ask-question.d.ts +133 -0
- package/dist/protocol/ask-question.js +320 -0
- package/dist/protocol/conversation-bind.d.ts +1 -0
- package/dist/protocol/conversation-bind.js +3 -0
- package/dist/protocol/create-plan.d.ts +141 -0
- package/dist/protocol/create-plan.js +373 -0
- package/dist/protocol/generate-image.d.ts +54 -0
- package/dist/protocol/generate-image.js +101 -0
- package/dist/protocol/interactions.d.ts +110 -8
- package/dist/protocol/interactions.js +295 -35
- package/dist/protocol/messages.js +118 -6
- package/dist/protocol/struct.js +1 -1
- package/dist/protocol/switch-mode.d.ts +178 -0
- package/dist/protocol/switch-mode.js +417 -0
- package/dist/protocol/tool-call-bridge.js +10 -5
- package/dist/protocol/tools.d.ts +27 -11
- package/dist/protocol/tools.js +98 -42
- package/dist/usage.d.ts +2 -2
- package/dist/usage.js +2 -2
- package/dist/web-search-tool.d.ts +22 -26
- package/dist/web-search-tool.js +25 -15
- package/package.json +7 -4
package/dist/protocol/tools.d.ts
CHANGED
|
@@ -71,9 +71,9 @@ export type HostSubagentDefinition = {
|
|
|
71
71
|
description?: string;
|
|
72
72
|
};
|
|
73
73
|
export type HostSubagentCatalog = {
|
|
74
|
-
executor?: "task"
|
|
74
|
+
executor?: "task";
|
|
75
75
|
agents: HostSubagentDefinition[];
|
|
76
|
-
/** True when the
|
|
76
|
+
/** True when the OpenCode task tool supplied its complete, permission-filtered catalog. */
|
|
77
77
|
complete: boolean;
|
|
78
78
|
};
|
|
79
79
|
/** Extract the current host's permission-filtered Task/Actor recipient catalog. */
|
|
@@ -151,7 +151,28 @@ export declare function parseExecServerMessage(msg: Record<string, unknown>): Pa
|
|
|
151
151
|
export declare function mapCursorArgsToOpencode(toolName: string, raw: Record<string, unknown>, execVariant?: string): {
|
|
152
152
|
toolName: string;
|
|
153
153
|
args: Record<string, unknown>;
|
|
154
|
+
binaryBytes?: Uint8Array;
|
|
154
155
|
};
|
|
156
|
+
/**
|
|
157
|
+
* Decode `WriteArgs.file_bytes`. `encoding_hint` names the file's original
|
|
158
|
+
* encoding; OpenCode's `write` takes a string, so an unknown encoding label
|
|
159
|
+
* falls back to UTF-8 rather than dropping the write.
|
|
160
|
+
*
|
|
161
|
+
* Returns undefined when the bytes are not text in that encoding. A non-fatal
|
|
162
|
+
* decode would replace every undecodable sequence with U+FFFD and hand the host
|
|
163
|
+
* a corrupted file that still reports success, so binary content must take the
|
|
164
|
+
* byte-preserving path instead (see `isBinaryWriteRequest`).
|
|
165
|
+
*/
|
|
166
|
+
export declare function decodeWriteBytes(bytes: Uint8Array, encodingHint?: string): string | undefined;
|
|
167
|
+
/**
|
|
168
|
+
* True when a parsed `write` request carries bytes that are not text. Such a
|
|
169
|
+
* request cannot go to OpenCode's `write` tool at all — it takes a string, and
|
|
170
|
+
* additionally BOM-splits, diffs, and runs `Format.file()` on what it writes.
|
|
171
|
+
*/
|
|
172
|
+
export declare function binaryWritePayload(parsed: ParsedExecRequest): {
|
|
173
|
+
path: string;
|
|
174
|
+
data: Uint8Array;
|
|
175
|
+
} | undefined;
|
|
155
176
|
/**
|
|
156
177
|
* Map Cursor McpArgs back to the OpenCode tool id.
|
|
157
178
|
* Prefers provider_identifier + bare tool_name (github + create_pull_request
|
|
@@ -162,15 +183,10 @@ export declare function mcpRealToolName(mcpArgs: Record<string, unknown>, defaul
|
|
|
162
183
|
* A read target addressed by URI scheme rather than by local filesystem path.
|
|
163
184
|
*
|
|
164
185
|
* Cursor's LocalReadExecutor only ever reads local files, so this provider
|
|
165
|
-
* mirrors its pre-execution validation.
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
* (`tools.xdev`, on by default). Resolving those against the workspace root
|
|
170
|
-
* mangles `xd://everything_echo` into `<root>/xd:/everything_echo`, which stats
|
|
171
|
-
* as missing and gets refused as `file_not_found` — so the host's read tool is
|
|
172
|
-
* never called and every xd://-mounted MCP tool is unreachable through this
|
|
173
|
-
* provider. Such targets must be forwarded untouched and left to the host.
|
|
186
|
+
* mirrors its pre-execution validation. An advertised host read tool may also
|
|
187
|
+
* accept URLs or internal URI schemes. Resolving those against the workspace
|
|
188
|
+
* root mangles the URI and produces a false `file_not_found`, so URI targets
|
|
189
|
+
* are forwarded untouched and left to the advertised executor.
|
|
174
190
|
*
|
|
175
191
|
* Requires a scheme of two or more characters followed by `://`, so Windows
|
|
176
192
|
* drive letters (`C:\src`, `C:/src`) stay local paths.
|
package/dist/protocol/tools.js
CHANGED
|
@@ -57,7 +57,9 @@ export function resolveToolServerIdentity(opencodeName, defaultServer = "opencod
|
|
|
57
57
|
* reconstructed in `mcpRealToolName`.
|
|
58
58
|
*/
|
|
59
59
|
export function toolsToDescriptors(tools, providerIdentifier = "opencode", knownMcpServers = []) {
|
|
60
|
-
return tools
|
|
60
|
+
return [...tools]
|
|
61
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
62
|
+
.map((t) => {
|
|
61
63
|
const id = resolveToolServerIdentity(t.sourceName ?? t.name, providerIdentifier, knownMcpServers);
|
|
62
64
|
const collisionSafeAlias = COLLISION_SAFE_ALIASES.has(t.name);
|
|
63
65
|
return {
|
|
@@ -299,6 +301,10 @@ const PRESERVE_EMPTY_STRING_KEYS = new Set([
|
|
|
299
301
|
"file_text",
|
|
300
302
|
"fileText",
|
|
301
303
|
"stream_content",
|
|
304
|
+
// Some advertised edit schemas use a single textual `input` payload. Preserve
|
|
305
|
+
// an empty value so the executor reports the real edit error rather than a
|
|
306
|
+
// misleading missing-property error.
|
|
307
|
+
"input",
|
|
302
308
|
"oldString",
|
|
303
309
|
"old_string",
|
|
304
310
|
"newString",
|
|
@@ -396,18 +402,16 @@ function subagentTypeEnumValues(schema) {
|
|
|
396
402
|
}
|
|
397
403
|
/** Extract the current host's permission-filtered Task/Actor recipient catalog. */
|
|
398
404
|
export function extractHostSubagentCatalog(tools) {
|
|
399
|
-
const executorTool = tools.find((tool) => tool.name === "
|
|
400
|
-
tools.find((tool) => tool.name === "task");
|
|
405
|
+
const executorTool = tools.find((tool) => tool.name === "task");
|
|
401
406
|
if (!executorTool)
|
|
402
407
|
return { agents: [], complete: true };
|
|
403
408
|
const described = parseSubagentDescriptionCatalog(executorTool.description);
|
|
404
409
|
const enumNames = subagentTypeEnumValues(executorTool.inputSchema);
|
|
405
410
|
const descriptions = new Map(described.agents.map((agent) => [agent.name, agent.description]));
|
|
406
|
-
//
|
|
407
|
-
// rather than every non-primary agent), so structured names are authoritative.
|
|
411
|
+
// A structured subagent_type enum is stricter than prose, so it is authoritative.
|
|
408
412
|
const names = new Set(enumNames.length > 0 ? enumNames : described.agents.map((agent) => agent.name));
|
|
409
413
|
return {
|
|
410
|
-
executor:
|
|
414
|
+
executor: "task",
|
|
411
415
|
agents: [...names].map((name) => ({
|
|
412
416
|
name,
|
|
413
417
|
...(descriptions.get(name) ? { description: descriptions.get(name) } : {}),
|
|
@@ -467,9 +471,9 @@ export function remapNativeSubagentForCatalog(parsed, advertisedToolNames, catal
|
|
|
467
471
|
if (parsed.resultField !== "subagent_result" || parsed.toolName !== "task")
|
|
468
472
|
return;
|
|
469
473
|
const advertised = new Set(advertisedToolNames);
|
|
470
|
-
|
|
471
|
-
if (!executor)
|
|
474
|
+
if (!advertised.has("task"))
|
|
472
475
|
return;
|
|
476
|
+
const executor = "task";
|
|
473
477
|
const description = str(parsed.args.description) ?? "";
|
|
474
478
|
const prompt = str(parsed.args.prompt) ?? "";
|
|
475
479
|
const cursorSubagentType = str(parsed.resultMetadata?.cursor_subagent_type) ??
|
|
@@ -484,24 +488,12 @@ export function remapNativeSubagentForCatalog(parsed, advertisedToolNames, catal
|
|
|
484
488
|
}
|
|
485
489
|
const resumeAgentId = str(parsed.args.task_id);
|
|
486
490
|
parsed.toolName = executor;
|
|
487
|
-
if (executor === "task") {
|
|
488
|
-
parsed.args = {
|
|
489
|
-
description,
|
|
490
|
-
prompt,
|
|
491
|
-
subagent_type: subagentType,
|
|
492
|
-
...(resumeAgentId ? { task_id: resumeAgentId } : {}),
|
|
493
|
-
...(parsed.args.background === true ? { background: true } : {}),
|
|
494
|
-
};
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
491
|
parsed.args = {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
...(resumeAgentId ? { actor_id: resumeAgentId } : {}),
|
|
504
|
-
},
|
|
492
|
+
description,
|
|
493
|
+
prompt,
|
|
494
|
+
subagent_type: subagentType,
|
|
495
|
+
...(resumeAgentId ? { task_id: resumeAgentId } : {}),
|
|
496
|
+
...(parsed.args.background === true ? { background: true } : {}),
|
|
505
497
|
};
|
|
506
498
|
}
|
|
507
499
|
/** Matches Cursor's own pre-write read threshold (`local-exec` 52428800). */
|
|
@@ -682,6 +674,12 @@ export function remapCorrelatedEditWriteForCatalog(parsed, advertisedToolNames,
|
|
|
682
674
|
export function remapEditToolsForCatalog(parsed, advertisedToolNames, workspaceRoot) {
|
|
683
675
|
if (parsed.toolName !== "write" && parsed.toolName !== "edit")
|
|
684
676
|
return;
|
|
677
|
+
// `apply_patch` envelopes are text. A binary write has no patch form at all,
|
|
678
|
+
// so it must stay a write and reach the byte-preserving path — converting it
|
|
679
|
+
// here would drop the bytes and report a patch-translation failure instead of
|
|
680
|
+
// the image Cursor asked to save.
|
|
681
|
+
if (binaryWritePayload(parsed))
|
|
682
|
+
return;
|
|
685
683
|
const advertised = new Set(advertisedToolNames);
|
|
686
684
|
if (advertised.has(parsed.toolName) || !advertised.has(APPLY_PATCH_TOOL))
|
|
687
685
|
return;
|
|
@@ -940,7 +938,19 @@ export function parseExecServerMessage(msg) {
|
|
|
940
938
|
toolName: mapped.toolName,
|
|
941
939
|
args: mapped.args,
|
|
942
940
|
resultField,
|
|
943
|
-
...(resultMetadata
|
|
941
|
+
...(resultMetadata || mapped.binaryBytes
|
|
942
|
+
? {
|
|
943
|
+
resultMetadata: {
|
|
944
|
+
...resultMetadata,
|
|
945
|
+
...(mapped.binaryBytes
|
|
946
|
+
? {
|
|
947
|
+
binaryWriteBytes: mapped.binaryBytes,
|
|
948
|
+
path: str(rawArgs.path) ?? str(rawArgs.file_path) ?? "",
|
|
949
|
+
}
|
|
950
|
+
: {}),
|
|
951
|
+
},
|
|
952
|
+
}
|
|
953
|
+
: {}),
|
|
944
954
|
};
|
|
945
955
|
}
|
|
946
956
|
function shellStreamResultMetadata(raw) {
|
|
@@ -1020,14 +1030,31 @@ export function mapCursorArgsToOpencode(toolName, raw, execVariant) {
|
|
|
1020
1030
|
// non-empty and only falls back to `file_text`; mirror that order so a
|
|
1021
1031
|
// byte-encoded write is not silently seen as empty content.
|
|
1022
1032
|
const bytes = bytesValue(cleaned.file_bytes) ?? bytesValue(cleaned.fileBytes);
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1033
|
+
let content;
|
|
1034
|
+
if (bytes !== undefined) {
|
|
1035
|
+
content = decodeWriteBytes(bytes, str(cleaned.encoding_hint) ?? str(cleaned.encodingHint));
|
|
1036
|
+
if (content === undefined) {
|
|
1037
|
+
// Binary. Surface the raw bytes to the caller instead of a lossy
|
|
1038
|
+
// decode; `binaryWritePayload` picks them up after parsing.
|
|
1039
|
+
return { toolName: "write", args, binaryBytes: bytes };
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
else {
|
|
1043
|
+
content = stringValue(cleaned.content)
|
|
1044
|
+
?? stringValue(cleaned.file_text)
|
|
1045
|
+
?? stringValue(cleaned.fileText);
|
|
1046
|
+
}
|
|
1026
1047
|
if (content !== undefined)
|
|
1027
1048
|
args.content = content;
|
|
1028
1049
|
return { toolName: "write", args };
|
|
1029
1050
|
}
|
|
1030
1051
|
case "edit": {
|
|
1052
|
+
// Preserve an opaque textual edit payload when the advertised executor
|
|
1053
|
+
// accepts that generic shape; rebuilding only OpenCode's targeted fields
|
|
1054
|
+
// would silently turn a valid call into `{}`.
|
|
1055
|
+
if (typeof cleaned.input === "string") {
|
|
1056
|
+
return { toolName: "edit", args: { input: cleaned.input } };
|
|
1057
|
+
}
|
|
1031
1058
|
const args = {};
|
|
1032
1059
|
const filePath = str(cleaned.filePath) ?? str(cleaned.path) ?? str(cleaned.file_path);
|
|
1033
1060
|
if (filePath)
|
|
@@ -1105,22 +1132,56 @@ function bytesValue(v) {
|
|
|
1105
1132
|
}
|
|
1106
1133
|
return undefined;
|
|
1107
1134
|
}
|
|
1135
|
+
/** UTF-16/32 embed NUL bytes in ordinary text; 8-bit encodings do not. */
|
|
1136
|
+
const WIDE_TEXT_ENCODING = /^utf-?(16|32)/;
|
|
1108
1137
|
/**
|
|
1109
1138
|
* Decode `WriteArgs.file_bytes`. `encoding_hint` names the file's original
|
|
1110
|
-
* encoding; OpenCode's `write` takes a string, so
|
|
1139
|
+
* encoding; OpenCode's `write` takes a string, so an unknown encoding label
|
|
1111
1140
|
* falls back to UTF-8 rather than dropping the write.
|
|
1141
|
+
*
|
|
1142
|
+
* Returns undefined when the bytes are not text in that encoding. A non-fatal
|
|
1143
|
+
* decode would replace every undecodable sequence with U+FFFD and hand the host
|
|
1144
|
+
* a corrupted file that still reports success, so binary content must take the
|
|
1145
|
+
* byte-preserving path instead (see `isBinaryWriteRequest`).
|
|
1112
1146
|
*/
|
|
1113
|
-
function decodeWriteBytes(bytes, encodingHint) {
|
|
1147
|
+
export function decodeWriteBytes(bytes, encodingHint) {
|
|
1114
1148
|
const encoding = encodingHint?.trim().toLowerCase().replace(/[_ ]/g, "-");
|
|
1149
|
+
let decoder;
|
|
1150
|
+
let label = "utf-8";
|
|
1115
1151
|
if (encoding && encoding !== "utf-8" && encoding !== "utf8") {
|
|
1116
1152
|
try {
|
|
1117
|
-
|
|
1153
|
+
decoder = new TextDecoder(encoding, { fatal: true });
|
|
1154
|
+
label = encoding;
|
|
1118
1155
|
}
|
|
1119
1156
|
catch {
|
|
1120
1157
|
trace(`write: unsupported encoding_hint ${JSON.stringify(encodingHint)} — decoding as utf-8`);
|
|
1121
1158
|
}
|
|
1122
1159
|
}
|
|
1123
|
-
|
|
1160
|
+
if (!decoder)
|
|
1161
|
+
decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1162
|
+
// Single-byte encodings decode any byte sequence without error, so a decode
|
|
1163
|
+
// that cannot fail is not on its own evidence of text.
|
|
1164
|
+
if (!WIDE_TEXT_ENCODING.test(label) && bytes.includes(0))
|
|
1165
|
+
return undefined;
|
|
1166
|
+
try {
|
|
1167
|
+
return decoder.decode(bytes);
|
|
1168
|
+
}
|
|
1169
|
+
catch {
|
|
1170
|
+
return undefined;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* True when a parsed `write` request carries bytes that are not text. Such a
|
|
1175
|
+
* request cannot go to OpenCode's `write` tool at all — it takes a string, and
|
|
1176
|
+
* additionally BOM-splits, diffs, and runs `Format.file()` on what it writes.
|
|
1177
|
+
*/
|
|
1178
|
+
export function binaryWritePayload(parsed) {
|
|
1179
|
+
if (parsed.toolName !== "write")
|
|
1180
|
+
return undefined;
|
|
1181
|
+
const data = parsed.resultMetadata?.binaryWriteBytes;
|
|
1182
|
+
if (!(data instanceof Uint8Array))
|
|
1183
|
+
return undefined;
|
|
1184
|
+
return { path: str(parsed.resultMetadata?.path) ?? "", data };
|
|
1124
1185
|
}
|
|
1125
1186
|
function num(v) {
|
|
1126
1187
|
if (typeof v === "number" && Number.isFinite(v))
|
|
@@ -1203,15 +1264,10 @@ function untildify(input) {
|
|
|
1203
1264
|
* A read target addressed by URI scheme rather than by local filesystem path.
|
|
1204
1265
|
*
|
|
1205
1266
|
* Cursor's LocalReadExecutor only ever reads local files, so this provider
|
|
1206
|
-
* mirrors its pre-execution validation.
|
|
1207
|
-
*
|
|
1208
|
-
*
|
|
1209
|
-
*
|
|
1210
|
-
* (`tools.xdev`, on by default). Resolving those against the workspace root
|
|
1211
|
-
* mangles `xd://everything_echo` into `<root>/xd:/everything_echo`, which stats
|
|
1212
|
-
* as missing and gets refused as `file_not_found` — so the host's read tool is
|
|
1213
|
-
* never called and every xd://-mounted MCP tool is unreachable through this
|
|
1214
|
-
* provider. Such targets must be forwarded untouched and left to the host.
|
|
1267
|
+
* mirrors its pre-execution validation. An advertised host read tool may also
|
|
1268
|
+
* accept URLs or internal URI schemes. Resolving those against the workspace
|
|
1269
|
+
* root mangles the URI and produces a false `file_not_found`, so URI targets
|
|
1270
|
+
* are forwarded untouched and left to the advertised executor.
|
|
1215
1271
|
*
|
|
1216
1272
|
* Requires a scheme of two or more characters followed by `://`, so Windows
|
|
1217
1273
|
* drive letters (`C:\src`, `C:/src`) stay local paths.
|
package/dist/usage.d.ts
CHANGED
|
@@ -49,8 +49,8 @@ export declare function formatCursorCacheDiagnostics(counters: CursorUsageCounte
|
|
|
49
49
|
export declare function formatTurnUsageValidation(counters: CursorUsageCounters, usage: LanguageModelV3Usage, tokenDetails?: CursorConversationTokenDetails, contextSource?: CursorContextUsageSource): string;
|
|
50
50
|
/** OpenCode requires a usage object at every step boundary. */
|
|
51
51
|
export declare function emptyLanguageModelV3Usage(): LanguageModelV3Usage;
|
|
52
|
-
/**
|
|
53
|
-
export declare function
|
|
52
|
+
/** Project nested V3 usage into the common flat AI-SDK counter shape. */
|
|
53
|
+
export declare function flatUsageFromV3(usage: LanguageModelV3Usage): {
|
|
54
54
|
inputTokens: number;
|
|
55
55
|
outputTokens: number;
|
|
56
56
|
reasoningTokens: number;
|
package/dist/usage.js
CHANGED
|
@@ -217,8 +217,8 @@ export function emptyLanguageModelV3Usage() {
|
|
|
217
217
|
reasoningTokens: 0,
|
|
218
218
|
});
|
|
219
219
|
}
|
|
220
|
-
/**
|
|
221
|
-
export function
|
|
220
|
+
/** Project nested V3 usage into the common flat AI-SDK counter shape. */
|
|
221
|
+
export function flatUsageFromV3(usage) {
|
|
222
222
|
const input = usage.inputTokens;
|
|
223
223
|
const output = usage.outputTokens;
|
|
224
224
|
const noCache = input.noCache ?? 0;
|
|
@@ -1,33 +1,29 @@
|
|
|
1
|
+
import { executeOpenCodeWebSearch } from "./web-tools.js";
|
|
2
|
+
type ToolFactory = {
|
|
3
|
+
tool: (input: Record<string, unknown>) => Record<string, unknown>;
|
|
4
|
+
schema: {
|
|
5
|
+
string: () => any;
|
|
6
|
+
number: () => any;
|
|
7
|
+
enum: (values: readonly string[]) => any;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
/** Build the classic OpenCode web-search tool with the host's own Zod helper. */
|
|
11
|
+
export declare function createOpenCodeWebSearchTool(factory: ToolFactory): Record<string, unknown>;
|
|
1
12
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* implementation in that module. 2.0's package root exports no `tool`, so
|
|
7
|
-
* importing it from a module on the 2.0 graph would throw during plugin load.
|
|
8
|
-
* Only `plugin.ts` (classic hooks) may import this file.
|
|
13
|
+
* Host-neutral JSON-schema fallback when the classic helper is unavailable.
|
|
14
|
+
* OpenCode's legacy schema adapter marks every listed property required, so
|
|
15
|
+
* expose only the genuinely required query rather than turning four optional
|
|
16
|
+
* tuning fields into mandatory inputs.
|
|
9
17
|
*/
|
|
10
18
|
export declare const openCodeWebSearchTool: {
|
|
11
19
|
description: string;
|
|
12
20
|
args: {
|
|
13
|
-
query:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
preferred: "preferred";
|
|
18
|
-
}>>;
|
|
19
|
-
type: import("zod").ZodOptional<import("zod").ZodEnum<{
|
|
20
|
-
fast: "fast";
|
|
21
|
-
auto: "auto";
|
|
22
|
-
deep: "deep";
|
|
23
|
-
}>>;
|
|
24
|
-
contextMaxCharacters: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
21
|
+
query: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
25
|
};
|
|
26
|
-
execute
|
|
27
|
-
query: string;
|
|
28
|
-
numResults?: number | undefined;
|
|
29
|
-
livecrawl?: "fallback" | "preferred" | undefined;
|
|
30
|
-
type?: "fast" | "auto" | "deep" | undefined;
|
|
31
|
-
contextMaxCharacters?: number | undefined;
|
|
32
|
-
}, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
|
|
26
|
+
execute: typeof executeOpenCodeWebSearch;
|
|
33
27
|
};
|
|
28
|
+
export type { ToolFactory as OpenCodeToolFactory };
|
|
29
|
+
export declare function createOpenCodeWebSearchToolFromPlugin(pluginModule: any): Record<string, unknown>;
|
package/dist/web-search-tool.js
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin";
|
|
2
1
|
import { executeOpenCodeWebSearch } from "./web-tools.js";
|
|
2
|
+
/** Build the classic OpenCode web-search tool with the host's own Zod helper. */
|
|
3
|
+
export function createOpenCodeWebSearchTool(factory) {
|
|
4
|
+
const schema = factory.schema;
|
|
5
|
+
return factory.tool({
|
|
6
|
+
description: "Search the web for current information using OpenCode's web search backend.",
|
|
7
|
+
args: {
|
|
8
|
+
query: schema.string().describe("Web search query"),
|
|
9
|
+
numResults: schema.number().int().min(1).max(20).optional(),
|
|
10
|
+
livecrawl: schema.enum(["fallback", "preferred"]).optional(),
|
|
11
|
+
type: schema.enum(["auto", "fast", "deep"]).optional(),
|
|
12
|
+
contextMaxCharacters: schema.number().int().positive().optional(),
|
|
13
|
+
},
|
|
14
|
+
execute: executeOpenCodeWebSearch,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
3
17
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* implementation in that module. 2.0's package root exports no `tool`, so
|
|
9
|
-
* importing it from a module on the 2.0 graph would throw during plugin load.
|
|
10
|
-
* Only `plugin.ts` (classic hooks) may import this file.
|
|
18
|
+
* Host-neutral JSON-schema fallback when the classic helper is unavailable.
|
|
19
|
+
* OpenCode's legacy schema adapter marks every listed property required, so
|
|
20
|
+
* expose only the genuinely required query rather than turning four optional
|
|
21
|
+
* tuning fields into mandatory inputs.
|
|
11
22
|
*/
|
|
12
|
-
export const openCodeWebSearchTool =
|
|
23
|
+
export const openCodeWebSearchTool = {
|
|
13
24
|
description: "Search the web for current information using OpenCode's web search backend.",
|
|
14
25
|
args: {
|
|
15
|
-
query:
|
|
16
|
-
numResults: tool.schema.number().int().min(1).max(20).optional(),
|
|
17
|
-
livecrawl: tool.schema.enum(["fallback", "preferred"]).optional(),
|
|
18
|
-
type: tool.schema.enum(["auto", "fast", "deep"]).optional(),
|
|
19
|
-
contextMaxCharacters: tool.schema.number().int().positive().optional(),
|
|
26
|
+
query: { type: "string", description: "Web search query" },
|
|
20
27
|
},
|
|
21
28
|
execute: executeOpenCodeWebSearch,
|
|
22
|
-
}
|
|
29
|
+
};
|
|
30
|
+
export function createOpenCodeWebSearchToolFromPlugin(pluginModule) {
|
|
31
|
+
return createOpenCodeWebSearchTool({ tool: pluginModule.tool, schema: pluginModule.tool.schema });
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-opencode-provider",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Use Cursor subscription models from OpenCode via Cursor's Connect-RPC agent protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -55,6 +55,10 @@
|
|
|
55
55
|
"./auth": {
|
|
56
56
|
"types": "./dist/auth.d.ts",
|
|
57
57
|
"import": "./dist/auth.js"
|
|
58
|
+
},
|
|
59
|
+
"./image-save": {
|
|
60
|
+
"types": "./dist/image-save.d.ts",
|
|
61
|
+
"import": "./dist/image-save.js"
|
|
58
62
|
}
|
|
59
63
|
},
|
|
60
64
|
"files": [
|
|
@@ -80,11 +84,10 @@
|
|
|
80
84
|
"protobufjs": "^7.6.5"
|
|
81
85
|
},
|
|
82
86
|
"peerDependencies": {
|
|
83
|
-
"@opencode-ai/plugin": "^1.17.13"
|
|
84
|
-
"@opencode-compat/profile": ">=0.1.0"
|
|
87
|
+
"@opencode-ai/plugin": "^1.17.13"
|
|
85
88
|
},
|
|
86
89
|
"peerDependenciesMeta": {
|
|
87
|
-
"@opencode-
|
|
90
|
+
"@opencode-ai/plugin": {
|
|
88
91
|
"optional": true
|
|
89
92
|
}
|
|
90
93
|
},
|