@velum-labs/cursorkit 0.1.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.
Files changed (142) hide show
  1. package/DISCLAIMER.md +12 -0
  2. package/README.md +157 -0
  3. package/dist/src/agentTools/diff.d.ts +11 -0
  4. package/dist/src/agentTools/diff.js +88 -0
  5. package/dist/src/agentTools/policy.d.ts +3 -0
  6. package/dist/src/agentTools/policy.js +12 -0
  7. package/dist/src/agentTools/registry.d.ts +114 -0
  8. package/dist/src/agentTools/registry.js +663 -0
  9. package/dist/src/agentTools/results.d.ts +14 -0
  10. package/dist/src/agentTools/results.js +117 -0
  11. package/dist/src/agentTools/schemas.d.ts +3 -0
  12. package/dist/src/agentTools/schemas.js +89 -0
  13. package/dist/src/agentTools/surface.d.ts +11 -0
  14. package/dist/src/agentTools/surface.js +251 -0
  15. package/dist/src/certs.d.ts +8 -0
  16. package/dist/src/certs.js +34 -0
  17. package/dist/src/ck.d.ts +2 -0
  18. package/dist/src/ck.js +6 -0
  19. package/dist/src/ckLauncher.d.ts +150 -0
  20. package/dist/src/ckLauncher.js +1496 -0
  21. package/dist/src/cli.d.ts +2 -0
  22. package/dist/src/cli.js +265 -0
  23. package/dist/src/config.d.ts +52 -0
  24. package/dist/src/config.js +210 -0
  25. package/dist/src/connectEnvelope.d.ts +16 -0
  26. package/dist/src/connectEnvelope.js +70 -0
  27. package/dist/src/desktop.d.ts +19 -0
  28. package/dist/src/desktop.js +167 -0
  29. package/dist/src/desktopConnectProxy.d.ts +26 -0
  30. package/dist/src/desktopConnectProxy.js +175 -0
  31. package/dist/src/extensions/index.d.ts +2 -0
  32. package/dist/src/extensions/index.js +1 -0
  33. package/dist/src/extensions/registry.d.ts +8 -0
  34. package/dist/src/extensions/registry.js +52 -0
  35. package/dist/src/extensions/types.d.ts +42 -0
  36. package/dist/src/extensions/types.js +1 -0
  37. package/dist/src/fixtures/modelFusion.d.ts +103 -0
  38. package/dist/src/fixtures/modelFusion.js +404 -0
  39. package/dist/src/fixtures/replay.d.ts +9 -0
  40. package/dist/src/fixtures/replay.js +41 -0
  41. package/dist/src/fixtures/sanitizer.d.ts +9 -0
  42. package/dist/src/fixtures/sanitizer.js +43 -0
  43. package/dist/src/fixtures/schema.d.ts +38 -0
  44. package/dist/src/fixtures/schema.js +33 -0
  45. package/dist/src/gen/agent/v1/agent_pb.d.ts +21577 -0
  46. package/dist/src/gen/agent/v1/agent_pb.js +5325 -0
  47. package/dist/src/gen/aiserver/v1/aiserver_pb.d.ts +135242 -0
  48. package/dist/src/gen/aiserver/v1/aiserver_pb.js +34430 -0
  49. package/dist/src/gen/anyrun/v1/anyrun_pb.d.ts +1163 -0
  50. package/dist/src/gen/anyrun/v1/anyrun_pb.js +374 -0
  51. package/dist/src/gen/google/protobuf/google_pb.d.ts +142 -0
  52. package/dist/src/gen/google/protobuf/google_pb.js +54 -0
  53. package/dist/src/gen/internapi/v1/internapi_pb.d.ts +121 -0
  54. package/dist/src/gen/internapi/v1/internapi_pb.js +79 -0
  55. package/dist/src/logger.d.ts +8 -0
  56. package/dist/src/logger.js +37 -0
  57. package/dist/src/modelFusion/cursorHarness.d.ts +146 -0
  58. package/dist/src/modelFusion/cursorHarness.js +647 -0
  59. package/dist/src/modelFusion/index.d.ts +4 -0
  60. package/dist/src/modelFusion/index.js +2 -0
  61. package/dist/src/models/registry.d.ts +22 -0
  62. package/dist/src/models/registry.js +30 -0
  63. package/dist/src/proto.d.ts +13 -0
  64. package/dist/src/proto.js +61 -0
  65. package/dist/src/providers/openai.d.ts +64 -0
  66. package/dist/src/providers/openai.js +355 -0
  67. package/dist/src/redaction.d.ts +4 -0
  68. package/dist/src/redaction.js +65 -0
  69. package/dist/src/routeInventory.d.ts +16 -0
  70. package/dist/src/routeInventory.js +39 -0
  71. package/dist/src/routes.d.ts +37 -0
  72. package/dist/src/routes.js +227 -0
  73. package/dist/src/server.d.ts +50 -0
  74. package/dist/src/server.js +1353 -0
  75. package/dist/src/services/agent.d.ts +1 -0
  76. package/dist/src/services/agent.js +7 -0
  77. package/dist/src/services/agentRun.d.ts +60 -0
  78. package/dist/src/services/agentRun.js +391 -0
  79. package/dist/src/services/chat.d.ts +11 -0
  80. package/dist/src/services/chat.js +47 -0
  81. package/dist/src/services/models.d.ts +10 -0
  82. package/dist/src/services/models.js +216 -0
  83. package/dist/src/services/serverConfig.d.ts +2 -0
  84. package/dist/src/services/serverConfig.js +19 -0
  85. package/dist/src/testing/artifacts.d.ts +14 -0
  86. package/dist/src/testing/artifacts.js +92 -0
  87. package/dist/src/testing/cli.d.ts +4 -0
  88. package/dist/src/testing/cli.js +192 -0
  89. package/dist/src/testing/localBackend.d.ts +24 -0
  90. package/dist/src/testing/localBackend.js +310 -0
  91. package/dist/src/testing/processRunner.d.ts +7 -0
  92. package/dist/src/testing/processRunner.js +74 -0
  93. package/dist/src/testing/runner.d.ts +9 -0
  94. package/dist/src/testing/runner.js +85 -0
  95. package/dist/src/testing/scenarios.d.ts +3 -0
  96. package/dist/src/testing/scenarios.js +2535 -0
  97. package/dist/src/testing/types.d.ts +66 -0
  98. package/dist/src/testing/types.js +1 -0
  99. package/dist/src/tools/baselineInventory.d.ts +12 -0
  100. package/dist/src/tools/baselineInventory.js +680 -0
  101. package/dist/src/tools/checkModelFusionProtocol.d.ts +1 -0
  102. package/dist/src/tools/checkModelFusionProtocol.js +274 -0
  103. package/dist/src/tools/checkReleasePublishConfig.d.ts +1 -0
  104. package/dist/src/tools/checkReleasePublishConfig.js +99 -0
  105. package/dist/src/tools/generateProtoInventory.d.ts +1 -0
  106. package/dist/src/tools/generateProtoInventory.js +89 -0
  107. package/dist/src/tools/normalizeGeneratedCode.d.ts +1 -0
  108. package/dist/src/tools/normalizeGeneratedCode.js +18 -0
  109. package/dist/src/tools/releaseCheck.d.ts +26 -0
  110. package/dist/src/tools/releaseCheck.js +367 -0
  111. package/dist/src/trace.d.ts +39 -0
  112. package/dist/src/trace.js +106 -0
  113. package/dist/src/translation.d.ts +6 -0
  114. package/dist/src/translation.js +22 -0
  115. package/dist/src/upstream.d.ts +20 -0
  116. package/dist/src/upstream.js +270 -0
  117. package/docs/configuration.md +55 -0
  118. package/docs/cursor-app.md +263 -0
  119. package/docs/implementation-inventory.json +609 -0
  120. package/docs/learnings.md +363 -0
  121. package/docs/model-fusion-protocol-origin.json +126 -0
  122. package/docs/model-fusion-protocol.md +110 -0
  123. package/docs/plugin-authoring.md +24 -0
  124. package/docs/proto-inventory.md +1477 -0
  125. package/docs/protocol-surface-audit.md +92 -0
  126. package/docs/protocol.md +52 -0
  127. package/docs/refreshing-protos.md +78 -0
  128. package/docs/release-gates.md +110 -0
  129. package/docs/release-summary.json +86 -0
  130. package/docs/route-contract-manifest.json +288 -0
  131. package/docs/route-policy.json +133 -0
  132. package/docs/service-manifest.json +9490 -0
  133. package/docs/test-manifest.json +155 -0
  134. package/docs/testing-harness.md +204 -0
  135. package/docs/troubleshooting.md +36 -0
  136. package/docs/type-manifest-summary.json +28927 -0
  137. package/package.json +93 -0
  138. package/proto/agent/v1/agent.proto +5371 -0
  139. package/proto/aiserver/v1/aiserver.proto +32944 -0
  140. package/proto/anyrun/v1/anyrun.proto +294 -0
  141. package/proto/google/protobuf/google.proto +37 -0
  142. package/proto/internapi/v1/internapi.proto +32 -0
@@ -0,0 +1,663 @@
1
+ import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
2
+ import { encodeEnvelope } from "../connectEnvelope.js";
3
+ import { AgentClientMessageSchema, AgentServerMessageSchema, DeleteArgsSchema, ExecServerMessageSchema, FetchArgsSchema, GoogleProtobuf_ListValueSchema, GoogleProtobuf_StructSchema, GoogleProtobuf_ValueSchema, GrepArgsSchema, LsArgsSchema, McpArgsSchema, ReadArgsSchema, ShellArgsSchema, WriteArgsSchema, } from "../gen/agent/v1/agent_pb.js";
4
+ import { diffStats, unifiedDiff } from "./diff.js";
5
+ import { toolEnabledByPolicy } from "./policy.js";
6
+ import { agentExecClientMessageFields, formatCursorToolError, formatCursorToolResult, } from "./results.js";
7
+ import { CURSOR_TOOL_SURFACE } from "./surface.js";
8
+ export { cursorOpenAITools } from "./schemas.js";
9
+ export { agentExecClientMessageFields } from "./results.js";
10
+ const TOOL_ARGUMENT_SCHEMAS = {
11
+ read_file: {
12
+ properties: { path: "string", offset: "integer", limit: "integer" },
13
+ required: ["path"],
14
+ nonEmpty: ["path"],
15
+ },
16
+ list_dir: {
17
+ properties: { path: "string" },
18
+ required: ["path"],
19
+ nonEmpty: ["path"],
20
+ },
21
+ grep: {
22
+ properties: {
23
+ pattern: "string",
24
+ path: "string",
25
+ glob: "string",
26
+ head_limit: "integer",
27
+ },
28
+ required: ["pattern"],
29
+ nonEmpty: ["pattern"],
30
+ },
31
+ run_shell: {
32
+ properties: {
33
+ command: "string",
34
+ working_directory: "string",
35
+ timeout: "integer",
36
+ description: "string",
37
+ },
38
+ required: ["command"],
39
+ nonEmpty: ["command"],
40
+ },
41
+ write_file: {
42
+ properties: {
43
+ path: "string",
44
+ content: "string",
45
+ return_file_content_after_write: "boolean",
46
+ },
47
+ required: ["path", "content"],
48
+ nonEmpty: ["path"],
49
+ },
50
+ apply_patch: {
51
+ properties: {
52
+ path: "string",
53
+ old_string: "string",
54
+ new_string: "string",
55
+ replace_all: "boolean",
56
+ },
57
+ required: ["path", "old_string", "new_string"],
58
+ nonEmpty: ["path"],
59
+ },
60
+ delete_path: {
61
+ properties: { path: "string" },
62
+ required: ["path"],
63
+ nonEmpty: ["path"],
64
+ },
65
+ fetch_url: {
66
+ properties: { url: "string" },
67
+ required: ["url"],
68
+ nonEmpty: ["url"],
69
+ },
70
+ mcp_tool: {
71
+ properties: {
72
+ provider_identifier: "string",
73
+ tool_name: "string",
74
+ name: "string",
75
+ arguments: "object",
76
+ },
77
+ required: ["provider_identifier", "tool_name"],
78
+ nonEmpty: ["provider_identifier", "tool_name"],
79
+ },
80
+ };
81
+ export async function executeCursorToolCall(response, runtime, payloads, toolCall, options = {}) {
82
+ const validation = validateCursorToolCall(toolCall, runtime.config.agentToolPolicy);
83
+ if (!validation.ok) {
84
+ runtime.logger.warn("rejected cursor tool call", {
85
+ toolCallId: toolCall.id,
86
+ toolName: toolCall.function.name,
87
+ errorCode: validation.error.code,
88
+ details: validation.error.details,
89
+ });
90
+ return formatCursorToolError(validation.error);
91
+ }
92
+ const parsedArgs = validation.args;
93
+ if (validation.name === "apply_patch") {
94
+ return await executeApplyPatch(response, runtime, payloads, toolCall, parsedArgs, options);
95
+ }
96
+ const execId = `cursor-rpc-tool-${toolCall.id}`;
97
+ const id = runtime.nextAgentExecId;
98
+ runtime.nextAgentExecId += 1;
99
+ const execServerMessage = cursorToolCallToExecServerMessage(id, execId, validation.name, toolCall.id, parsedArgs);
100
+ if (execServerMessage === undefined) {
101
+ return formatCursorToolError({
102
+ code: "unsupported_tool",
103
+ toolName: toolCall.function.name,
104
+ toolCallId: toolCall.id,
105
+ message: `Unsupported tool call: ${toolCall.function.name}`,
106
+ });
107
+ }
108
+ runtime.logger.info("requested cursor tool execution", {
109
+ id,
110
+ execId,
111
+ toolCallId: toolCall.id,
112
+ toolName: validation.name,
113
+ toolArgsSummary: summarizeToolArgs(parsedArgs),
114
+ });
115
+ response.write(encodeEnvelope(toBinary(AgentServerMessageSchema, create(AgentServerMessageSchema, {
116
+ execServerMessage,
117
+ }))));
118
+ let result;
119
+ try {
120
+ result = await waitForCursorToolResult(payloads, id, execId, runtime.config.toolResultTimeoutMs ?? 120_000, options.signal);
121
+ }
122
+ catch (error) {
123
+ const message = error instanceof Error ? error.message : String(error);
124
+ return formatCursorToolError({
125
+ code: "tool_result_timeout",
126
+ toolName: validation.name,
127
+ toolCallId: toolCall.id,
128
+ message,
129
+ });
130
+ }
131
+ runtime.logger.info("received cursor tool result", {
132
+ id,
133
+ execId,
134
+ fields: agentExecClientMessageFields(result),
135
+ });
136
+ return formatCursorToolResult(result);
137
+ }
138
+ export function validateCursorToolCall(toolCall, policy) {
139
+ const name = toolCall.function.name;
140
+ const schema = toolArgumentSchemaFor(name);
141
+ const surfaceEntry = CURSOR_TOOL_SURFACE.find((entry) => entry.openAIToolName === name);
142
+ if (schema === undefined || surfaceEntry === undefined) {
143
+ return {
144
+ ok: false,
145
+ error: {
146
+ code: "unsupported_tool",
147
+ toolName: name,
148
+ toolCallId: toolCall.id,
149
+ message: `Unsupported tool call: ${name}`,
150
+ },
151
+ };
152
+ }
153
+ if (!toolEnabledByPolicy(surfaceEntry, policy)) {
154
+ return {
155
+ ok: false,
156
+ error: {
157
+ code: "tool_not_enabled",
158
+ toolName: name,
159
+ toolCallId: toolCall.id,
160
+ message: `Tool ${name} is not enabled by the current Cursor tool policy.`,
161
+ },
162
+ };
163
+ }
164
+ const parsed = parseToolArguments(toolCall);
165
+ if (!parsed.ok) {
166
+ return {
167
+ ok: false,
168
+ error: {
169
+ code: "invalid_tool_arguments",
170
+ toolName: name,
171
+ toolCallId: toolCall.id,
172
+ message: parsed.message,
173
+ },
174
+ };
175
+ }
176
+ const details = validateToolArguments(parsed.args, schema);
177
+ if (details.length > 0) {
178
+ return {
179
+ ok: false,
180
+ error: {
181
+ code: "invalid_tool_arguments",
182
+ toolName: name,
183
+ toolCallId: toolCall.id,
184
+ message: `Invalid arguments for ${name}.`,
185
+ details,
186
+ },
187
+ };
188
+ }
189
+ return { ok: true, name: name, args: parsed.args };
190
+ }
191
+ export function cursorToolCallToExecServerMessage(id, execId, name, toolCallId, args) {
192
+ if (name === "read_file") {
193
+ return create(ExecServerMessageSchema, {
194
+ id,
195
+ execId,
196
+ readArgs: create(ReadArgsSchema, {
197
+ path: stringArg(args, "path"),
198
+ toolCallId,
199
+ offset: numberArg(args, "offset"),
200
+ limit: numberArg(args, "limit"),
201
+ }),
202
+ });
203
+ }
204
+ if (name === "list_dir") {
205
+ return create(ExecServerMessageSchema, {
206
+ id,
207
+ execId,
208
+ lsArgs: create(LsArgsSchema, {
209
+ path: stringArg(args, "path"),
210
+ ignore: [],
211
+ toolCallId,
212
+ }),
213
+ });
214
+ }
215
+ if (name === "grep") {
216
+ return create(ExecServerMessageSchema, {
217
+ id,
218
+ execId,
219
+ grepArgs: create(GrepArgsSchema, {
220
+ pattern: stringArg(args, "pattern"),
221
+ path: optionalStringArg(args, "path"),
222
+ glob: optionalStringArg(args, "glob"),
223
+ headLimit: numberArg(args, "head_limit"),
224
+ toolCallId,
225
+ }),
226
+ });
227
+ }
228
+ if (name === "run_shell") {
229
+ const command = stringArg(args, "command");
230
+ return create(ExecServerMessageSchema, {
231
+ id,
232
+ execId,
233
+ shellArgs: create(ShellArgsSchema, {
234
+ command,
235
+ workingDirectory: optionalStringArg(args, "working_directory") ?? "",
236
+ timeout: numberArg(args, "timeout") ?? 60_000,
237
+ toolCallId,
238
+ simpleCommands: shellSimpleCommands(command),
239
+ hasInputRedirect: /<\s*\S/.test(command),
240
+ hasOutputRedirect: />\s*\S/.test(command),
241
+ isBackground: false,
242
+ skipApproval: false,
243
+ closeStdin: true,
244
+ description: optionalStringArg(args, "description"),
245
+ }),
246
+ });
247
+ }
248
+ if (name === "write_file") {
249
+ return create(ExecServerMessageSchema, {
250
+ id,
251
+ execId,
252
+ writeArgs: create(WriteArgsSchema, {
253
+ path: stringArg(args, "path"),
254
+ fileText: stringArg(args, "content"),
255
+ toolCallId,
256
+ returnFileContentAfterWrite: booleanArg(args, "return_file_content_after_write") ?? true,
257
+ }),
258
+ });
259
+ }
260
+ if (name === "delete_path") {
261
+ return create(ExecServerMessageSchema, {
262
+ id,
263
+ execId,
264
+ deleteArgs: create(DeleteArgsSchema, {
265
+ path: stringArg(args, "path"),
266
+ toolCallId,
267
+ }),
268
+ });
269
+ }
270
+ if (name === "fetch_url") {
271
+ return create(ExecServerMessageSchema, {
272
+ id,
273
+ execId,
274
+ fetchArgs: create(FetchArgsSchema, {
275
+ url: stringArg(args, "url"),
276
+ toolCallId,
277
+ }),
278
+ });
279
+ }
280
+ if (name === "mcp_tool") {
281
+ const toolName = stringArg(args, "tool_name");
282
+ const providerIdentifier = stringArg(args, "provider_identifier");
283
+ return create(ExecServerMessageSchema, {
284
+ id,
285
+ execId,
286
+ mcpArgs: create(McpArgsSchema, {
287
+ name: optionalStringArg(args, "name") ?? toolName,
288
+ toolName,
289
+ providerIdentifier,
290
+ args: jsonObjectToProtoValueMap(args.arguments),
291
+ toolCallId,
292
+ smartModeApprovalOnly: false,
293
+ skipApproval: false,
294
+ }),
295
+ });
296
+ }
297
+ return undefined;
298
+ }
299
+ async function waitForCursorToolResult(payloads, id, execId, timeoutMs, signal) {
300
+ const started = Date.now();
301
+ while (Date.now() - started < timeoutMs) {
302
+ if (signal?.aborted === true) {
303
+ throw new Error(`Aborted waiting for Cursor tool result ${execId}`);
304
+ }
305
+ const remainingMs = Math.max(1, timeoutMs - (Date.now() - started));
306
+ const next = await Promise.race([
307
+ payloads.next(),
308
+ new Promise((resolve) => setTimeout(() => resolve({ done: true }), remainingMs)),
309
+ ]);
310
+ if (next.done === true || next.value === undefined) {
311
+ break;
312
+ }
313
+ let message;
314
+ try {
315
+ message = fromBinary(AgentClientMessageSchema, next.value);
316
+ }
317
+ catch {
318
+ continue;
319
+ }
320
+ const execMessage = message.execClientMessage;
321
+ if (execMessage === undefined) {
322
+ continue;
323
+ }
324
+ if (execMessage.id === id || execMessage.execId === execId) {
325
+ return execMessage;
326
+ }
327
+ }
328
+ throw new Error(`Timed out waiting for Cursor tool result ${execId}`);
329
+ }
330
+ async function runExecRoundTrip(response, runtime, payloads, toolCallId, build, signal) {
331
+ const id = runtime.nextAgentExecId;
332
+ runtime.nextAgentExecId += 1;
333
+ const execId = `cursor-rpc-tool-${toolCallId}-${id}`;
334
+ response.write(encodeEnvelope(toBinary(AgentServerMessageSchema, create(AgentServerMessageSchema, {
335
+ execServerMessage: build(id, execId),
336
+ }))));
337
+ return await waitForCursorToolResult(payloads, id, execId, runtime.config.toolResultTimeoutMs ?? 120_000, signal);
338
+ }
339
+ /**
340
+ * apply_patch is synthesized on the Exec channel: the native Cursor edit tool
341
+ * (EditToolCall) is not exposed through ExecServerMessage, so we read the file,
342
+ * apply the search/replace in-process, write the new content back, and return a
343
+ * unified diff as the tool result.
344
+ */
345
+ async function executeApplyPatch(response, runtime, payloads, toolCall, args, options) {
346
+ const path = stringArg(args, "path");
347
+ const oldString = stringArg(args, "old_string");
348
+ const newString = stringArg(args, "new_string");
349
+ const replaceAll = booleanArg(args, "replace_all") ?? false;
350
+ runtime.logger.info("requested cursor apply_patch", {
351
+ toolCallId: toolCall.id,
352
+ path,
353
+ oldStringChars: oldString.length,
354
+ newStringChars: newString.length,
355
+ replaceAll,
356
+ });
357
+ let readClient;
358
+ try {
359
+ readClient = await runExecRoundTrip(response, runtime, payloads, toolCall.id, (id, execId) => create(ExecServerMessageSchema, {
360
+ id,
361
+ execId,
362
+ readArgs: create(ReadArgsSchema, { path, toolCallId: toolCall.id }),
363
+ }), options.signal);
364
+ }
365
+ catch (error) {
366
+ return formatCursorToolError({
367
+ code: "tool_result_timeout",
368
+ toolName: "apply_patch",
369
+ toolCallId: toolCall.id,
370
+ message: error instanceof Error ? error.message : String(error),
371
+ });
372
+ }
373
+ const read = readClient.readResult;
374
+ let before;
375
+ let creating = false;
376
+ if (read?.success !== undefined) {
377
+ if (read.success.truncated) {
378
+ return applyPatchError(toolCall.id, "file_truncated", `File ${path} is too large to edit with apply_patch; use write_file with the full content instead.`);
379
+ }
380
+ before = readSuccessContent(read.success);
381
+ }
382
+ else if (read?.fileNotFound !== undefined && oldString.length === 0) {
383
+ before = "";
384
+ creating = true;
385
+ }
386
+ else {
387
+ return applyPatchError(toolCall.id, "read_failed", `Could not read ${path} for apply_patch.`, { read: agentExecClientMessageFields(readClient) });
388
+ }
389
+ let after;
390
+ if (oldString.length === 0) {
391
+ after = newString;
392
+ }
393
+ else {
394
+ const occurrences = countOccurrences(before, oldString);
395
+ if (occurrences === 0) {
396
+ return applyPatchError(toolCall.id, "old_string_not_found", `old_string was not found in ${path}.`);
397
+ }
398
+ if (occurrences > 1 && !replaceAll) {
399
+ return applyPatchError(toolCall.id, "old_string_not_unique", `old_string matched ${occurrences} times in ${path}. Add surrounding context to make it unique, or set replace_all=true.`);
400
+ }
401
+ after = replaceAll
402
+ ? before.split(oldString).join(newString)
403
+ : replaceOnce(before, oldString, newString);
404
+ }
405
+ if (after === before) {
406
+ return applyPatchError(toolCall.id, "no_change", `apply_patch produced no change to ${path}.`);
407
+ }
408
+ let writeClient;
409
+ try {
410
+ writeClient = await runExecRoundTrip(response, runtime, payloads, toolCall.id, (id, execId) => create(ExecServerMessageSchema, {
411
+ id,
412
+ execId,
413
+ writeArgs: create(WriteArgsSchema, {
414
+ path,
415
+ fileText: after,
416
+ toolCallId: toolCall.id,
417
+ returnFileContentAfterWrite: false,
418
+ }),
419
+ }), options.signal);
420
+ }
421
+ catch (error) {
422
+ return formatCursorToolError({
423
+ code: "tool_result_timeout",
424
+ toolName: "apply_patch",
425
+ toolCallId: toolCall.id,
426
+ message: error instanceof Error ? error.message : String(error),
427
+ });
428
+ }
429
+ const write = writeClient.writeResult;
430
+ if (write?.success === undefined) {
431
+ return applyPatchError(toolCall.id, "write_failed", `Cursor did not apply the write for ${path}.`, { write: formatCursorToolResult(writeClient) });
432
+ }
433
+ const stats = diffStats(before, after);
434
+ runtime.logger.info("applied cursor apply_patch", {
435
+ toolCallId: toolCall.id,
436
+ path,
437
+ created: creating,
438
+ linesAdded: stats.added,
439
+ linesRemoved: stats.removed,
440
+ });
441
+ return JSON.stringify({
442
+ status: "success",
443
+ tool: "apply_patch",
444
+ path,
445
+ created: creating,
446
+ lines_added: stats.added,
447
+ lines_removed: stats.removed,
448
+ diff: unifiedDiff(path, before, after),
449
+ }, null, 2);
450
+ }
451
+ function applyPatchError(toolCallId, reason, message, extra) {
452
+ return JSON.stringify({
453
+ status: "error",
454
+ tool: "apply_patch",
455
+ reason,
456
+ tool_call_id: toolCallId,
457
+ message,
458
+ ...(extra ?? {}),
459
+ }, null, 2);
460
+ }
461
+ function readSuccessContent(success) {
462
+ if (success.content.length > 0) {
463
+ return success.content;
464
+ }
465
+ if (success.data.length > 0) {
466
+ return Buffer.from(success.data).toString("utf8");
467
+ }
468
+ return success.content;
469
+ }
470
+ function countOccurrences(haystack, needle) {
471
+ if (needle.length === 0) {
472
+ return 0;
473
+ }
474
+ let count = 0;
475
+ let index = haystack.indexOf(needle);
476
+ while (index !== -1) {
477
+ count += 1;
478
+ index = haystack.indexOf(needle, index + needle.length);
479
+ }
480
+ return count;
481
+ }
482
+ function replaceOnce(haystack, needle, replacement) {
483
+ const index = haystack.indexOf(needle);
484
+ if (index === -1) {
485
+ return haystack;
486
+ }
487
+ return (haystack.slice(0, index) +
488
+ replacement +
489
+ haystack.slice(index + needle.length));
490
+ }
491
+ export function summarizeToolArgs(args) {
492
+ const summary = {};
493
+ for (const [key, value] of Object.entries(args)) {
494
+ if (typeof value === "string") {
495
+ summary[key] = { type: "string", chars: value.length };
496
+ continue;
497
+ }
498
+ if (typeof value === "number" || typeof value === "boolean") {
499
+ summary[key] = value;
500
+ continue;
501
+ }
502
+ if (value !== null && typeof value === "object") {
503
+ summary[key] = {
504
+ type: Array.isArray(value) ? "array" : "object",
505
+ keys: Array.isArray(value) ? value.length : Object.keys(value).sort(),
506
+ };
507
+ continue;
508
+ }
509
+ summary[key] = value;
510
+ }
511
+ return summary;
512
+ }
513
+ function parseToolArguments(toolCall) {
514
+ try {
515
+ const parsed = JSON.parse(toolCall.function.arguments);
516
+ if (parsed === null ||
517
+ typeof parsed !== "object" ||
518
+ Array.isArray(parsed)) {
519
+ return {
520
+ ok: false,
521
+ message: "Tool arguments must be a JSON object.",
522
+ };
523
+ }
524
+ return { ok: true, args: parsed };
525
+ }
526
+ catch (error) {
527
+ const message = error instanceof Error ? error.message : String(error);
528
+ return {
529
+ ok: false,
530
+ message: `Tool arguments are not valid JSON: ${message}`,
531
+ };
532
+ }
533
+ }
534
+ function toolArgumentSchemaFor(name) {
535
+ if (Object.hasOwn(TOOL_ARGUMENT_SCHEMAS, name)) {
536
+ return TOOL_ARGUMENT_SCHEMAS[name];
537
+ }
538
+ return undefined;
539
+ }
540
+ function validateToolArguments(args, schema) {
541
+ const details = [];
542
+ for (const key of Object.keys(args)) {
543
+ if (!Object.hasOwn(schema.properties, key)) {
544
+ details.push(`Unknown argument "${key}".`);
545
+ }
546
+ }
547
+ for (const key of schema.required) {
548
+ if (args[key] === undefined) {
549
+ details.push(`Missing required argument "${key}".`);
550
+ }
551
+ }
552
+ for (const [key, expectedType] of Object.entries(schema.properties)) {
553
+ const value = args[key];
554
+ if (value === undefined) {
555
+ continue;
556
+ }
557
+ if (!valueMatchesToolArgumentType(value, expectedType)) {
558
+ details.push(`Argument "${key}" must be ${toolArgumentTypeDescription(expectedType)}.`);
559
+ continue;
560
+ }
561
+ if (schema.nonEmpty?.includes(key) === true &&
562
+ typeof value === "string" &&
563
+ value.trim().length === 0) {
564
+ details.push(`Argument "${key}" must not be empty.`);
565
+ }
566
+ }
567
+ return details;
568
+ }
569
+ function valueMatchesToolArgumentType(value, type) {
570
+ switch (type) {
571
+ case "string":
572
+ return typeof value === "string";
573
+ case "integer":
574
+ return typeof value === "number" && Number.isInteger(value);
575
+ case "boolean":
576
+ return typeof value === "boolean";
577
+ case "object":
578
+ return (value !== null && typeof value === "object" && !Array.isArray(value));
579
+ default: {
580
+ const exhaustive = type;
581
+ return exhaustive;
582
+ }
583
+ }
584
+ }
585
+ function toolArgumentTypeDescription(type) {
586
+ switch (type) {
587
+ case "string":
588
+ return "a string";
589
+ case "integer":
590
+ return "an integer";
591
+ case "boolean":
592
+ return "a boolean";
593
+ case "object":
594
+ return "an object";
595
+ default: {
596
+ const exhaustive = type;
597
+ return exhaustive;
598
+ }
599
+ }
600
+ }
601
+ function stringArg(args, key) {
602
+ const value = args[key];
603
+ return typeof value === "string" ? value : "";
604
+ }
605
+ function optionalStringArg(args, key) {
606
+ const value = stringArg(args, key);
607
+ return value.length > 0 ? value : undefined;
608
+ }
609
+ function numberArg(args, key) {
610
+ const value = args[key];
611
+ return typeof value === "number" && Number.isFinite(value)
612
+ ? value
613
+ : undefined;
614
+ }
615
+ function booleanArg(args, key) {
616
+ const value = args[key];
617
+ return typeof value === "boolean" ? value : undefined;
618
+ }
619
+ function shellSimpleCommands(command) {
620
+ return command
621
+ .split(/[;&|]+/)
622
+ .map((part) => part.trim().split(/\s+/)[0])
623
+ .filter((part) => part !== undefined && part.length > 0);
624
+ }
625
+ function jsonObjectToProtoValueMap(value) {
626
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
627
+ return {};
628
+ }
629
+ const result = {};
630
+ for (const [key, entry] of Object.entries(value)) {
631
+ result[key] = jsonToProtoValue(entry);
632
+ }
633
+ return result;
634
+ }
635
+ function jsonToProtoValue(value) {
636
+ if (value === null) {
637
+ return create(GoogleProtobuf_ValueSchema, { nullValue: 0 });
638
+ }
639
+ if (typeof value === "number") {
640
+ return create(GoogleProtobuf_ValueSchema, { numberValue: value });
641
+ }
642
+ if (typeof value === "string") {
643
+ return create(GoogleProtobuf_ValueSchema, { stringValue: value });
644
+ }
645
+ if (typeof value === "boolean") {
646
+ return create(GoogleProtobuf_ValueSchema, { boolValue: value });
647
+ }
648
+ if (Array.isArray(value)) {
649
+ return create(GoogleProtobuf_ValueSchema, {
650
+ listValue: create(GoogleProtobuf_ListValueSchema, {
651
+ values: value.map((item) => jsonToProtoValue(item)),
652
+ }),
653
+ });
654
+ }
655
+ if (typeof value === "object") {
656
+ return create(GoogleProtobuf_ValueSchema, {
657
+ structValue: create(GoogleProtobuf_StructSchema, {
658
+ fields: jsonObjectToProtoValueMap(value),
659
+ }),
660
+ });
661
+ }
662
+ return create(GoogleProtobuf_ValueSchema, { stringValue: String(value) });
663
+ }
@@ -0,0 +1,14 @@
1
+ import { fromBinary } from "@bufbuild/protobuf";
2
+ import { AgentClientMessageSchema } from "../gen/agent/v1/agent_pb.js";
3
+ type ExecClientMessage = NonNullable<ReturnType<typeof fromBinary<typeof AgentClientMessageSchema>>["execClientMessage"]>;
4
+ export interface CursorToolError {
5
+ code: "invalid_tool_arguments" | "tool_not_enabled" | "unsupported_tool" | "tool_result_timeout";
6
+ toolName: string;
7
+ toolCallId: string;
8
+ message: string;
9
+ details?: string[];
10
+ }
11
+ export declare function formatCursorToolResult(message: ExecClientMessage): string;
12
+ export declare function formatCursorToolError(error: CursorToolError): string;
13
+ export declare function agentExecClientMessageFields(message: ExecClientMessage): string[];
14
+ export {};