reeval 0.0.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.
Files changed (213) hide show
  1. package/LICENSE +202 -0
  2. package/THIRD_PARTY_NOTICES.md +2160 -0
  3. package/assets/package.json +9 -0
  4. package/assets/pnpm-lock.yaml +1336 -0
  5. package/dist/agent-DVHkx8aT.js +532 -0
  6. package/dist/agent-DVHkx8aT.js.map +1 -0
  7. package/dist/analysis-CxSCt1YY.js +16 -0
  8. package/dist/analysis-CxSCt1YY.js.map +1 -0
  9. package/dist/assemble-B-nmz8oH.js +695 -0
  10. package/dist/assemble-B-nmz8oH.js.map +1 -0
  11. package/dist/assemble-CmRVDZ2Q.js +2 -0
  12. package/dist/attempt-BdRQ9jKX.js +176 -0
  13. package/dist/attempt-BdRQ9jKX.js.map +1 -0
  14. package/dist/bridge/index.mjs +1911 -0
  15. package/dist/bridge/package.json +13 -0
  16. package/dist/bridge/pnpm-lock.yaml +1041 -0
  17. package/dist/bridge/pnpm-workspace.yaml +2 -0
  18. package/dist/budget-DBUrH4jX.js +8 -0
  19. package/dist/budget-DBUrH4jX.js.map +1 -0
  20. package/dist/cli--CQ3Iy9q.js +1865 -0
  21. package/dist/cli--CQ3Iy9q.js.map +1 -0
  22. package/dist/cli-spec-DKAu09Gi.js +478 -0
  23. package/dist/cli-spec-DKAu09Gi.js.map +1 -0
  24. package/dist/cli.d.ts +1 -0
  25. package/dist/cli.js +8 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/colors-QV9dFVhN.js +34 -0
  28. package/dist/colors-QV9dFVhN.js.map +1 -0
  29. package/dist/command-BSJzHMtP.js +280 -0
  30. package/dist/command-BSJzHMtP.js.map +1 -0
  31. package/dist/command-CEm8PZ1N.js +1043 -0
  32. package/dist/command-CEm8PZ1N.js.map +1 -0
  33. package/dist/command-D-jnQWVG.js +673 -0
  34. package/dist/command-D-jnQWVG.js.map +1 -0
  35. package/dist/command-DQAvbG7d.js +432 -0
  36. package/dist/command-DQAvbG7d.js.map +1 -0
  37. package/dist/commands-A4udfyKZ.js +2 -0
  38. package/dist/commands-CF8mOfiu.js +207 -0
  39. package/dist/commands-CF8mOfiu.js.map +1 -0
  40. package/dist/config-Cuj0kVYj.js +415 -0
  41. package/dist/config-Cuj0kVYj.js.map +1 -0
  42. package/dist/context-loi9OCZv.js +106 -0
  43. package/dist/context-loi9OCZv.js.map +1 -0
  44. package/dist/core-tLN-z1Be.js +96 -0
  45. package/dist/core-tLN-z1Be.js.map +1 -0
  46. package/dist/db-Cjj2ZsfQ.js +2 -0
  47. package/dist/db-DxDzMhtB.js +827 -0
  48. package/dist/db-DxDzMhtB.js.map +1 -0
  49. package/dist/debug-D24NOQH1.js +89 -0
  50. package/dist/debug-D24NOQH1.js.map +1 -0
  51. package/dist/debug-SEmW4w8f.js +2 -0
  52. package/dist/dist-BZXYHN2X.js +1673 -0
  53. package/dist/dist-BZXYHN2X.js.map +1 -0
  54. package/dist/docker-DQDZWXF9.js +20 -0
  55. package/dist/docker-DQDZWXF9.js.map +1 -0
  56. package/dist/docker-DnWAVdEm.js +2 -0
  57. package/dist/docker-oSbCExHL.js +153 -0
  58. package/dist/docker-oSbCExHL.js.map +1 -0
  59. package/dist/doctor-CxlH1pDM.js +525 -0
  60. package/dist/doctor-CxlH1pDM.js.map +1 -0
  61. package/dist/egress-73SQfKdx.js +619 -0
  62. package/dist/egress-73SQfKdx.js.map +1 -0
  63. package/dist/env-BQqj93gu.js +21 -0
  64. package/dist/env-BQqj93gu.js.map +1 -0
  65. package/dist/environment-Mz7wvrtD.js +552 -0
  66. package/dist/environment-Mz7wvrtD.js.map +1 -0
  67. package/dist/errors-C9w_ud7g.js +160 -0
  68. package/dist/errors-C9w_ud7g.js.map +1 -0
  69. package/dist/errors-CSxfJ4qu.js +11 -0
  70. package/dist/errors-CSxfJ4qu.js.map +1 -0
  71. package/dist/files--6CEUXm7.js +2 -0
  72. package/dist/files-BLlrhU4-.js +62 -0
  73. package/dist/files-BLlrhU4-.js.map +1 -0
  74. package/dist/files-Bd25OAQH.js +2 -0
  75. package/dist/files-BvHCFWgD.js +2 -0
  76. package/dist/files-D2-qn_Dl.js +275 -0
  77. package/dist/files-D2-qn_Dl.js.map +1 -0
  78. package/dist/files-bF2fPuT9.js +99 -0
  79. package/dist/files-bF2fPuT9.js.map +1 -0
  80. package/dist/git-D6hMeieS.js +44 -0
  81. package/dist/git-D6hMeieS.js.map +1 -0
  82. package/dist/harness-ls5aX1UO.js +131 -0
  83. package/dist/harness-ls5aX1UO.js.map +1 -0
  84. package/dist/harness.d.ts +74 -0
  85. package/dist/harness.js +2 -0
  86. package/dist/header-DzFDoMCI.js +78 -0
  87. package/dist/header-DzFDoMCI.js.map +1 -0
  88. package/dist/ids-Bp6ut7ZD.js +49 -0
  89. package/dist/ids-Bp6ut7ZD.js.map +1 -0
  90. package/dist/images-Cc1-coFc.js +286 -0
  91. package/dist/images-Cc1-coFc.js.map +1 -0
  92. package/dist/index.d.ts +97 -0
  93. package/dist/index.js +1959 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/init-CFljS8BJ.js +344 -0
  96. package/dist/init-CFljS8BJ.js.map +1 -0
  97. package/dist/init-z2ZPn9Tj.js +2 -0
  98. package/dist/invocations-rXktgSAn.js +157 -0
  99. package/dist/invocations-rXktgSAn.js.map +1 -0
  100. package/dist/jiti-BOLXpKyG.js +46 -0
  101. package/dist/jiti-BOLXpKyG.js.map +1 -0
  102. package/dist/judge-D8Uf4dA9.js +146 -0
  103. package/dist/judge-D8Uf4dA9.js.map +1 -0
  104. package/dist/judge.d.ts +21 -0
  105. package/dist/judge.js +19 -0
  106. package/dist/judge.js.map +1 -0
  107. package/dist/live-Bs3LReMV.js +98 -0
  108. package/dist/live-Bs3LReMV.js.map +1 -0
  109. package/dist/matchers-DyLvm7EG.js +374 -0
  110. package/dist/matchers-DyLvm7EG.js.map +1 -0
  111. package/dist/model-BdO79gWz.js +44 -0
  112. package/dist/model-BdO79gWz.js.map +1 -0
  113. package/dist/model-CDL98xue.js +725 -0
  114. package/dist/model-CDL98xue.js.map +1 -0
  115. package/dist/model-v-0_Ik7S.js +2 -0
  116. package/dist/node-kG64XxdE.js +506 -0
  117. package/dist/node-kG64XxdE.js.map +1 -0
  118. package/dist/opencode-providers-DZuD_oYt.js +3149 -0
  119. package/dist/opencode-providers-DZuD_oYt.js.map +1 -0
  120. package/dist/options-CAQhu--C.js +452 -0
  121. package/dist/options-CAQhu--C.js.map +1 -0
  122. package/dist/patch-BkKje4cp.js +69 -0
  123. package/dist/patch-BkKje4cp.js.map +1 -0
  124. package/dist/paths-CBvm6pap.js +72 -0
  125. package/dist/paths-CBvm6pap.js.map +1 -0
  126. package/dist/plan-gwk6dJ8c.js +237 -0
  127. package/dist/plan-gwk6dJ8c.js.map +1 -0
  128. package/dist/preflight-BtubXKQ8.js +2 -0
  129. package/dist/preflight-BypZMNBG.js +185 -0
  130. package/dist/preflight-BypZMNBG.js.map +1 -0
  131. package/dist/prepare-Cwv0bHs_.js +3 -0
  132. package/dist/prepare-D6Uzcubb.js +213 -0
  133. package/dist/prepare-D6Uzcubb.js.map +1 -0
  134. package/dist/provenance-Dfb8YY5Y.js +129 -0
  135. package/dist/provenance-Dfb8YY5Y.js.map +1 -0
  136. package/dist/provider-BDbNOQhQ.js +2 -0
  137. package/dist/provider-CmAYTQQs.js +14 -0
  138. package/dist/provider-CmAYTQQs.js.map +1 -0
  139. package/dist/providers-2p3ylMHI.js +107 -0
  140. package/dist/providers-2p3ylMHI.js.map +1 -0
  141. package/dist/providers-BUOxitcT.js +2 -0
  142. package/dist/query-CFATHZmV.js +91 -0
  143. package/dist/query-CFATHZmV.js.map +1 -0
  144. package/dist/query-DR4m6CXf.js +2 -0
  145. package/dist/record-DQ8wzyco.js +99 -0
  146. package/dist/record-DQ8wzyco.js.map +1 -0
  147. package/dist/redact-C4fL_bZP.js +167 -0
  148. package/dist/redact-C4fL_bZP.js.map +1 -0
  149. package/dist/results-CeXb8Wde.js +37 -0
  150. package/dist/results-CeXb8Wde.js.map +1 -0
  151. package/dist/root-CFDjpWuc.js +110 -0
  152. package/dist/root-CFDjpWuc.js.map +1 -0
  153. package/dist/sandbox-By2gkdkC.js +133 -0
  154. package/dist/sandbox-By2gkdkC.js.map +1 -0
  155. package/dist/sandbox.d.ts +41 -0
  156. package/dist/sandbox.js +2 -0
  157. package/dist/server-Bi61uiY0.js +350 -0
  158. package/dist/server-Bi61uiY0.js.map +1 -0
  159. package/dist/server-CsSBL8NC.js +2 -0
  160. package/dist/source-BXzrmof5.js +2 -0
  161. package/dist/source-Bsfa3LZH.js +40 -0
  162. package/dist/source-Bsfa3LZH.js.map +1 -0
  163. package/dist/src-55j1zNHH.js +3 -0
  164. package/dist/src-AEwxd1UP.js +602 -0
  165. package/dist/src-AEwxd1UP.js.map +1 -0
  166. package/dist/src-BDwSuyIo.js +3 -0
  167. package/dist/src-BQyMiWri.js +490 -0
  168. package/dist/src-BQyMiWri.js.map +1 -0
  169. package/dist/src-C-LHyTSV.js +3 -0
  170. package/dist/src-Cmp-7YrD.js +1136 -0
  171. package/dist/src-Cmp-7YrD.js.map +1 -0
  172. package/dist/src-D0RdqpNb.js +252 -0
  173. package/dist/src-D0RdqpNb.js.map +1 -0
  174. package/dist/src-D8KYzxBX.js +793 -0
  175. package/dist/src-D8KYzxBX.js.map +1 -0
  176. package/dist/src-DEgff9Z7.js +4 -0
  177. package/dist/src-DXx_29pq.js +567 -0
  178. package/dist/src-DXx_29pq.js.map +1 -0
  179. package/dist/src-DwwiVLqv.js +2937 -0
  180. package/dist/src-DwwiVLqv.js.map +1 -0
  181. package/dist/src-N7E_mfcZ.js +2 -0
  182. package/dist/src-W9g-y5X5.js +3 -0
  183. package/dist/src-daS0wKH2.js +3 -0
  184. package/dist/state-Cyw3QTsN.js +2 -0
  185. package/dist/state-DZ7eqb1F.js +167 -0
  186. package/dist/state-DZ7eqb1F.js.map +1 -0
  187. package/dist/stats-BNAHlNMD.js +114 -0
  188. package/dist/stats-BNAHlNMD.js.map +1 -0
  189. package/dist/studio-app/assets/index-D6XyfFSH.js +58 -0
  190. package/dist/studio-app/assets/index-xwzWdcE_.css +2 -0
  191. package/dist/studio-app/favicon.svg +1 -0
  192. package/dist/studio-app/index.html +15 -0
  193. package/dist/summary-D7Ttky0z.js +166 -0
  194. package/dist/summary-D7Ttky0z.js.map +1 -0
  195. package/dist/text-gEcKjAyp.js +29 -0
  196. package/dist/text-gEcKjAyp.js.map +1 -0
  197. package/dist/types-C5vI3g1C.js +9 -0
  198. package/dist/types-C5vI3g1C.js.map +1 -0
  199. package/dist/types-DLec-6Cx.d.ts +1535 -0
  200. package/dist/usage-DsYUqIUM.js +24 -0
  201. package/dist/usage-DsYUqIUM.js.map +1 -0
  202. package/dist/view/index.d.ts +645 -0
  203. package/dist/view/index.js +8 -0
  204. package/dist/views-BrHOv6wg.js +220 -0
  205. package/dist/views-BrHOv6wg.js.map +1 -0
  206. package/dist/views-CwCWLWqo.js +3 -0
  207. package/dist/worker-entry-DnYkANPG.js +506 -0
  208. package/dist/worker-entry-DnYkANPG.js.map +1 -0
  209. package/dist/workspace-C2S0Z6gl.js +327 -0
  210. package/dist/workspace-C2S0Z6gl.js.map +1 -0
  211. package/dist/workspace-CyoXG6CY.js +2 -0
  212. package/docker/Dockerfile +19 -0
  213. package/package.json +79 -0
@@ -0,0 +1,1136 @@
1
+ import { posix } from "node:path";
2
+ import { Readable } from "node:stream";
3
+ import { HarnessCapabilityUnsupportedError, commonTool, harnessSessionDataDirectoryPath, harnessStateDirectoryPath } from "@ai-sdk/harness";
4
+ import { generateSandboxCredentialPlaceholder, isHarnessAuthenticationEnvironment, resolveSandboxHomeDir, shellQuote, warnCredentialBrokeringUnavailable, writeSkills } from "@ai-sdk/harness/utils";
5
+ import { tool } from "@ai-sdk/provider-utils";
6
+ import { createInterface } from "node:readline";
7
+ import { z } from "zod";
8
+ //#region ../harness-cursor-sdk/src/cursor-sdk-auth.ts
9
+ /** The only credential the adapter uses: a Cursor user or service-account API key. */
10
+ const CURSOR_SDK_CREDENTIAL_ENV = "CURSOR_API_KEY";
11
+ /** Cursor's public API: the model catalog and `GET /v1/agents/{id}/usage`. Called from the host. */
12
+ const CURSOR_API_URL = "https://api.cursor.com";
13
+ /**
14
+ * The SDK looks the model up in this catalog instead of calling `GET api.cursor.com/v1/models`
15
+ * with the API key. The adapter fetches it on the host, so the sandbox never needs that host.
16
+ */
17
+ const CURSOR_SDK_MODEL_CATALOG_ENV = "CURSOR_SDK_LOCAL_MODEL_CATALOG_JSON";
18
+ function resolveCursorSdkCredentials(auth = "auto", hostEnv = process.env) {
19
+ if (auth !== "auto" && !isHarnessAuthenticationEnvironment(auth)) throw new Error(`Cursor SDK auth must be 'auto' or an environment object such as { ${CURSOR_SDK_CREDENTIAL_ENV}: '…' } (got ${JSON.stringify(auth)})`);
20
+ const apiKey = (auth === "auto" ? hostEnv : auth)[CURSOR_SDK_CREDENTIAL_ENV];
21
+ if (!apiKey) throw new Error(`${CURSOR_SDK_CREDENTIAL_ENV} isn't set, so the Cursor SDK can't authenticate. It needs a Cursor user or service-account API key.`);
22
+ return { apiKey };
23
+ }
24
+ /**
25
+ * The one request the SDK sends the API key on (checked with @cursor/sdk 1.0.32): it trades
26
+ * the key for session tokens, which it sends everywhere else. The same endpoint the Cursor CLI
27
+ * uses, so a gateway that brokers the CLI brokers the SDK unchanged.
28
+ */
29
+ function cursorSdkEndpoints() {
30
+ return [{
31
+ host: "api2.cursor.sh",
32
+ path: { exact: "/auth/exchange_user_api_key" },
33
+ method: ["POST"]
34
+ }];
35
+ }
36
+ /** Swaps the sandbox's placeholder for the real key on the key exchange, and nowhere else. */
37
+ function createCursorSdkRequestTransformations({ credentials, placeholder, headers }) {
38
+ const [exchange] = cursorSdkEndpoints();
39
+ return [{
40
+ match: {
41
+ host: exchange.host,
42
+ path: exchange.path,
43
+ method: [...exchange.method],
44
+ headers: [{
45
+ key: { exact: "Authorization" },
46
+ value: { exact: `Bearer ${placeholder}` }
47
+ }]
48
+ },
49
+ transform: { headers: { Authorization: `Bearer ${credentials.apiKey}` } }
50
+ }, ...headers && Object.keys(headers).length > 0 ? [{
51
+ match: { host: exchange.host },
52
+ transform: { headers: { ...headers } }
53
+ }] : []];
54
+ }
55
+ const catalogs = /* @__PURE__ */ new Map();
56
+ /** `GET /v1/models` with the real key, on the host, once per key and process. */
57
+ function fetchCursorModelCatalog(credentials, options = {}) {
58
+ const baseUrl = (options.baseUrl ?? "https://api.cursor.com").replace(/\/+$/, "");
59
+ const cacheKey = `${baseUrl} ${credentials.apiKey}`;
60
+ let pending = catalogs.get(cacheKey);
61
+ if (!pending) {
62
+ pending = (async () => {
63
+ const res = await (options.fetch ?? fetch)(`${baseUrl}/v1/models`, {
64
+ headers: { Authorization: `Bearer ${credentials.apiKey}` },
65
+ ...options.signal ? { signal: options.signal } : {}
66
+ });
67
+ if (!res.ok) throw new Error(`Cursor's model catalog request failed: HTTP ${res.status}`);
68
+ const body = await res.json();
69
+ const items = Array.isArray(body) ? body : body.items;
70
+ if (!Array.isArray(items)) throw new Error("Cursor's model catalog has no items");
71
+ return items;
72
+ })();
73
+ pending.catch(() => catalogs.delete(cacheKey));
74
+ catalogs.set(cacheKey, pending);
75
+ }
76
+ return pending;
77
+ }
78
+ /** The catalog entries for `model` (by id or alias), or the whole catalog if none match. */
79
+ function catalogFor(catalog, model) {
80
+ if (!model) return catalog;
81
+ const match = catalog.filter((m) => m.id === model || m.aliases?.includes(model));
82
+ return match.length > 0 ? match : catalog;
83
+ }
84
+ //#endregion
85
+ //#region ../harness-cursor-sdk/src/cursor-sdk-bootstrap.ts
86
+ /** The SDK build the bootstrap installs. Bump it, then check the stream and endpoints still hold. */
87
+ const CURSOR_SDK_VERSION = "1.0.32";
88
+ /**
89
+ * Built-in tools whose calls never reach the stream at {@link CURSOR_SDK_VERSION}: Cursor's
90
+ * backend sends them, but the SDK's interaction-update converter maps `webSearchToolCall` and
91
+ * `webFetchToolCall` to null, so neither `run.stream()` nor `onDelta` / `onStep` sees them. The
92
+ * agent's stored conversation (`Agent.messages.list`) keeps them, with args, results and times,
93
+ * so the runner reports them from there after each turn, as `tool_call` events marked
94
+ * `recovered`. When it can't, the turn's `harnessMetadata.cursorSdk.unreportedTools` lists them.
95
+ */
96
+ const CURSOR_SDK_RECOVERED_TOOLS = {
97
+ webSearchToolCall: "webSearch",
98
+ webFetchToolCall: "webFetch"
99
+ };
100
+ /**
101
+ * The web calls in `Agent.messages.list()`'s messages (as JSON), wherever they're nested: each is
102
+ * a `ToolCall` whose oneof key is in `recovered`. The runner embeds this function's source.
103
+ */
104
+ function storedWebCalls(value, recovered = CURSOR_SDK_RECOVERED_TOOLS, found = []) {
105
+ if (Array.isArray(value)) for (const v of value) storedWebCalls(v, recovered, found);
106
+ else if (value && typeof value === "object") {
107
+ const o = value;
108
+ for (const [key, name] of Object.entries(recovered)) if (o[key] && typeof o.toolCallId === "string") found.push({
109
+ name,
110
+ id: o.toolCallId,
111
+ at: o,
112
+ call: o[key]
113
+ });
114
+ for (const v of Object.values(o)) storedWebCalls(v, recovered, found);
115
+ }
116
+ return found;
117
+ }
118
+ /**
119
+ * What's wrong with the stored conversation's web calls, by the fields the runner reads: each
120
+ * call's `toolCallId`, `startedAtMs` and `completedAtMs`, its `args` (`url` for a fetch,
121
+ * `searchTerm` for a search) and a `result` with `success` or `error`; and any web tool key the
122
+ * runner doesn't know (a renamed `webFetchToolCall` would otherwise just find nothing). Empty when
123
+ * the shape is as the runner expects. The runner embeds this function's source, and reports what
124
+ * it finds as the turn's unreported tools, with this message.
125
+ */
126
+ function storedShapeProblems(value, calls, recovered = CURSOR_SDK_RECOVERED_TOOLS) {
127
+ const problems = [];
128
+ const unknown = /* @__PURE__ */ new Set();
129
+ const walk = (v) => {
130
+ if (Array.isArray(v)) for (const x of v) walk(x);
131
+ else if (v && typeof v === "object") for (const [k, x] of Object.entries(v)) {
132
+ if (/^web\w*ToolCall$/.test(k) && !(k in recovered)) unknown.add(k);
133
+ walk(x);
134
+ }
135
+ };
136
+ walk(value);
137
+ for (const k of unknown) problems.push(`an unknown web tool call key ${k}`);
138
+ const target = {
139
+ webFetch: "url",
140
+ webSearch: "searchTerm"
141
+ };
142
+ for (const c of calls) {
143
+ const at = `${c.name} ${c.id}`;
144
+ const epoch = (x) => x !== void 0 && x !== null && !Number.isNaN(Number(x));
145
+ if (!epoch(c.at.startedAtMs)) problems.push(`${at}: no startedAtMs`);
146
+ if (!epoch(c.at.completedAtMs)) problems.push(`${at}: no completedAtMs`);
147
+ const args = c.call?.args;
148
+ const field = target[c.name];
149
+ if (!args || typeof args !== "object") problems.push(`${at}: no args`);
150
+ else if (field && typeof args[field] !== "string") problems.push(`${at}: args has no ${field}`);
151
+ const result = c.call?.result;
152
+ if (!result || typeof result !== "object" || !("success" in result || "error" in result)) problems.push(`${at}: result has neither success nor error`);
153
+ }
154
+ return problems;
155
+ }
156
+ /** Relative to the harness state directory, `$HOME/.ai-sdk-harness`. */
157
+ const CURSOR_SDK_BOOTSTRAP_DIR = ".harness-bootstrap/cursor-sdk";
158
+ /** The runner's path relative to the bootstrap directory. */
159
+ const CURSOR_SDK_RUNNER_PATH = "runner.mjs";
160
+ /**
161
+ * The SDK needs Node 22.13; routing its requests through `HTTPS_PROXY` (`NODE_USE_ENV_PROXY`)
162
+ * needs 22.21, and a sandbox that brokers the key has no other way out.
163
+ */
164
+ const CURSOR_SDK_MIN_NODE = "22.21.0";
165
+ /** The file the adapter writes once a turn's request is complete, so the runner never reads half. */
166
+ const cursorSdkTurnReady = (turn) => `turn-${turn}.ready`;
167
+ /**
168
+ * Runs a session's turns in the sandbox, one process for as long as it lives: it waits for each
169
+ * turn's request (`turn-<n>.json`, then `turn-<n>.ready`) in the session directory, creates the
170
+ * agent on the first (or resumes it by id from its JSONL store), sends the prompt and prints one
171
+ * JSON line per event on stdout, then a `result` line. The agent stays loaded between turns, so a
172
+ * follow-up turn skips Node's start, the SDK's import and the resume. The SDK logs to stdout, so
173
+ * everything but those lines goes to stderr.
174
+ */
175
+ const CURSOR_SDK_RUNNER = `// A local Cursor SDK agent's turns, as NDJSON on stdout.
176
+ import { existsSync, readFileSync } from 'node:fs'
177
+ import { join } from 'node:path'
178
+ import { setTimeout as sleep } from 'node:timers/promises'
179
+
180
+ const write = process.stdout.write.bind(process.stdout)
181
+ process.stdout.write = process.stderr.write.bind(process.stderr)
182
+ const out = (line) => write(\`\${JSON.stringify(line)}\\n\`)
183
+ const dir = process.argv[2]
184
+ let n = Number(process.argv[3] ?? 1)
185
+
186
+ let sdk
187
+ try {
188
+ sdk = await import('@cursor/sdk')
189
+ } catch (error) {
190
+ out({ type: 'error', turn: n, message: \`The Cursor SDK didn't load: \${error?.message ?? error}\` })
191
+ process.exit(1)
192
+ }
193
+ const { Agent, Cursor, JsonlLocalAgentStore } = sdk
194
+
195
+ // Over HTTP/2 the agent's stream connects directly, ignoring HTTPS_PROXY; over HTTP/1.1 it
196
+ // goes through the proxy like every other request.
197
+ Cursor.configure({ local: { useHttp1ForAgent: true } })
198
+
199
+ const RECOVERED = ${JSON.stringify(CURSOR_SDK_RECOVERED_TOOLS)}
200
+ const storedWebCalls = ${storedWebCalls.toString()}
201
+ const storedShapeProblems = ${storedShapeProblems.toString()}
202
+ const epochMs = (v) => (v === undefined || v === null || Number.isNaN(Number(v)) ? undefined : Number(v))
203
+ // As JSON: in memory they're protobuf messages, whose oneofs are { case, value }.
204
+ const listMessages = async (turn) =>
205
+ JSON.parse(JSON.stringify(await Agent.messages.list(agent.agentId, { cwd: turn.cwd, store })))
206
+
207
+ let agent
208
+ let run
209
+ let store
210
+ let stopping = false
211
+ const stop = () => {
212
+ stopping = true
213
+ if (run) void run.cancel().catch(() => {})
214
+ else process.exit(130)
215
+ }
216
+ process.on('SIGTERM', stop)
217
+ process.on('SIGINT', stop)
218
+
219
+ for (; !stopping; n++) {
220
+ while (!existsSync(join(dir, \`turn-\${n}.ready\`))) await sleep(15)
221
+ const turn = JSON.parse(readFileSync(join(dir, \`turn-\${n}.json\`), 'utf8'))
222
+ const prompt = readFileSync(turn.promptFile, 'utf8')
223
+ const started = Date.now()
224
+ try {
225
+ if (!agent) {
226
+ store = new JsonlLocalAgentStore(turn.storeDir)
227
+ const options = {
228
+ apiKey: process.env.CURSOR_API_KEY,
229
+ ...(turn.model ? { model: turn.model } : {}),
230
+ local: {
231
+ cwd: turn.cwd,
232
+ store,
233
+ settingSources: turn.settingSources,
234
+ ...(turn.sandbox ? { sandboxOptions: { enabled: true } } : {}),
235
+ },
236
+ ...(turn.mcpServers ? { mcpServers: turn.mcpServers } : {}),
237
+ }
238
+ agent = turn.agentId ? await Agent.resume(turn.agentId, options) : await Agent.create(options)
239
+ }
240
+ out({ type: 'init', turn: n, agentId: agent.agentId, model: agent.model, sdkVersion: turn.sdkVersion })
241
+ // Stored messages are updated in place, so earlier turns' calls are told apart by id.
242
+ const before = await listMessages(turn).then(
243
+ (m) => new Set(storedWebCalls(m, RECOVERED).map((c) => c.id)),
244
+ () => undefined,
245
+ )
246
+ run = await agent.send(prompt)
247
+ const streamed = new Set()
248
+ for await (const event of run.stream()) {
249
+ if (event.type === 'tool_call') streamed.add(event.call_id)
250
+ out({ type: 'event', event })
251
+ }
252
+ const result = await run.wait()
253
+ try {
254
+ if (before === undefined) throw new Error('the stored conversation could not be read before the turn')
255
+ const messages = await listMessages(turn)
256
+ const calls = storedWebCalls(messages, RECOVERED)
257
+ const problems = storedShapeProblems(messages, calls.filter((c) => !before.has(c.id)), RECOVERED)
258
+ if (problems.length) throw new Error(\`the stored conversation's shape changed (@cursor/sdk \${turn.sdkVersion ?? '?'}): \${problems.join('; ')}\`)
259
+ for (const c of calls.filter((c) => !before.has(c.id) && !streamed.has(c.id))) {
260
+ const { toolCallId, ...args } = c.call.args ?? {}
261
+ const base = { type: 'tool_call', agent_id: agent.agentId, run_id: result.id, call_id: c.id, name: c.name, args }
262
+ out({ type: 'event', recovered: 'Agent.messages.list', event: { ...base, status: 'running', started_at_ms: epochMs(c.at.startedAtMs) } })
263
+ const r = c.call.result
264
+ const failed = !r?.success
265
+ out({
266
+ type: 'event',
267
+ recovered: 'Agent.messages.list',
268
+ event: {
269
+ ...base,
270
+ status: failed ? 'error' : 'completed',
271
+ result: failed ? { status: 'error', error: r?.error ?? r ?? 'no result' } : { status: 'success', value: r.success },
272
+ started_at_ms: epochMs(c.at.startedAtMs),
273
+ completed_at_ms: epochMs(c.at.completedAtMs),
274
+ },
275
+ })
276
+ }
277
+ } catch (error) {
278
+ out({ type: 'unreported', turn: n, tools: Object.values(RECOVERED), message: String(error?.message ?? error) })
279
+ }
280
+ out({
281
+ type: 'result',
282
+ turn: n,
283
+ agentId: agent.agentId,
284
+ runId: result.id,
285
+ requestId: result.requestId,
286
+ status: result.status,
287
+ result: result.result,
288
+ error: result.error,
289
+ model: result.model,
290
+ durationMs: result.durationMs ?? Date.now() - started,
291
+ usage: result.usage,
292
+ })
293
+ } catch (error) {
294
+ out({
295
+ type: 'error',
296
+ turn: n,
297
+ message: String(error?.message ?? error),
298
+ name: error?.name,
299
+ code: error?.code,
300
+ status: error?.status,
301
+ })
302
+ // The next turn starts from the store, not from an agent in an unknown state.
303
+ await agent?.[Symbol.asyncDispose]?.().catch(() => {})
304
+ agent = undefined
305
+ } finally {
306
+ run = undefined
307
+ }
308
+ }
309
+ await agent?.[Symbol.asyncDispose]?.().catch(() => {})
310
+ process.exit(130)
311
+ `;
312
+ /** Checks Node, then installs the pinned SDK (and its platform package) next to the runner. */
313
+ function cursorSdkInstallScript(version = CURSOR_SDK_VERSION) {
314
+ return `set -eu
315
+ node -e 'const [a,b]=process.versions.node.split(".").map(Number);const [x,y]="${CURSOR_SDK_MIN_NODE}".split(".").map(Number);if(a<x||(a===x&&b<y)){console.error("The Cursor SDK harness needs Node ${CURSOR_SDK_MIN_NODE} or later (found "+process.versions.node+")");process.exit(1)}'
316
+ cat > package.json <<'JSON'
317
+ { "private": true, "type": "module", "dependencies": { "@cursor/sdk": "${version}" } }
318
+ JSON
319
+ npm install --omit=dev --no-audit --no-fund --loglevel=error
320
+ node -e 'import("@cursor/sdk").then(() => console.log("@cursor/sdk ${version}"))'
321
+ `;
322
+ }
323
+ async function getCursorSdkBootstrap() {
324
+ return {
325
+ harnessId: "cursor-sdk",
326
+ bootstrapDir: CURSOR_SDK_BOOTSTRAP_DIR,
327
+ files: [{
328
+ path: `${CURSOR_SDK_BOOTSTRAP_DIR}/install.sh`,
329
+ content: cursorSdkInstallScript()
330
+ }, {
331
+ path: `${CURSOR_SDK_BOOTSTRAP_DIR}/${CURSOR_SDK_RUNNER_PATH}`,
332
+ content: CURSOR_SDK_RUNNER
333
+ }],
334
+ commands: [{ command: "sh install.sh" }]
335
+ };
336
+ }
337
+ //#endregion
338
+ //#region ../harness-cursor-sdk/src/cursor-sdk-tools.ts
339
+ const native = (nativeName, title, toolUseKind) => ({
340
+ ...tool({
341
+ title,
342
+ inputSchema: z.looseObject({})
343
+ }),
344
+ nativeName,
345
+ ...toolUseKind ? { toolUseKind } : {}
346
+ });
347
+ const CURSOR_SDK_BUILTIN_TOOLS = {
348
+ bash: {
349
+ ...commonTool("bash", {
350
+ nativeName: "shell",
351
+ toolUseKind: "bash",
352
+ inputSchema: z.looseObject({ command: z.string() })
353
+ }),
354
+ title: "Terminal"
355
+ },
356
+ read: {
357
+ ...tool({
358
+ title: "Read",
359
+ inputSchema: z.looseObject({ path: z.string() })
360
+ }),
361
+ nativeName: "read",
362
+ toolUseKind: "readonly"
363
+ },
364
+ edit: {
365
+ ...tool({
366
+ title: "Edit",
367
+ inputSchema: z.looseObject({ path: z.string() })
368
+ }),
369
+ nativeName: "edit",
370
+ toolUseKind: "edit"
371
+ },
372
+ write: {
373
+ ...tool({
374
+ title: "Write",
375
+ inputSchema: z.looseObject({ path: z.string() })
376
+ }),
377
+ nativeName: "write",
378
+ toolUseKind: "edit"
379
+ },
380
+ delete: {
381
+ ...tool({
382
+ title: "Delete",
383
+ inputSchema: z.looseObject({ path: z.string() })
384
+ }),
385
+ nativeName: "delete",
386
+ toolUseKind: "edit"
387
+ },
388
+ glob: {
389
+ ...commonTool("glob", {
390
+ nativeName: "glob",
391
+ toolUseKind: "readonly",
392
+ inputSchema: z.looseObject({ pattern: z.string() })
393
+ }),
394
+ title: "Find"
395
+ },
396
+ grep: {
397
+ ...commonTool("grep", {
398
+ nativeName: "grep",
399
+ toolUseKind: "readonly",
400
+ inputSchema: z.looseObject({
401
+ pattern: z.string(),
402
+ path: z.string().optional()
403
+ })
404
+ }),
405
+ title: "grep"
406
+ },
407
+ ls: native("ls", "List", "readonly"),
408
+ webSearch: {
409
+ ...commonTool("webSearch", {
410
+ nativeName: "webSearch",
411
+ toolUseKind: "readonly",
412
+ inputSchema: z.looseObject({ query: z.string() })
413
+ }),
414
+ title: "Web Search"
415
+ },
416
+ webFetch: {
417
+ ...tool({
418
+ title: "Fetch",
419
+ inputSchema: z.looseObject({ url: z.string() })
420
+ }),
421
+ nativeName: "webFetch",
422
+ toolUseKind: "readonly"
423
+ },
424
+ mcp: native("mcp", "MCP"),
425
+ task: native("task", "Task"),
426
+ semSearch: native("semSearch", "Semantic Search", "readonly"),
427
+ updateTodos: native("updateTodos", "Update TODOs"),
428
+ readTodos: native("readTodos", "Read TODOs", "readonly"),
429
+ readLints: native("readLints", "Read Lints", "readonly")
430
+ };
431
+ const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
432
+ const str = (value) => typeof value === "string" ? value : void 0;
433
+ const BY_NATIVE_NAME = new Map(Object.entries(CURSOR_SDK_BUILTIN_TOOLS).map(([key, t]) => [t.nativeName ?? key, key]));
434
+ /** Maps an SDK `tool_call` to its catalog name, adding the common-schema fields it implies. */
435
+ function projectToolCall(nativeName, args) {
436
+ const a = record(args);
437
+ const common = (toolName, extra = {}) => ({
438
+ toolName,
439
+ nativeName,
440
+ input: {
441
+ ...a,
442
+ ...extra
443
+ },
444
+ dynamic: false
445
+ });
446
+ switch (nativeName) {
447
+ case "shell": return common("bash", { command: str(a.command) ?? "" });
448
+ case "glob": return common("glob", { pattern: str(a.globPattern) ?? str(a.pattern) ?? "" });
449
+ case "grep": return common("grep", { pattern: str(a.pattern) ?? "" });
450
+ case "webSearch": return common("webSearch", { query: str(a.searchTerm) ?? str(a.query) ?? "" });
451
+ }
452
+ const key = BY_NATIVE_NAME.get(nativeName);
453
+ return key ? common(key) : {
454
+ toolName: nativeName,
455
+ nativeName,
456
+ input: { ...a },
457
+ dynamic: true
458
+ };
459
+ }
460
+ /**
461
+ * An SDK tool result is `{ status: 'success', value }` or `{ status: 'error', error }`. The
462
+ * value is passed on as is, except shell output, which becomes its text (as the ACP adapter
463
+ * reports it), and MCP content, which is flattened to text parts.
464
+ */
465
+ function projectToolResult(toolName, status, result) {
466
+ const r = record(result);
467
+ if (status === "error" || r.status !== void 0 && r.status !== "success") {
468
+ const error = str(r.error) ?? str(record(r.error).message) ?? str(record(r.value).error) ?? (result === void 0 ? `${toolName} failed` : JSON.stringify(result));
469
+ return {
470
+ output: error,
471
+ error
472
+ };
473
+ }
474
+ const value = r.status === "success" ? r.value : result;
475
+ if (toolName === "bash") {
476
+ const v = record(value);
477
+ const stdout = str(v.stdout) ?? "";
478
+ const stderr = str(v.stderr) ?? "";
479
+ return { output: stderr ? `${stdout}${stdout.endsWith("\n") || !stdout ? "" : "\n"}${stderr}` : stdout };
480
+ }
481
+ if (toolName === "mcp") {
482
+ const content = record(value).content;
483
+ if (Array.isArray(content)) return { output: content.map((c) => {
484
+ const text = record(record(c).text).text ?? record(c).text;
485
+ return typeof text === "string" ? {
486
+ type: "text",
487
+ text
488
+ } : c;
489
+ }) };
490
+ }
491
+ return { output: value ?? "" };
492
+ }
493
+ //#endregion
494
+ //#region ../harness-cursor-sdk/src/cursor-sdk-stream.ts
495
+ /** Parts that are a step's content, as opposed to the stream's framing. */
496
+ const isContent = (part) => ![
497
+ "raw",
498
+ "stream-start",
499
+ "finish",
500
+ "finish-step",
501
+ "error"
502
+ ].includes(part.type);
503
+ /** Maps the SDK's counts onto the AI SDK's usage shape, keeping the original under `raw`. */
504
+ function toUsage(usage) {
505
+ if (!usage) return {
506
+ inputTokens: {
507
+ total: void 0,
508
+ noCache: void 0,
509
+ cacheRead: void 0,
510
+ cacheWrite: void 0
511
+ },
512
+ outputTokens: {
513
+ total: void 0,
514
+ text: void 0,
515
+ reasoning: void 0
516
+ }
517
+ };
518
+ const noCache = usage.inputTokens;
519
+ const cacheRead = usage.cacheReadTokens;
520
+ const cacheWrite = usage.cacheWriteTokens;
521
+ const output = usage.outputTokens;
522
+ const reasoning = usage.reasoningTokens;
523
+ return {
524
+ inputTokens: {
525
+ total: noCache === void 0 ? void 0 : noCache + (cacheRead ?? 0) + (cacheWrite ?? 0),
526
+ noCache,
527
+ cacheRead,
528
+ cacheWrite
529
+ },
530
+ outputTokens: {
531
+ total: output,
532
+ text: output !== void 0 && reasoning !== void 0 ? output - reasoning : void 0,
533
+ reasoning
534
+ },
535
+ raw: { ...usage }
536
+ };
537
+ }
538
+ /** The model as one id, with its parameters: `composer-2.5[fast=false]`. */
539
+ function modelLabel(model) {
540
+ if (!model?.id) return void 0;
541
+ const params = model.params ?? [];
542
+ return params.length ? `${model.id}[${params.map((p) => `${p.id}=${p.value}`).join(",")}]` : model.id;
543
+ }
544
+ const FINISH = {
545
+ finished: "stop",
546
+ error: "error",
547
+ cancelled: "other"
548
+ };
549
+ /** `webFetch <url>` or `webSearch <query>` for a web tool call, else undefined. */
550
+ function webKey(e) {
551
+ const p = projectToolCall(e.name ?? "", e.args);
552
+ if (p.toolName !== "webFetch" && p.toolName !== "webSearch") return void 0;
553
+ const target = p.input.url ?? p.input.query;
554
+ return typeof target === "string" ? `${p.toolName} ${target}` : void 0;
555
+ }
556
+ /**
557
+ * Turns the runner's NDJSON for one turn into harness stream parts, in order. Text and
558
+ * thinking arrive as deltas; a thinking event with `thinking_duration_ms` ends its block.
559
+ * Each tool call is reported `running` (maybe more than once) and then `completed` or `error`.
560
+ */
561
+ var CursorSdkStreamTranslator = class {
562
+ agentId;
563
+ /** The last `result` line, for callers that want the SDK's own record of the run. */
564
+ result;
565
+ started = false;
566
+ /** True once the turn's `result` or `error` line (or {@link end}) closed it. */
567
+ finished = false;
568
+ sawText = false;
569
+ block = 0;
570
+ openText;
571
+ openReasoning;
572
+ openTools = /* @__PURE__ */ new Map();
573
+ /** Calls already reported, so a late duplicate `completed` doesn't open a second one. */
574
+ seen = /* @__PURE__ */ new Set();
575
+ /** Web calls the stream itself reported, by id and by tool and target. */
576
+ streamedWeb = /* @__PURE__ */ new Set();
577
+ /**
578
+ * The turn's `usage` event, held until its result line: the runner reports the calls it
579
+ * recovers after it, and HarnessAgent refuses content after a step's `finish-step`.
580
+ */
581
+ stepUsage;
582
+ /** Whether the open step has content, which HarnessAgent needs closed before `finish`. */
583
+ stepHasContent = false;
584
+ /** Built-in tools this turn may have used with no `tool_call` reported. */
585
+ unreportedTools = [];
586
+ workDir;
587
+ /**
588
+ * `workDir`: the agent's workspace. Tool paths under it are reported relative to it, as the
589
+ * ACP adapter reports them; the SDK reports them absolute.
590
+ */
591
+ constructor(options = {}) {
592
+ this.agentId = options.agentId;
593
+ this.workDir = options.workDir?.replace(/\/+$/, "");
594
+ }
595
+ relative(input) {
596
+ const prefix = this.workDir ? `${this.workDir}/` : void 0;
597
+ if (!prefix || typeof input.path !== "string" || !input.path.startsWith(prefix)) return input;
598
+ return {
599
+ ...input,
600
+ path: input.path.slice(prefix.length)
601
+ };
602
+ }
603
+ /** Parts for one stdout line. Lines that aren't runner lines are passed through as raw. */
604
+ push(line) {
605
+ const trimmed = line.trim();
606
+ if (!trimmed) return [];
607
+ let parsed;
608
+ try {
609
+ parsed = JSON.parse(trimmed);
610
+ } catch {
611
+ return [{
612
+ type: "raw",
613
+ rawValue: { unparsed: trimmed }
614
+ }];
615
+ }
616
+ if (this.finished) return [{
617
+ type: "raw",
618
+ rawValue: parsed
619
+ }];
620
+ const parts = [];
621
+ switch (parsed.type) {
622
+ case "init":
623
+ this.agentId = parsed.agentId ?? this.agentId;
624
+ this.start(parts, modelLabel(parsed.model));
625
+ break;
626
+ case "event":
627
+ this.start(parts);
628
+ this.event(parts, parsed.event ?? { type: "unknown" }, parsed.recovered !== void 0);
629
+ break;
630
+ case "result":
631
+ this.start(parts);
632
+ this.finish(parts, parsed);
633
+ break;
634
+ case "error":
635
+ this.start(parts);
636
+ this.fail(parts, parsed.message, parsed.code ?? parsed.name ?? "error");
637
+ break;
638
+ case "unreported": this.unreportedTools = [.../* @__PURE__ */ new Set([...this.unreportedTools, ...parsed.tools ?? []])];
639
+ }
640
+ parts.push({
641
+ type: "raw",
642
+ rawValue: parsed
643
+ });
644
+ if (parts.some(isContent)) this.stepHasContent = true;
645
+ return parts;
646
+ }
647
+ /** Closes the turn when the runner ends without a result (killed, crashed, aborted). */
648
+ end(options) {
649
+ if (this.finished) return [];
650
+ const parts = [];
651
+ this.start(parts);
652
+ this.closeTools(parts, options.aborted ? "aborted" : "interrupted");
653
+ this.closeBlocks(parts);
654
+ this.closeStep(parts);
655
+ if (!options.aborted) {
656
+ const detail = options.stderr?.trim();
657
+ parts.push({
658
+ type: "error",
659
+ error: /* @__PURE__ */ new Error(`The Cursor SDK runner exited with code ${options.exitCode} before reporting a result${detail ? `: ${detail}` : ""}`)
660
+ });
661
+ }
662
+ this.finished = true;
663
+ parts.push({
664
+ type: "finish",
665
+ finishReason: options.aborted ? {
666
+ unified: "other",
667
+ raw: "aborted"
668
+ } : {
669
+ unified: "error",
670
+ raw: `exit ${options.exitCode}`
671
+ },
672
+ totalUsage: toUsage(void 0),
673
+ harnessMetadata: this.unrecovered()
674
+ });
675
+ return parts;
676
+ }
677
+ start(parts, modelId) {
678
+ if (this.started) return;
679
+ this.started = true;
680
+ parts.push({
681
+ type: "stream-start",
682
+ ...modelId ? { modelId } : {}
683
+ });
684
+ }
685
+ closeText(parts) {
686
+ if (this.openText) parts.push({
687
+ type: "text-end",
688
+ id: this.openText
689
+ });
690
+ this.openText = void 0;
691
+ }
692
+ closeReasoning(parts) {
693
+ if (this.openReasoning) parts.push({
694
+ type: "reasoning-end",
695
+ id: this.openReasoning
696
+ });
697
+ this.openReasoning = void 0;
698
+ }
699
+ closeBlocks(parts) {
700
+ this.closeText(parts);
701
+ this.closeReasoning(parts);
702
+ }
703
+ closeTools(parts, why) {
704
+ for (const [toolCallId, call] of this.openTools) parts.push({
705
+ type: "tool-result",
706
+ toolCallId,
707
+ toolName: call.toolName,
708
+ result: `${call.toolName} ${why}`,
709
+ isError: true,
710
+ ...call.dynamic ? { dynamic: true } : {}
711
+ });
712
+ this.openTools.clear();
713
+ }
714
+ event(parts, e, recovered = false) {
715
+ this.agentId ??= e.agent_id;
716
+ switch (e.type) {
717
+ case "thinking":
718
+ if (e.text) {
719
+ this.closeText(parts);
720
+ if (!this.openReasoning) {
721
+ this.openReasoning = `reasoning-${++this.block}`;
722
+ parts.push({
723
+ type: "reasoning-start",
724
+ id: this.openReasoning
725
+ });
726
+ }
727
+ parts.push({
728
+ type: "reasoning-delta",
729
+ id: this.openReasoning,
730
+ delta: e.text
731
+ });
732
+ }
733
+ if (e.thinking_duration_ms !== void 0) this.closeReasoning(parts);
734
+ return;
735
+ case "assistant": {
736
+ const text = (e.message?.content ?? []).filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("");
737
+ if (!text) return;
738
+ this.closeReasoning(parts);
739
+ if (!this.openText) {
740
+ this.openText = `text-${++this.block}`;
741
+ parts.push({
742
+ type: "text-start",
743
+ id: this.openText
744
+ });
745
+ }
746
+ this.sawText = true;
747
+ parts.push({
748
+ type: "text-delta",
749
+ id: this.openText,
750
+ delta: text
751
+ });
752
+ return;
753
+ }
754
+ case "tool_call": {
755
+ const id = e.call_id;
756
+ if (!id) return;
757
+ const web = webKey(e);
758
+ if (recovered) {
759
+ if (this.streamedWeb.has(id) || web && this.streamedWeb.has(web)) return;
760
+ } else if (web) {
761
+ this.streamedWeb.add(id);
762
+ this.streamedWeb.add(web);
763
+ }
764
+ const status = e.status ?? "running";
765
+ let call = this.openTools.get(id);
766
+ if (!call) {
767
+ if (status !== "running" && this.seen.has(id)) return;
768
+ this.closeBlocks(parts);
769
+ const projected = projectToolCall(e.name ?? "unknown", e.args);
770
+ call = {
771
+ toolName: projected.toolName,
772
+ dynamic: projected.dynamic
773
+ };
774
+ this.openTools.set(id, call);
775
+ this.seen.add(id);
776
+ parts.push({
777
+ type: "tool-call",
778
+ toolCallId: id,
779
+ toolName: projected.toolName,
780
+ input: JSON.stringify(this.relative(projected.input)),
781
+ providerExecuted: true,
782
+ ...projected.dynamic ? { dynamic: true } : {},
783
+ ...projected.toolName !== projected.nativeName ? { nativeName: projected.nativeName } : {}
784
+ });
785
+ }
786
+ if (status === "running") return;
787
+ this.openTools.delete(id);
788
+ const { output, error } = projectToolResult(call.toolName, status, e.result);
789
+ parts.push({
790
+ type: "tool-result",
791
+ toolCallId: id,
792
+ toolName: call.toolName,
793
+ result: output,
794
+ ...error !== void 0 ? { isError: true } : {},
795
+ ...call.dynamic ? { dynamic: true } : {}
796
+ });
797
+ return;
798
+ }
799
+ case "usage":
800
+ this.closeBlocks(parts);
801
+ this.stepUsage = e.usage ?? {};
802
+ return;
803
+ }
804
+ }
805
+ finish(parts, r) {
806
+ this.result = r;
807
+ this.agentId = r.agentId ?? this.agentId;
808
+ this.closeTools(parts, "unfinished");
809
+ if (!this.sawText && r.result) {
810
+ parts.push({
811
+ type: "text-start",
812
+ id: "text-result"
813
+ });
814
+ parts.push({
815
+ type: "text-delta",
816
+ id: "text-result",
817
+ delta: r.result
818
+ });
819
+ parts.push({
820
+ type: "text-end",
821
+ id: "text-result"
822
+ });
823
+ }
824
+ this.closeBlocks(parts);
825
+ this.closeStep(parts, r.usage);
826
+ const status = r.status ?? "unknown";
827
+ if (status !== "finished" && status !== "cancelled") parts.push({
828
+ type: "error",
829
+ error: new Error(r.error?.message || `The Cursor SDK run ended with status ${status}`)
830
+ });
831
+ this.finished = true;
832
+ parts.push({
833
+ type: "finish",
834
+ finishReason: {
835
+ unified: FINISH[status] ?? "other",
836
+ raw: status
837
+ },
838
+ totalUsage: toUsage(r.usage),
839
+ harnessMetadata: { cursorSdk: {
840
+ ...r.agentId ? { agentId: r.agentId } : {},
841
+ ...r.runId ? { runId: r.runId } : {},
842
+ ...r.requestId ? { requestId: r.requestId } : {},
843
+ ...r.model ? { model: r.model } : {},
844
+ ...r.durationMs !== void 0 ? { durationMs: r.durationMs } : {},
845
+ ...r.usage ? { usage: { ...r.usage } } : {},
846
+ ...this.unreportedTools.length ? { unreportedTools: [...this.unreportedTools] } : {}
847
+ } }
848
+ });
849
+ }
850
+ fail(parts, message, raw) {
851
+ this.closeTools(parts, "interrupted");
852
+ this.closeBlocks(parts);
853
+ this.closeStep(parts);
854
+ parts.push({
855
+ type: "error",
856
+ error: new Error(message)
857
+ });
858
+ this.finished = true;
859
+ parts.push({
860
+ type: "finish",
861
+ finishReason: {
862
+ unified: "error",
863
+ raw
864
+ },
865
+ totalUsage: toUsage(void 0),
866
+ harnessMetadata: this.unrecovered()
867
+ });
868
+ }
869
+ /** The turn's one step, with the `usage` event's counts, else the result's when it has them. */
870
+ closeStep(parts, resultUsage) {
871
+ const usage = this.stepUsage ?? resultUsage;
872
+ if (!usage && !this.stepHasContent && !parts.some(isContent)) return;
873
+ parts.push({
874
+ type: "finish-step",
875
+ finishReason: usage ? {
876
+ unified: "stop",
877
+ raw: this.stepUsage ? "turn-ended" : "result"
878
+ } : {
879
+ unified: "other",
880
+ raw: "interrupted"
881
+ },
882
+ usage: toUsage(usage)
883
+ });
884
+ this.stepUsage = void 0;
885
+ this.stepHasContent = false;
886
+ }
887
+ /** A turn that ended before its result never had its web calls recovered. */
888
+ unrecovered() {
889
+ return { cursorSdk: { unreportedTools: Object.values(CURSOR_SDK_RECOVERED_TOOLS) } };
890
+ }
891
+ };
892
+ //#endregion
893
+ //#region ../harness-cursor-sdk/src/cursor-sdk-harness.ts
894
+ const CURSOR_SDK_HARNESS_ID = "cursor-sdk";
895
+ /** Where harness-placed skills go, under `~/` (the SDK's `user` layer reads `~/.agents/skills`). */
896
+ const CURSOR_SDK_HOME_SKILLS_DIR = ".agents/skills";
897
+ const lifecycleData = z.object({ agentId: z.string().nullable() });
898
+ function promptText(prompt, instructions) {
899
+ const content = typeof prompt === "string" ? prompt : prompt.content;
900
+ const text = typeof content === "string" ? content : content.map((part) => {
901
+ if (part.type !== "text") throw new HarnessCapabilityUnsupportedError({
902
+ message: `The Cursor SDK adapter takes text prompts only (got a ${part.type} part)`,
903
+ harnessId: CURSOR_SDK_HARNESS_ID
904
+ });
905
+ return part.text;
906
+ }).join("\n");
907
+ if (!instructions) return text;
908
+ return [
909
+ "<session-guidance>",
910
+ "This block is operating guidance from the harness, not user-authored content.",
911
+ "<instructions>",
912
+ instructions,
913
+ "</instructions>",
914
+ "</session-guidance>",
915
+ text
916
+ ].join("\n");
917
+ }
918
+ const unsupported = (what) => new HarnessCapabilityUnsupportedError({
919
+ message: `The Cursor SDK adapter has no ${what}`,
920
+ harnessId: CURSOR_SDK_HARNESS_ID
921
+ });
922
+ const lines = (stream) => createInterface({
923
+ input: Readable.fromWeb(stream),
924
+ crlfDelay: Infinity
925
+ });
926
+ function startRunner(proc) {
927
+ let stderr = "";
928
+ const drained = (async () => {
929
+ for await (const line of lines(proc.stderr)) stderr = `${stderr}${line}\n`.slice(-4e3);
930
+ })().catch(() => {});
931
+ const runner = {
932
+ proc,
933
+ stdout: lines(proc.stdout)[Symbol.asyncIterator](),
934
+ stderr: async () => {
935
+ await Promise.race([drained, new Promise((r) => setTimeout(r, 1e3).unref())]);
936
+ return stderr;
937
+ },
938
+ exited: false
939
+ };
940
+ Promise.resolve(proc.wait()).then(() => runner.exited = true, () => runner.exited = true);
941
+ return runner;
942
+ }
943
+ const canBroker = (sandbox) => "addRequestTransformations" in sandbox && typeof sandbox.addRequestTransformations === "function";
944
+ /**
945
+ * Cursor's agent through the Cursor TypeScript SDK's local runtime, as an AI SDK harness. The
946
+ * agent loop runs in a Node process in the sandbox that lives across the session's turns (a new
947
+ * one resumes the agent by id from a JSONL store), so its tools act on the sandbox's files;
948
+ * inference is Cursor's hosted models. The SDK reports token usage for every turn.
949
+ */
950
+ function createCursorSdk(settings = {}) {
951
+ return {
952
+ specificationVersion: "harness-v1",
953
+ harnessId: CURSOR_SDK_HARNESS_ID,
954
+ builtinTools: CURSOR_SDK_BUILTIN_TOOLS,
955
+ lifecycleStateSchema: lifecycleData,
956
+ getBootstrap: getCursorSdkBootstrap,
957
+ doStart: (options) => startSession(settings, options)
958
+ };
959
+ }
960
+ async function startSession(settings, options) {
961
+ const { sandboxSession: sandbox, sessionWorkDir, sessionId, abortSignal } = options;
962
+ const credentials = resolveCursorSdkCredentials(settings.auth);
963
+ let sandboxKey = credentials.apiKey;
964
+ if (canBroker(sandbox)) {
965
+ sandboxKey = generateSandboxCredentialPlaceholder();
966
+ await sandbox.addRequestTransformations(createCursorSdkRequestTransformations({
967
+ credentials,
968
+ placeholder: sandboxKey,
969
+ ...options.headers ? { headers: options.headers } : {}
970
+ }));
971
+ } else if (settings.requireCredentialBrokering) throw new Error(`The sandbox can't broker ${CURSOR_SDK_CREDENTIAL_ENV} (it has no request transformations), and requireCredentialBrokering is set, so the key stays out of it.`);
972
+ else warnCredentialBrokeringUnavailable({
973
+ environment: { [CURSOR_SDK_CREDENTIAL_ENV]: credentials.apiKey },
974
+ forwardedEnvironment: { [CURSOR_SDK_CREDENTIAL_ENV]: sandboxKey },
975
+ credentialEnvironmentVariables: [CURSOR_SDK_CREDENTIAL_ENV]
976
+ });
977
+ const catalogSetting = settings.modelCatalog ?? "fetch";
978
+ const catalog = catalogSetting === "fetch" ? await fetchCursorModelCatalog(credentials, abortSignal ? { signal: abortSignal } : {}) : catalogSetting === false ? void 0 : [...catalogSetting];
979
+ const home = await resolveSandboxHomeDir({
980
+ sandbox,
981
+ abortSignal
982
+ });
983
+ const stateDir = harnessStateDirectoryPath({ sandboxHomeDir: home });
984
+ const runnerPath = posix.join(stateDir, CURSOR_SDK_BOOTSTRAP_DIR, CURSOR_SDK_RUNNER_PATH);
985
+ const sessionDir = harnessSessionDataDirectoryPath({
986
+ stateDirectory: stateDir,
987
+ sessionId
988
+ });
989
+ const storeDir = posix.join(sessionDir, "agent-store");
990
+ const resumed = lifecycleData.safeParse(options.resumeFrom?.data);
991
+ let agentId = resumed.success ? resumed.data.agentId : null;
992
+ let turns = 0;
993
+ let runner;
994
+ let ended = false;
995
+ const state = () => ({
996
+ type: "resume-session",
997
+ harnessId: CURSOR_SDK_HARNESS_ID,
998
+ specificationVersion: "harness-v1",
999
+ data: { agentId }
1000
+ });
1001
+ const stopRunner = async () => {
1002
+ const r = runner;
1003
+ runner = void 0;
1004
+ await r?.proc.kill();
1005
+ };
1006
+ const stop = async () => {
1007
+ ended = true;
1008
+ await stopRunner();
1009
+ };
1010
+ return {
1011
+ sessionId,
1012
+ isResume: options.resumeFrom !== void 0,
1013
+ async doPromptTurn(turn) {
1014
+ if (ended) throw new Error("This Cursor SDK session has ended");
1015
+ if (turn.tools.length > 0) throw unsupported("host-executed tools");
1016
+ if (turn.responseFormat?.type === "json") throw unsupported("structured output");
1017
+ const n = ++turns;
1018
+ await writeSkills({
1019
+ sandbox,
1020
+ homePath: home,
1021
+ skillsDir: CURSOR_SDK_HOME_SKILLS_DIR,
1022
+ skills: turn.skills,
1023
+ ...turn.abortSignal ? { abortSignal: turn.abortSignal } : {}
1024
+ });
1025
+ const promptFile = posix.join(sessionDir, `prompt-${n}.txt`);
1026
+ await sandbox.writeTextFile({
1027
+ path: promptFile,
1028
+ content: promptText(turn.prompt, turn.instructions),
1029
+ abortSignal: turn.abortSignal
1030
+ });
1031
+ const model = turn.model ? withParams(turn.model, settings.modelParams, catalog) : void 0;
1032
+ const turnFile = posix.join(sessionDir, `turn-${n}.json`);
1033
+ await sandbox.writeTextFile({
1034
+ path: turnFile,
1035
+ content: JSON.stringify({
1036
+ cwd: sessionWorkDir,
1037
+ storeDir,
1038
+ promptFile,
1039
+ agentId: agentId ?? void 0,
1040
+ model,
1041
+ settingSources: settings.settingSources ?? ["project", "user"],
1042
+ sandbox: settings.sdkSandbox === true,
1043
+ sdkVersion: CURSOR_SDK_VERSION,
1044
+ ...settings.mcpServers && Object.keys(settings.mcpServers).length > 0 ? { mcpServers: settings.mcpServers } : {}
1045
+ }),
1046
+ abortSignal: turn.abortSignal
1047
+ });
1048
+ await sandbox.writeTextFile({
1049
+ path: posix.join(sessionDir, cursorSdkTurnReady(n)),
1050
+ content: "",
1051
+ abortSignal: turn.abortSignal
1052
+ });
1053
+ if (runner?.exited) runner = void 0;
1054
+ if (!runner) {
1055
+ const env = {
1056
+ ...settings.env,
1057
+ [CURSOR_SDK_CREDENTIAL_ENV]: sandboxKey,
1058
+ ...catalog ? { [CURSOR_SDK_MODEL_CATALOG_ENV]: JSON.stringify(catalogFor(catalog, turn.model)) } : {}
1059
+ };
1060
+ runner = startRunner(await sandbox.spawn({
1061
+ command: `exec node ${shellQuote(runnerPath)} ${shellQuote(sessionDir)} ${n}`,
1062
+ workingDirectory: sessionWorkDir,
1063
+ env
1064
+ }));
1065
+ }
1066
+ const current = runner;
1067
+ const translator = new CursorSdkStreamTranslator({
1068
+ agentId: agentId ?? void 0,
1069
+ workDir: sessionWorkDir
1070
+ });
1071
+ const emit = (parts) => {
1072
+ for (const part of parts) turn.emit(part);
1073
+ };
1074
+ const onAbort = () => void stopRunner();
1075
+ if (turn.abortSignal?.aborted) onAbort();
1076
+ turn.abortSignal?.addEventListener("abort", onAbort, { once: true });
1077
+ return {
1078
+ submitToolResult: () => Promise.reject(unsupported("host-executed tools")),
1079
+ done: (async () => {
1080
+ try {
1081
+ while (!translator.finished) {
1082
+ const next = await current.stdout.next();
1083
+ if (next.done) break;
1084
+ emit(translator.push(next.value));
1085
+ }
1086
+ if (!translator.finished) {
1087
+ if (runner === current) runner = void 0;
1088
+ const { exitCode } = await current.proc.wait();
1089
+ emit(translator.end({
1090
+ exitCode,
1091
+ stderr: await current.stderr(),
1092
+ aborted: turn.abortSignal?.aborted === true
1093
+ }));
1094
+ }
1095
+ } finally {
1096
+ turn.abortSignal?.removeEventListener("abort", onAbort);
1097
+ agentId = translator.agentId ?? agentId;
1098
+ }
1099
+ })()
1100
+ };
1101
+ },
1102
+ doCompact: () => Promise.reject(unsupported("manual compaction")),
1103
+ doContinueTurn: () => Promise.reject(unsupported("turn continuation")),
1104
+ doSuspendTurn: () => Promise.reject(unsupported("turn suspension")),
1105
+ async doDetach() {
1106
+ await stop();
1107
+ return state();
1108
+ },
1109
+ async doStop() {
1110
+ await stop();
1111
+ return state();
1112
+ },
1113
+ doDestroy: stop
1114
+ };
1115
+ }
1116
+ /**
1117
+ * The model with every parameter set: the ones given, then the rest from the catalog's default
1118
+ * variant (the SDK's own choice for an unset parameter), so nothing is left for the SDK to pick.
1119
+ */
1120
+ function withParams(id, given, catalog) {
1121
+ const entry = catalog?.find((m) => m.id === id || m.aliases?.includes(id));
1122
+ const variants = Array.isArray(entry?.variants) ? entry.variants : [];
1123
+ const fallback = (variants.find((v) => v.isDefault) ?? variants[0])?.params ?? [];
1124
+ const params = [...Object.entries(given ?? {}).map(([pid, value]) => ({
1125
+ id: pid,
1126
+ value
1127
+ })), ...fallback.filter((p) => !given || !(p.id in given))];
1128
+ return params.length ? {
1129
+ id,
1130
+ params
1131
+ } : { id };
1132
+ }
1133
+ //#endregion
1134
+ export { createCursorSdkRequestTransformations as C, resolveCursorSdkCredentials as E, catalogFor as S, fetchCursorModelCatalog as T, storedShapeProblems as _, toUsage as a, CURSOR_SDK_CREDENTIAL_ENV as b, projectToolResult as c, CURSOR_SDK_RECOVERED_TOOLS as d, CURSOR_SDK_RUNNER as f, getCursorSdkBootstrap as g, cursorSdkInstallScript as h, CursorSdkStreamTranslator as i, CURSOR_SDK_BOOTSTRAP_DIR as l, CURSOR_SDK_VERSION as m, CURSOR_SDK_HOME_SKILLS_DIR as n, CURSOR_SDK_BUILTIN_TOOLS as o, CURSOR_SDK_RUNNER_PATH as p, createCursorSdk as r, projectToolCall as s, CURSOR_SDK_HARNESS_ID as t, CURSOR_SDK_MIN_NODE as u, storedWebCalls as v, cursorSdkEndpoints as w, CURSOR_SDK_MODEL_CATALOG_ENV as x, CURSOR_API_URL as y };
1135
+
1136
+ //# sourceMappingURL=src-Cmp-7YrD.js.map