cursor-opencode-provider 0.7.4 → 0.7.6

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.
@@ -16,10 +16,12 @@ export const REQUEST_CONTEXT_RESULT_FIELD = 10;
16
16
  export const OPENCODE_1_TOOL_DIALECT = {
17
17
  filePathKey: "filePath",
18
18
  shellTool: "bash",
19
+ skillArgKey: "name",
19
20
  };
20
21
  export const OPENCODE_2_TOOL_DIALECT = {
21
22
  filePathKey: "path",
22
23
  shellTool: "shell",
24
+ skillArgKey: "id",
23
25
  };
24
26
  function jsonSchemaProperties(schema) {
25
27
  if (!schema || typeof schema !== "object" || Array.isArray(schema))
@@ -46,6 +48,7 @@ export function opencodePathArg(args) {
46
48
  /**
47
49
  * Infer the host tool dialect from advertised AI SDK schemas.
48
50
  * OpenCode 2.0 read/edit/write require `path` and rename bash → `shell`.
51
+ * Skill: OpenCode 1.x requires `name`; OpenCode 2.0 requires `id`.
49
52
  */
50
53
  export function hostToolDialectFromTools(tools, defaultDialect = OPENCODE_1_TOOL_DIALECT) {
51
54
  let filePathKey;
@@ -71,7 +74,21 @@ export function hostToolDialectFromTools(tools, defaultDialect = OPENCODE_1_TOOL
71
74
  // OpenCode 2 hosts advertise `shell` and `path` together; use that when schemas are opaque.
72
75
  filePathKey = shellTool === "shell" ? "path" : defaultDialect.filePathKey;
73
76
  }
74
- return { filePathKey, shellTool };
77
+ const skillProps = jsonSchemaProperties(tools.find((tool) => tool.name === "skill")?.inputSchema);
78
+ let skillArgKey;
79
+ if (skillProps) {
80
+ if ("id" in skillProps && !("name" in skillProps))
81
+ skillArgKey = "id";
82
+ else if ("name" in skillProps)
83
+ skillArgKey = "name";
84
+ }
85
+ if (!skillArgKey) {
86
+ // Opaque skill schema: OpenCode 2 pairs `shell`+`path` with skill `id`.
87
+ skillArgKey = shellTool === "shell" && filePathKey === "path"
88
+ ? "id"
89
+ : defaultDialect.skillArgKey;
90
+ }
91
+ return { filePathKey, shellTool, skillArgKey };
75
92
  }
76
93
  function assignHostFilePath(args, filePath, dialect) {
77
94
  delete args.filePath;
@@ -1257,6 +1274,18 @@ export function mapCursorArgsToOpencode(toolName, raw, execVariant, dialect = OP
1257
1274
  args.path = path;
1258
1275
  return { toolName: "glob", args };
1259
1276
  }
1277
+ case "skill": {
1278
+ // OpenCode 1.x skill requires `name`; OpenCode 2.0 requires `id`. Both
1279
+ // reject the other key (`additionalProperties: false`). Accept either
1280
+ // model/Cursor shape and emit only the dialect key.
1281
+ const value = str(cleaned[dialect.skillArgKey]) ?? str(cleaned.id) ?? str(cleaned.name);
1282
+ const args = { ...cleaned };
1283
+ delete args.id;
1284
+ delete args.name;
1285
+ if (value)
1286
+ args[dialect.skillArgKey] = value;
1287
+ return { toolName: "skill", args };
1288
+ }
1260
1289
  default:
1261
1290
  return { toolName, args: cleaned };
1262
1291
  }
@@ -7,9 +7,12 @@
7
7
  * time is wrong for any session but the one open when the daemon started.
8
8
  *
9
9
  * The 2.0 runtime names a session directory in two places:
10
- * - request header `x-opencode-directory` (per-request; preferred in the LM)
11
- * - `ctx.session.get()` → flat `info.directory`, or legacy
12
- * `info.location.directory`, reachable from `session.hook("context")`
10
+ * - request header `x-opencode-directory` (per-request; preferred in the LM).
11
+ * The host does not set it on model requests; the opencode2 plugin adds it
12
+ * from `session.hook("model.request")`, so it travels with the request
13
+ * instead of depending on this module's in-memory state.
14
+ * - `ctx.session.get()` → `info.location.directory`, reachable from session
15
+ * hooks
13
16
  *
14
17
  * This module records the session-get value by id so the language model can
15
18
  * fall back when the header is absent, same mechanism as `compaction-marker.ts`.
@@ -7,9 +7,12 @@
7
7
  * time is wrong for any session but the one open when the daemon started.
8
8
  *
9
9
  * The 2.0 runtime names a session directory in two places:
10
- * - request header `x-opencode-directory` (per-request; preferred in the LM)
11
- * - `ctx.session.get()` → flat `info.directory`, or legacy
12
- * `info.location.directory`, reachable from `session.hook("context")`
10
+ * - request header `x-opencode-directory` (per-request; preferred in the LM).
11
+ * The host does not set it on model requests; the opencode2 plugin adds it
12
+ * from `session.hook("model.request")`, so it travels with the request
13
+ * instead of depending on this module's in-memory state.
14
+ * - `ctx.session.get()` → `info.location.directory`, reachable from session
15
+ * hooks
13
16
  *
14
17
  * This module records the session-get value by id so the language model can
15
18
  * fall back when the header is absent, same mechanism as `compaction-marker.ts`.
@@ -17,6 +20,7 @@
17
20
  * Bounded so a long-lived server cannot accumulate ids for dead sessions.
18
21
  */
19
22
  import path from "node:path";
23
+ import { trace } from "./debug.js";
20
24
  const MAX_TRACKED_SESSIONS = 256;
21
25
  const sessionDirectories = new Map();
22
26
  export function markSessionDirectory(sessionID, directory) {
@@ -61,7 +65,10 @@ export function opencodeDirectoryHeader(headers) {
61
65
  * Prefer the per-request header, then the session mark, then static options/cwd.
62
66
  */
63
67
  export function resolveSessionWorkspaceRoot(input) {
64
- return path.resolve(opencodeDirectoryHeader(input.headers) ??
65
- getSessionDirectory(input.sessionKey) ??
66
- (input.workspaceRoot || input.cwd || process.cwd()));
68
+ const resolved = opencodeDirectoryHeader(input.headers) ?? getSessionDirectory(input.sessionKey);
69
+ if (resolved)
70
+ return path.resolve(resolved);
71
+ const fallback = input.workspaceRoot || input.cwd || process.cwd();
72
+ trace(`session directory: no header or session mark sessionKey=${input.sessionKey ?? "-"}; fallback=${fallback}`);
73
+ return path.resolve(fallback);
67
74
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-opencode-provider",
3
- "version": "0.7.4",
3
+ "version": "0.7.6",
4
4
  "description": "Use Cursor subscription models from OpenCode via Cursor's Connect-RPC agent protocol",
5
5
  "type": "module",
6
6
  "license": "MIT",