@bastani/atomic 0.8.28-alpha.4 → 0.8.29-alpha.2

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 (134) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +27 -0
  3. package/dist/builtin/cursor/LICENSE +26 -0
  4. package/dist/builtin/cursor/README.md +22 -0
  5. package/dist/builtin/cursor/index.ts +9 -0
  6. package/dist/builtin/cursor/package.json +46 -0
  7. package/dist/builtin/cursor/src/auth.ts +352 -0
  8. package/dist/builtin/cursor/src/catalog-cache.ts +155 -0
  9. package/dist/builtin/cursor/src/config.ts +123 -0
  10. package/dist/builtin/cursor/src/conversation-state.ts +135 -0
  11. package/dist/builtin/cursor/src/cursor-models-raw.json +583 -0
  12. package/dist/builtin/cursor/src/model-mapper.ts +270 -0
  13. package/dist/builtin/cursor/src/models.ts +54 -0
  14. package/dist/builtin/cursor/src/native-loader.ts +71 -0
  15. package/dist/builtin/cursor/src/proto/README.md +34 -0
  16. package/dist/builtin/cursor/src/proto/agent_pb.ts +15294 -0
  17. package/dist/builtin/cursor/src/proto/protobuf-codec.ts +717 -0
  18. package/dist/builtin/cursor/src/provider.ts +301 -0
  19. package/dist/builtin/cursor/src/stream.ts +564 -0
  20. package/dist/builtin/cursor/src/transport.ts +791 -0
  21. package/dist/builtin/intercom/CHANGELOG.md +10 -0
  22. package/dist/builtin/intercom/package.json +2 -2
  23. package/dist/builtin/intercom/skills/intercom/SKILL.md +5 -5
  24. package/dist/builtin/mcp/CHANGELOG.md +10 -0
  25. package/dist/builtin/mcp/package.json +3 -3
  26. package/dist/builtin/subagents/CHANGELOG.md +18 -0
  27. package/dist/builtin/subagents/README.md +7 -3
  28. package/dist/builtin/subagents/agents/codebase-online-researcher.md +9 -24
  29. package/dist/builtin/subagents/agents/debugger.md +3 -5
  30. package/dist/builtin/subagents/package.json +4 -4
  31. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +2 -1
  32. package/dist/builtin/subagents/src/runs/foreground/execution.ts +2 -1
  33. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +1 -0
  34. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +19 -2
  35. package/dist/builtin/subagents/src/runs/shared/structured-output.ts +271 -10
  36. package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +12 -39
  37. package/dist/builtin/subagents/src/shared/types.ts +1 -0
  38. package/dist/builtin/subagents/src/shared/utils.ts +50 -10
  39. package/dist/builtin/subagents/src/slash/saved-chain-mapping.ts +77 -0
  40. package/dist/builtin/subagents/src/slash/slash-commands.ts +1 -55
  41. package/dist/builtin/web-access/CHANGELOG.md +11 -1
  42. package/dist/builtin/web-access/README.md +1 -1
  43. package/dist/builtin/web-access/github-extract.ts +1 -1
  44. package/dist/builtin/web-access/package.json +3 -3
  45. package/dist/builtin/workflows/CHANGELOG.md +44 -0
  46. package/dist/builtin/workflows/README.md +19 -1
  47. package/dist/builtin/workflows/package.json +2 -2
  48. package/dist/builtin/workflows/skills/research-codebase/SKILL.md +17 -3
  49. package/dist/builtin/workflows/src/extension/wiring.ts +17 -1
  50. package/dist/builtin/workflows/src/extension/workflow-schema.ts +34 -0
  51. package/dist/builtin/workflows/src/runs/foreground/executor.ts +13 -2
  52. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +86 -14
  53. package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +11 -3
  54. package/dist/builtin/workflows/src/shared/types.ts +8 -4
  55. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +64 -2
  56. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +8 -8
  57. package/dist/builtin/workflows/src/tui/workflow-status.ts +2 -0
  58. package/dist/core/builtin-packages.d.ts.map +1 -1
  59. package/dist/core/builtin-packages.js +6 -0
  60. package/dist/core/builtin-packages.js.map +1 -1
  61. package/dist/core/extensions/index.d.ts +1 -1
  62. package/dist/core/extensions/index.d.ts.map +1 -1
  63. package/dist/core/extensions/index.js.map +1 -1
  64. package/dist/core/extensions/types.d.ts +20 -0
  65. package/dist/core/extensions/types.d.ts.map +1 -1
  66. package/dist/core/extensions/types.js.map +1 -1
  67. package/dist/core/model-resolver.d.ts +1 -0
  68. package/dist/core/model-resolver.d.ts.map +1 -1
  69. package/dist/core/model-resolver.js +17 -8
  70. package/dist/core/model-resolver.js.map +1 -1
  71. package/dist/core/package-manager.d.ts +11 -9
  72. package/dist/core/package-manager.d.ts.map +1 -1
  73. package/dist/core/package-manager.js +55 -10
  74. package/dist/core/package-manager.js.map +1 -1
  75. package/dist/core/project-trust.d.ts +1 -0
  76. package/dist/core/project-trust.d.ts.map +1 -1
  77. package/dist/core/project-trust.js +3 -3
  78. package/dist/core/project-trust.js.map +1 -1
  79. package/dist/core/resource-loader.d.ts +9 -0
  80. package/dist/core/resource-loader.d.ts.map +1 -1
  81. package/dist/core/resource-loader.js +72 -9
  82. package/dist/core/resource-loader.js.map +1 -1
  83. package/dist/core/sdk.d.ts +3 -3
  84. package/dist/core/sdk.d.ts.map +1 -1
  85. package/dist/core/sdk.js +5 -5
  86. package/dist/core/sdk.js.map +1 -1
  87. package/dist/core/tools/index.d.ts +1 -0
  88. package/dist/core/tools/index.d.ts.map +1 -1
  89. package/dist/core/tools/index.js +1 -0
  90. package/dist/core/tools/index.js.map +1 -1
  91. package/dist/core/tools/structured-output.d.ts +39 -0
  92. package/dist/core/tools/structured-output.d.ts.map +1 -0
  93. package/dist/core/tools/structured-output.js +141 -0
  94. package/dist/core/tools/structured-output.js.map +1 -0
  95. package/dist/index.d.ts +1 -1
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +1 -1
  98. package/dist/index.js.map +1 -1
  99. package/dist/main.d.ts.map +1 -1
  100. package/dist/main.js +36 -14
  101. package/dist/main.js.map +1 -1
  102. package/dist/modes/interactive/components/login-dialog.d.ts +3 -0
  103. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  104. package/dist/modes/interactive/components/login-dialog.js +16 -0
  105. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  106. package/dist/modes/interactive/interactive-mode.d.ts +11 -0
  107. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  108. package/dist/modes/interactive/interactive-mode.js +158 -11
  109. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  110. package/dist/modes/print-mode.d.ts.map +1 -1
  111. package/dist/modes/print-mode.js +39 -0
  112. package/dist/modes/print-mode.js.map +1 -1
  113. package/docs/custom-provider.md +1 -0
  114. package/docs/extensions.md +2 -2
  115. package/docs/models.md +2 -0
  116. package/docs/packages.md +3 -1
  117. package/docs/providers.md +15 -0
  118. package/docs/sdk.md +61 -0
  119. package/docs/security.md +1 -1
  120. package/docs/subagents.md +21 -0
  121. package/docs/usage.md +2 -0
  122. package/docs/workflows.md +10 -7
  123. package/examples/extensions/README.md +1 -1
  124. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  125. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  126. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  127. package/examples/extensions/gondolin/package-lock.json +2 -2
  128. package/examples/extensions/gondolin/package.json +1 -1
  129. package/examples/extensions/sandbox/package-lock.json +2 -2
  130. package/examples/extensions/sandbox/package.json +1 -1
  131. package/examples/extensions/structured-output.ts +22 -53
  132. package/examples/extensions/with-deps/package-lock.json +2 -2
  133. package/examples/extensions/with-deps/package.json +1 -1
  134. package/package.json +12 -9
@@ -0,0 +1,717 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { create, fromBinary, fromJson, type JsonValue as ProtobufJsonValue, toBinary, toJson } from "@bufbuild/protobuf";
3
+ import { ValueSchema } from "@bufbuild/protobuf/wkt";
4
+ import { createCursorExperimentalProtocolError, parseJsonObject, parseJsonValue, type JsonObject, type JsonValue } from "../config.js";
5
+ import type { CursorUsableModel } from "../model-mapper.js";
6
+ import type { CursorConnectFrame, CursorControlMessage, CursorProtocolCodec, CursorProtocolMessage, CursorRunRequest, CursorServerMessage, CursorToolResultMessage } from "../transport.js";
7
+ import {
8
+ AgentClientMessageSchema,
9
+ AgentConversationTurnStructureSchema,
10
+ AgentRunRequestSchema,
11
+ AgentServerMessageSchema,
12
+ AssistantMessageSchema,
13
+ BackgroundShellSpawnResultSchema,
14
+ CancelActionSchema,
15
+ ClientHeartbeatSchema,
16
+ ConversationActionSchema,
17
+ ConversationStateStructureSchema,
18
+ ConversationStepSchema,
19
+ ConversationTurnStructureSchema,
20
+ DeleteRejectedSchema,
21
+ DeleteResultSchema,
22
+ DiagnosticsResultSchema,
23
+ ExecClientMessageSchema,
24
+ FetchErrorSchema,
25
+ FetchResultSchema,
26
+ GetBlobResultSchema,
27
+ GetUsableModelsRequestSchema,
28
+ GetUsableModelsResponseSchema,
29
+ GrepErrorSchema,
30
+ GrepResultSchema,
31
+ KvClientMessageSchema,
32
+ LsRejectedSchema,
33
+ LsResultSchema,
34
+ McpArgsSchema,
35
+ McpErrorSchema,
36
+ McpResultSchema,
37
+ McpSuccessSchema,
38
+ McpTextContentSchema,
39
+ McpToolCallSchema,
40
+ McpToolDefinitionSchema,
41
+ McpToolErrorSchema,
42
+ McpToolResultContentItemSchema,
43
+ McpToolResultSchema,
44
+ ModelDetailsSchema,
45
+ ReadRejectedSchema,
46
+ ReadResultSchema,
47
+ RequestContextResultSchema,
48
+ RequestContextSchema,
49
+ RequestContextSuccessSchema,
50
+ SelectedContextSchema,
51
+ SetBlobResultSchema,
52
+ ShellRejectedSchema,
53
+ ShellResultSchema,
54
+ ShellStreamSchema,
55
+ ToolCallSchema,
56
+ UserMessageActionSchema,
57
+ UserMessageSchema,
58
+ WriteRejectedSchema,
59
+ WriteResultSchema,
60
+ WriteShellStdinErrorSchema,
61
+ WriteShellStdinResultSchema,
62
+ type AgentServerMessage,
63
+ type ConversationStateStructure,
64
+ type ExecServerMessage,
65
+ type KvServerMessage,
66
+ type McpToolDefinition,
67
+ type ModelDetails,
68
+ type UserMessage,
69
+ } from "./agent_pb.js";
70
+
71
+ // Cursor protocol codec intentionally follows the MIT-licensed
72
+ // ndraiman/pi-cursor-provider implementation. The request/control bytes are
73
+ // built through Cursor's generated protobuf descriptors instead of inferred
74
+ // hand-written field concatenation so the private API sees the same semantic
75
+ // messages as the reference provider.
76
+
77
+ interface StoredCursorConversationState {
78
+ checkpoint?: Uint8Array;
79
+ blobStore: Map<string, Uint8Array>;
80
+ }
81
+
82
+
83
+ interface ParsedAssistantTextStep {
84
+ readonly kind: "assistantText";
85
+ readonly text: string;
86
+ }
87
+
88
+ interface ParsedToolCallStep {
89
+ readonly kind: "toolCall";
90
+ readonly toolCallId: string;
91
+ readonly toolName: string;
92
+ readonly arguments: JsonObject;
93
+ result?: { readonly content: string; readonly isError: boolean };
94
+ }
95
+
96
+ type ParsedTurnStep = ParsedAssistantTextStep | ParsedToolCallStep;
97
+
98
+ interface ParsedTurn {
99
+ readonly userText: string;
100
+ readonly steps: ParsedTurnStep[];
101
+ }
102
+
103
+ const CURSOR_PROTO_CLIENT_NAME = "pi";
104
+ const NATIVE_EXEC_REJECT_REASON = "Tool not available in this environment. Use the MCP tools provided instead.";
105
+ const textEncoder = new TextEncoder();
106
+ const textDecoder = new TextDecoder();
107
+
108
+ const EXEC_CASE_FIELD_NUMBERS: ReadonlyMap<string, number> = new Map([
109
+ ["shellArgs", 2],
110
+ ["writeArgs", 3],
111
+ ["deleteArgs", 4],
112
+ ["grepArgs", 5],
113
+ ["readArgs", 7],
114
+ ["lsArgs", 8],
115
+ ["diagnosticsArgs", 9],
116
+ ["requestContextArgs", 10],
117
+ ["mcpArgs", 11],
118
+ ["shellStreamArgs", 14],
119
+ ["backgroundShellSpawnArgs", 16],
120
+ ["listMcpResourcesExecArgs", 17],
121
+ ["readMcpResourceExecArgs", 18],
122
+ ["fetchArgs", 20],
123
+ ["recordScreenArgs", 21],
124
+ ["computerUseArgs", 22],
125
+ ["writeShellStdinArgs", 23],
126
+ ]);
127
+
128
+ export class CursorProtobufProtocolCodec implements CursorProtocolCodec {
129
+ readonly #blobStores = new Map<string, Map<string, Uint8Array>>();
130
+ readonly #toolDefinitions = new Map<string, readonly McpToolDefinition[]>();
131
+ readonly #runConversationIds = new Map<string, string>();
132
+ readonly #conversationStates = new Map<string, StoredCursorConversationState>();
133
+
134
+ encodeGetUsableModelsRequest(): Uint8Array {
135
+ return toBinary(GetUsableModelsRequestSchema, create(GetUsableModelsRequestSchema, {}));
136
+ }
137
+
138
+ decodeGetUsableModelsResponse(data: Uint8Array): readonly CursorUsableModel[] {
139
+ try {
140
+ try {
141
+ const direct = decodeGetUsableModelsBody(data);
142
+ if (direct.length > 0) return direct;
143
+ } catch {
144
+ // Some Cursor deployments reply to unary calls with a Connect envelope;
145
+ // fall through and try the reference provider's unwrap behavior.
146
+ }
147
+ const unwrapped = unwrapConnectUnaryBody(data);
148
+ return unwrapped ? decodeGetUsableModelsBody(unwrapped) : [];
149
+ } catch (error) {
150
+ throw createCursorExperimentalProtocolError(`Cursor protobuf GetUsableModels decoding failed: ${error instanceof Error ? error.message : String(error)}`);
151
+ }
152
+ }
153
+
154
+ encodeRunRequest(request: CursorRunRequest): Uint8Array {
155
+ const conversationIdValue = request.conversationId ?? request.requestId;
156
+ const storedState = this.#conversationStates.get(conversationIdValue);
157
+ const payload = buildCursorRequest(
158
+ request.resolvedModelId,
159
+ request.context.systemPrompt ?? "",
160
+ extractCurrentActionText(request),
161
+ parseHistoricalTurns(request.context.messages.slice(0, -1)),
162
+ conversationIdValue,
163
+ storedState?.checkpoint ?? null,
164
+ storedState?.blobStore,
165
+ );
166
+ this.#blobStores.set(request.requestId, payload.blobStore);
167
+ this.#toolDefinitions.set(request.requestId, buildMcpToolDefinitions(request));
168
+ this.#runConversationIds.set(request.requestId, conversationIdValue);
169
+ return payload.requestBytes;
170
+ }
171
+
172
+ decodeRunFrame(frame: CursorConnectFrame): readonly CursorProtocolMessage[] {
173
+ try {
174
+ const message = fromBinary(AgentServerMessageSchema, frame.data);
175
+ return decodeAgentServerMessage(message);
176
+ } catch (error) {
177
+ throw createCursorExperimentalProtocolError(`Cursor protobuf Run decoding failed: ${error instanceof Error ? error.message : String(error)}`);
178
+ }
179
+ }
180
+
181
+ encodeServerResponse(message: CursorProtocolMessage, requestId: string): Uint8Array | undefined {
182
+ if (message.type === "kvGetBlob") {
183
+ const data = this.#blobStores.get(requestId)?.get(blobKey(message.blobId));
184
+ return encodeKvClientMessage(message.id, "getBlobResult", create(GetBlobResultSchema, data ? { blobData: data } : {}));
185
+ }
186
+ if (message.type === "kvSetBlob") {
187
+ const store = this.#blobStores.get(requestId);
188
+ if (store) store.set(blobKey(message.blobId), message.blobData);
189
+ this.commitRunState(requestId);
190
+ return encodeKvClientMessage(message.id, "setBlobResult", create(SetBlobResultSchema, {}));
191
+ }
192
+ if (message.type === "conversationCheckpoint") {
193
+ this.commitRunState(requestId, message.checkpoint);
194
+ return undefined;
195
+ }
196
+ if (message.type === "requestContext") {
197
+ return encodeRequestContextResult(message, this.#toolDefinitions.get(requestId) ?? []);
198
+ }
199
+ if (message.type === "nonMcpExec") {
200
+ return encodeNativeExecRejection(message);
201
+ }
202
+ return undefined;
203
+ }
204
+
205
+ disposeRun(requestId: string): void {
206
+ this.commitRunState(requestId);
207
+ this.cleanupRun(requestId);
208
+ }
209
+
210
+ discardRun(requestId: string): void {
211
+ const conversationId = this.#runConversationIds.get(requestId);
212
+ if (conversationId) this.discardConversation(conversationId);
213
+ this.cleanupRun(requestId);
214
+ }
215
+
216
+ discardConversation(conversationId: string): void {
217
+ this.#conversationStates.delete(conversationId);
218
+ }
219
+
220
+ private cleanupRun(requestId: string): void {
221
+ this.#blobStores.delete(requestId);
222
+ this.#toolDefinitions.delete(requestId);
223
+ this.#runConversationIds.delete(requestId);
224
+ }
225
+
226
+ private commitRunState(requestId: string, checkpoint?: Uint8Array): void {
227
+ const conversationId = this.#runConversationIds.get(requestId);
228
+ if (!conversationId) return;
229
+ const runBlobStore = this.#blobStores.get(requestId);
230
+ const stored = this.#conversationStates.get(conversationId) ?? { blobStore: new Map<string, Uint8Array>() };
231
+ if (runBlobStore) {
232
+ for (const [key, value] of runBlobStore) stored.blobStore.set(key, value);
233
+ }
234
+ if (checkpoint && checkpoint.byteLength > 0) stored.checkpoint = checkpoint.slice();
235
+ this.#conversationStates.set(conversationId, stored);
236
+ }
237
+
238
+ encodeToolResult(result: CursorToolResultMessage): Uint8Array {
239
+ const mcpResult = createMcpToolResult(result.text, result.isError);
240
+ return encodeExecClientMessage(result.execNumericId, result.execId, "mcpResult", mcpResult);
241
+ }
242
+
243
+ encodeCancelRequest(): Uint8Array {
244
+ const cancelAction = create(ConversationActionSchema, {
245
+ action: { case: "cancelAction", value: create(CancelActionSchema, {}) },
246
+ });
247
+ const clientMessage = create(AgentClientMessageSchema, {
248
+ message: { case: "conversationAction", value: cancelAction },
249
+ });
250
+ return toBinary(AgentClientMessageSchema, clientMessage);
251
+ }
252
+
253
+ encodeHeartbeatRequest(): Uint8Array {
254
+ const clientMessage = create(AgentClientMessageSchema, {
255
+ message: { case: "clientHeartbeat", value: create(ClientHeartbeatSchema, {}) },
256
+ });
257
+ return toBinary(AgentClientMessageSchema, clientMessage);
258
+ }
259
+ }
260
+
261
+ function decodeGetUsableModelsBody(data: Uint8Array): readonly CursorUsableModel[] {
262
+ const decoded = fromBinary(GetUsableModelsResponseSchema, data);
263
+ return decoded.models.flatMap((model) => {
264
+ const normalized = modelDetailsToCursorUsableModel(model);
265
+ return normalized ? [normalized] : [];
266
+ });
267
+ }
268
+
269
+ function modelDetailsToCursorUsableModel(model: ModelDetails): CursorUsableModel | undefined {
270
+ const id = model.modelId.trim();
271
+ if (!id) return undefined;
272
+ return {
273
+ id,
274
+ displayName: model.displayName || model.displayNameShort || model.displayModelId || undefined,
275
+ supportsThinking: Boolean(model.thinkingDetails),
276
+ supportsReasoning: Boolean(model.thinkingDetails || model.maxMode),
277
+ };
278
+ }
279
+
280
+ function unwrapConnectUnaryBody(data: Uint8Array): Uint8Array | undefined {
281
+ let offset = 0;
282
+ while (offset + 5 <= data.byteLength) {
283
+ const flags = data[offset] ?? 0;
284
+ const view = new DataView(data.buffer, data.byteOffset + offset, data.byteLength - offset);
285
+ const length = view.getUint32(1, false);
286
+ const frameEnd = offset + 5 + length;
287
+ if (frameEnd > data.byteLength) return undefined;
288
+ if ((flags & 0b0000_0001) !== 0) return undefined;
289
+ if ((flags & 0b0000_0010) === 0) return data.slice(offset + 5, frameEnd);
290
+ offset = frameEnd;
291
+ }
292
+ return undefined;
293
+ }
294
+
295
+ function buildMcpToolDefinitions(request: CursorRunRequest): readonly McpToolDefinition[] {
296
+ return (request.context.tools ?? []).map((tool) => {
297
+ const jsonSchema = serializableJsonValue(tool.parameters);
298
+ return create(McpToolDefinitionSchema, {
299
+ name: tool.name,
300
+ description: tool.description,
301
+ providerIdentifier: CURSOR_PROTO_CLIENT_NAME,
302
+ toolName: tool.name,
303
+ inputSchema: encodeProtobufValue(jsonSchema),
304
+ });
305
+ });
306
+ }
307
+
308
+ function buildCursorRequest(
309
+ modelId: string,
310
+ systemPrompt: string,
311
+ userText: string,
312
+ turns: readonly ParsedTurn[],
313
+ conversationId: string,
314
+ checkpoint: Uint8Array | null,
315
+ existingBlobStore?: Map<string, Uint8Array>,
316
+ ): { readonly requestBytes: Uint8Array; readonly blobStore: Map<string, Uint8Array> } {
317
+ const blobStore = new Map<string, Uint8Array>(existingBlobStore ?? []);
318
+ const systemBlobId = storeAsBlob(textEncoder.encode(JSON.stringify({ role: "system", content: systemPrompt })), blobStore);
319
+ const selectedContextBlob = storeAsBlob(buildSelectedContextBlob([systemBlobId], CURSOR_PROTO_CLIENT_NAME), blobStore);
320
+ const conversationState = checkpoint
321
+ ? fromBinary(ConversationStateStructureSchema, checkpoint)
322
+ : buildConversationState(turns, blobStore, systemBlobId, selectedContextBlob);
323
+ const userMessage = createUserMessage(userText, selectedContextBlob);
324
+ const action = create(ConversationActionSchema, {
325
+ action: { case: "userMessageAction", value: create(UserMessageActionSchema, { userMessage }) },
326
+ });
327
+ const modelDetails = create(ModelDetailsSchema, { modelId, displayModelId: modelId, displayName: modelId });
328
+ const runRequest = create(AgentRunRequestSchema, { conversationState, action, modelDetails, conversationId });
329
+ const clientMessage = create(AgentClientMessageSchema, {
330
+ message: { case: "runRequest", value: runRequest },
331
+ });
332
+ return { requestBytes: toBinary(AgentClientMessageSchema, clientMessage), blobStore };
333
+ }
334
+
335
+ function buildConversationState(
336
+ turns: readonly ParsedTurn[],
337
+ blobStore: Map<string, Uint8Array>,
338
+ systemBlobId: Uint8Array,
339
+ selectedContextBlob: Uint8Array,
340
+ ): ConversationStateStructure {
341
+ const turnBlobIds: Uint8Array[] = [];
342
+ for (const turn of turns) {
343
+ const userMessage = createUserMessage(turn.userText, selectedContextBlob);
344
+ const userMessageBlobId = storeAsBlob(toBinary(UserMessageSchema, userMessage), blobStore);
345
+ const stepBlobIds = turn.steps.map((step) => storeAsBlob(buildTurnStepBytes(step), blobStore));
346
+ const agentTurn = create(AgentConversationTurnStructureSchema, {
347
+ userMessage: userMessageBlobId,
348
+ steps: stepBlobIds,
349
+ requestId: randomUUID(),
350
+ });
351
+ const turnStructure = create(ConversationTurnStructureSchema, {
352
+ turn: { case: "agentConversationTurn", value: agentTurn },
353
+ });
354
+ turnBlobIds.push(storeAsBlob(toBinary(ConversationTurnStructureSchema, turnStructure), blobStore));
355
+ }
356
+ return create(ConversationStateStructureSchema, {
357
+ rootPromptMessagesJson: [systemBlobId],
358
+ turns: turnBlobIds,
359
+ todos: [],
360
+ pendingToolCalls: [],
361
+ previousWorkspaceUris: [],
362
+ mode: 1,
363
+ fileStates: {},
364
+ fileStatesV2: {},
365
+ summaryArchives: [],
366
+ turnTimings: [],
367
+ subagentStates: {},
368
+ selfSummaryCount: 0,
369
+ readPaths: [],
370
+ clientName: CURSOR_PROTO_CLIENT_NAME,
371
+ });
372
+ }
373
+
374
+ function createUserMessage(text: string, selectedContextBlob: Uint8Array): UserMessage {
375
+ const messageId = randomUUID();
376
+ return create(UserMessageSchema, {
377
+ text,
378
+ messageId,
379
+ selectedContext: create(SelectedContextSchema, {}),
380
+ mode: 1,
381
+ selectedContextBlob,
382
+ correlationId: messageId,
383
+ });
384
+ }
385
+
386
+ function buildTurnStepBytes(step: ParsedTurnStep): Uint8Array {
387
+ if (step.kind === "assistantText") {
388
+ return toBinary(ConversationStepSchema, create(ConversationStepSchema, {
389
+ message: { case: "assistantMessage", value: create(AssistantMessageSchema, { text: step.text }) },
390
+ }));
391
+ }
392
+ const toolName = step.toolName || "tool";
393
+ const mcpToolCall = create(McpToolCallSchema, {
394
+ args: create(McpArgsSchema, {
395
+ name: toolName,
396
+ args: encodeMcpArgsMap(step.arguments),
397
+ toolCallId: step.toolCallId,
398
+ providerIdentifier: CURSOR_PROTO_CLIENT_NAME,
399
+ toolName,
400
+ }),
401
+ ...(step.result ? { result: createMcpToolCallResult(step.result.content, step.result.isError) } : {}),
402
+ });
403
+ return toBinary(ConversationStepSchema, create(ConversationStepSchema, {
404
+ message: {
405
+ case: "toolCall",
406
+ value: create(ToolCallSchema, { tool: { case: "mcpToolCall", value: mcpToolCall } }),
407
+ },
408
+ }));
409
+ }
410
+
411
+ function parseHistoricalTurns(messages: readonly CursorRunRequest["context"]["messages"][number][]): readonly ParsedTurn[] {
412
+ const turns: ParsedTurn[] = [];
413
+ let currentTurn: { userText: string; steps: ParsedTurnStep[]; toolCallById: Map<string, ParsedToolCallStep> } | undefined;
414
+ const ensureTurn = (): { userText: string; steps: ParsedTurnStep[]; toolCallById: Map<string, ParsedToolCallStep> } => {
415
+ currentTurn ??= { userText: "", steps: [], toolCallById: new Map() };
416
+ return currentTurn;
417
+ };
418
+ const flushTurn = (): void => {
419
+ if (!currentTurn) return;
420
+ if (currentTurn.userText || currentTurn.steps.length > 0) turns.push({ userText: currentTurn.userText, steps: currentTurn.steps });
421
+ currentTurn = undefined;
422
+ };
423
+ for (const message of messages) {
424
+ if (message.role === "user") {
425
+ flushTurn();
426
+ currentTurn = { userText: textFromMessage(message), steps: [], toolCallById: new Map() };
427
+ } else if (message.role === "assistant") {
428
+ const turn = ensureTurn();
429
+ for (const part of message.content) {
430
+ if (part.type === "text") appendAssistantTextStep(turn.steps, part.text);
431
+ else if (part.type === "thinking") appendAssistantTextStep(turn.steps, part.thinking);
432
+ else {
433
+ const step: ParsedToolCallStep = { kind: "toolCall", toolCallId: part.id, toolName: part.name, arguments: parseJsonObject(JSON.stringify(part.arguments)) ?? {} };
434
+ turn.steps.push(step);
435
+ turn.toolCallById.set(step.toolCallId, step);
436
+ }
437
+ }
438
+ } else {
439
+ const turn = ensureTurn();
440
+ let step = turn.toolCallById.get(message.toolCallId);
441
+ if (!step) {
442
+ step = { kind: "toolCall", toolCallId: message.toolCallId, toolName: message.toolName, arguments: {} };
443
+ turn.steps.push(step);
444
+ turn.toolCallById.set(step.toolCallId, step);
445
+ }
446
+ step.result = { content: rawToolResultText(message), isError: message.isError };
447
+ }
448
+ }
449
+ flushTurn();
450
+ return turns;
451
+ }
452
+
453
+ function appendAssistantTextStep(steps: ParsedTurnStep[], text: string): void {
454
+ if (!text) return;
455
+ const last = steps.at(-1);
456
+ if (last?.kind === "assistantText") {
457
+ steps[steps.length - 1] = { kind: "assistantText", text: `${last.text}${text}` };
458
+ return;
459
+ }
460
+ steps.push({ kind: "assistantText", text });
461
+ }
462
+
463
+ function decodeAgentServerMessage(message: AgentServerMessage): readonly CursorProtocolMessage[] {
464
+ switch (message.message.case) {
465
+ case "interactionUpdate": {
466
+ const update = message.message.value;
467
+ if (update.message.case === "textDelta") return update.message.value.text ? [{ type: "textDelta", text: update.message.value.text }] : [];
468
+ if (update.message.case === "thinkingDelta") return update.message.value.text ? [{ type: "thinkingDelta", text: update.message.value.text }] : [];
469
+ if (update.message.case === "tokenDelta") return [{ type: "usage", kind: "outputDelta", outputTokens: update.message.value.tokens }];
470
+ return [];
471
+ }
472
+ case "conversationCheckpointUpdate": {
473
+ const checkpointState = message.message.value;
474
+ const checkpoint = toBinary(ConversationStateStructureSchema, checkpointState);
475
+ const messages: CursorProtocolMessage[] = [{ type: "conversationCheckpoint", checkpoint }];
476
+ if (checkpointState.tokenDetails) messages.push({ type: "usage", kind: "checkpoint", usedTokens: checkpointState.tokenDetails.usedTokens });
477
+ return messages;
478
+ }
479
+ case "kvServerMessage":
480
+ return decodeKvServerMessage(message.message.value);
481
+ case "execServerMessage":
482
+ return decodeExecServerMessage(message.message.value);
483
+ case "interactionQuery":
484
+ return [];
485
+ default:
486
+ return [];
487
+ }
488
+ }
489
+
490
+ function decodeKvServerMessage(kvMessage: KvServerMessage): readonly CursorControlMessage[] {
491
+ if (kvMessage.message.case === "getBlobArgs") return [{ type: "kvGetBlob", id: kvMessage.id, blobId: kvMessage.message.value.blobId }];
492
+ if (kvMessage.message.case === "setBlobArgs") {
493
+ return [{ type: "kvSetBlob", id: kvMessage.id, blobId: kvMessage.message.value.blobId, blobData: kvMessage.message.value.blobData }];
494
+ }
495
+ return [];
496
+ }
497
+
498
+ function decodeExecServerMessage(execMessage: ExecServerMessage): readonly CursorProtocolMessage[] {
499
+ const execCase = execMessage.message.case;
500
+ if (execCase === "requestContextArgs") {
501
+ return [{ type: "requestContext", ...(execMessage.execId ? { execId: execMessage.execId } : {}), execNumericId: execMessage.id }];
502
+ }
503
+ if (execCase === "mcpArgs") {
504
+ const mcpArgs = execMessage.message.value;
505
+ return [{
506
+ type: "toolCall",
507
+ id: mcpArgs.toolCallId || randomUUID(),
508
+ name: mcpArgs.toolName || mcpArgs.name || "cursor_tool",
509
+ argumentsJson: JSON.stringify(decodeMcpArgsMap(mcpArgs.args ?? {})),
510
+ ...(execMessage.execId ? { execId: execMessage.execId } : {}),
511
+ execNumericId: execMessage.id,
512
+ }];
513
+ }
514
+ const fieldNumber = execCase ? EXEC_CASE_FIELD_NUMBERS.get(execCase) : undefined;
515
+ return fieldNumber === undefined ? [] : [{ type: "nonMcpExec", fieldNumber, ...(execMessage.execId ? { execId: execMessage.execId } : {}), execNumericId: execMessage.id }];
516
+ }
517
+
518
+ function encodeKvClientMessage(id: number, messageCase: "getBlobResult" | "setBlobResult", value: unknown): Uint8Array {
519
+ const response = create(KvClientMessageSchema, { id, message: { case: messageCase, value } as never });
520
+ const clientMessage = create(AgentClientMessageSchema, { message: { case: "kvClientMessage", value: response } });
521
+ return toBinary(AgentClientMessageSchema, clientMessage);
522
+ }
523
+
524
+ function encodeRequestContextResult(message: Extract<CursorControlMessage, { readonly type: "requestContext" }>, toolDefinitions: readonly McpToolDefinition[]): Uint8Array {
525
+ const requestContext = create(RequestContextSchema, {
526
+ rules: [],
527
+ repositoryInfo: [],
528
+ tools: [...toolDefinitions],
529
+ gitRepos: [],
530
+ projectLayouts: [],
531
+ mcpInstructions: [],
532
+ fileContents: {},
533
+ customSubagents: [],
534
+ });
535
+ const result = create(RequestContextResultSchema, {
536
+ result: { case: "success", value: create(RequestContextSuccessSchema, { requestContext }) },
537
+ });
538
+ return encodeExecClientMessage(message.execNumericId, message.execId, "requestContextResult", result);
539
+ }
540
+
541
+ function encodeNativeExecRejection(message: Extract<CursorServerMessage, { readonly type: "nonMcpExec" }>): Uint8Array | undefined {
542
+ const result = createNativeExecResult(message.fieldNumber);
543
+ return result ? encodeExecClientMessage(message.execNumericId, message.execId, result.caseName, result.value) : undefined;
544
+ }
545
+
546
+ function createNativeExecResult(fieldNumber: number): { readonly caseName: string; readonly value: unknown } | undefined {
547
+ switch (fieldNumber) {
548
+ case 2:
549
+ return { caseName: "shellResult", value: create(ShellResultSchema, { result: { case: "rejected", value: createShellRejected() } }) };
550
+ case 3:
551
+ return { caseName: "writeResult", value: create(WriteResultSchema, { result: { case: "rejected", value: create(WriteRejectedSchema, { path: "", reason: NATIVE_EXEC_REJECT_REASON }) } }) };
552
+ case 4:
553
+ return { caseName: "deleteResult", value: create(DeleteResultSchema, { result: { case: "rejected", value: create(DeleteRejectedSchema, { path: "", reason: NATIVE_EXEC_REJECT_REASON }) } }) };
554
+ case 5:
555
+ return { caseName: "grepResult", value: create(GrepResultSchema, { result: { case: "error", value: create(GrepErrorSchema, { error: NATIVE_EXEC_REJECT_REASON }) } }) };
556
+ case 7:
557
+ return { caseName: "readResult", value: create(ReadResultSchema, { result: { case: "rejected", value: create(ReadRejectedSchema, { path: "", reason: NATIVE_EXEC_REJECT_REASON }) } }) };
558
+ case 8:
559
+ return { caseName: "lsResult", value: create(LsResultSchema, { result: { case: "rejected", value: create(LsRejectedSchema, { path: "", reason: NATIVE_EXEC_REJECT_REASON }) } }) };
560
+ case 9:
561
+ return { caseName: "diagnosticsResult", value: create(DiagnosticsResultSchema, {}) };
562
+ case 14:
563
+ return { caseName: "shellStream", value: create(ShellStreamSchema, { event: { case: "rejected", value: createShellRejected() } }) };
564
+ case 16:
565
+ return { caseName: "backgroundShellSpawnResult", value: create(BackgroundShellSpawnResultSchema, { result: { case: "rejected", value: createShellRejected() } }) };
566
+ case 17:
567
+ return { caseName: "listMcpResourcesExecResult", value: create(McpResultSchema, {}) };
568
+ case 18:
569
+ return { caseName: "readMcpResourceExecResult", value: create(McpResultSchema, {}) };
570
+ case 20:
571
+ return { caseName: "fetchResult", value: create(FetchResultSchema, { result: { case: "error", value: create(FetchErrorSchema, { url: "", error: NATIVE_EXEC_REJECT_REASON }) } }) };
572
+ case 21:
573
+ return { caseName: "recordScreenResult", value: create(McpResultSchema, {}) };
574
+ case 22:
575
+ return { caseName: "computerUseResult", value: create(McpResultSchema, {}) };
576
+ case 23:
577
+ return { caseName: "writeShellStdinResult", value: create(WriteShellStdinResultSchema, { result: { case: "error", value: create(WriteShellStdinErrorSchema, { error: NATIVE_EXEC_REJECT_REASON }) } }) };
578
+ default:
579
+ return undefined;
580
+ }
581
+ }
582
+
583
+ function createShellRejected(): ReturnType<typeof create<typeof ShellRejectedSchema>> {
584
+ return create(ShellRejectedSchema, {
585
+ command: "",
586
+ workingDirectory: "",
587
+ reason: NATIVE_EXEC_REJECT_REASON,
588
+ isReadonly: false,
589
+ });
590
+ }
591
+
592
+ function encodeExecClientMessage(execNumericId: number | undefined, execId: string | undefined, messageCase: string, value: unknown): Uint8Array {
593
+ const execClientMessage = create(ExecClientMessageSchema, {
594
+ id: execNumericId ?? 0,
595
+ execId: execId ?? "",
596
+ message: { case: messageCase, value } as never,
597
+ });
598
+ const clientMessage = create(AgentClientMessageSchema, { message: { case: "execClientMessage", value: execClientMessage } });
599
+ return toBinary(AgentClientMessageSchema, clientMessage);
600
+ }
601
+
602
+ function createMcpToolResult(text: string, isError: boolean): ReturnType<typeof create<typeof McpResultSchema>> {
603
+ if (isError) {
604
+ return create(McpResultSchema, { result: { case: "error", value: create(McpErrorSchema, { error: text }) } });
605
+ }
606
+ return create(McpResultSchema, {
607
+ result: {
608
+ case: "success",
609
+ value: createMcpSuccess(text),
610
+ },
611
+ });
612
+ }
613
+
614
+ function createMcpToolCallResult(text: string, isError: boolean): ReturnType<typeof create<typeof McpToolResultSchema>> {
615
+ if (isError) {
616
+ return create(McpToolResultSchema, { result: { case: "error", value: create(McpToolErrorSchema, { error: text }) } });
617
+ }
618
+ return create(McpToolResultSchema, { result: { case: "success", value: createMcpSuccess(text) } });
619
+ }
620
+
621
+ function createMcpSuccess(text: string): ReturnType<typeof create<typeof McpSuccessSchema>> {
622
+ return create(McpSuccessSchema, {
623
+ content: [create(McpToolResultContentItemSchema, { content: { case: "text", value: create(McpTextContentSchema, { text }) } })],
624
+ isError: false,
625
+ });
626
+ }
627
+
628
+ function encodeProtobufValue(value: JsonValue): Uint8Array {
629
+ return toBinary(ValueSchema, fromJson(ValueSchema, value as ProtobufJsonValue));
630
+ }
631
+
632
+ function encodeMcpArgValue(value: JsonValue): Uint8Array {
633
+ try {
634
+ return encodeProtobufValue(value);
635
+ } catch {
636
+ return textEncoder.encode(String(value));
637
+ }
638
+ }
639
+
640
+ function encodeMcpArgsMap(args: JsonObject): Record<string, Uint8Array> {
641
+ const encoded: Record<string, Uint8Array> = {};
642
+ for (const [key, value] of Object.entries(args)) encoded[key] = encodeMcpArgValue(value);
643
+ return encoded;
644
+ }
645
+
646
+ function decodeMcpArgValue(value: Uint8Array): JsonValue {
647
+ try {
648
+ const decoded = toJson(ValueSchema, fromBinary(ValueSchema, value)) as ProtobufJsonValue;
649
+ return parseJsonValue(JSON.stringify(decoded)) ?? null;
650
+ } catch {
651
+ return textDecoder.decode(value);
652
+ }
653
+ }
654
+
655
+ function decodeMcpArgsMap(args: Record<string, Uint8Array>): JsonObject {
656
+ const decoded: JsonObject = {};
657
+ for (const [key, value] of Object.entries(args)) decoded[key] = decodeMcpArgValue(value);
658
+ return decoded;
659
+ }
660
+
661
+ function serializableJsonValue(value: object): JsonValue {
662
+ return parseJsonValue(JSON.stringify(value)) ?? {};
663
+ }
664
+
665
+ function extractCurrentActionText(request: CursorRunRequest): string {
666
+ const last = request.context.messages.at(-1);
667
+ return last ? textFromMessage(last) : "";
668
+ }
669
+
670
+ function rawToolResultText(message: Extract<CursorRunRequest["context"]["messages"][number], { readonly role: "toolResult" }>): string {
671
+ return message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("\n");
672
+ }
673
+
674
+ function textFromMessage(message: CursorRunRequest["context"]["messages"][number]): string {
675
+ if (message.role === "user") {
676
+ if (typeof message.content === "string") return message.content;
677
+ return message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("\n");
678
+ }
679
+ if (message.role === "assistant") {
680
+ return message.content.map((part) => {
681
+ if (part.type === "text") return part.text;
682
+ if (part.type === "thinking") return part.thinking;
683
+ return `toolCall:${part.id}:${part.name}:${JSON.stringify(part.arguments)}`;
684
+ }).join("\n");
685
+ }
686
+ return rawToolResultText(message);
687
+ }
688
+
689
+ function buildSelectedContextBlob(rootPromptBlobIds: readonly Uint8Array[], clientName: string): Uint8Array {
690
+ const parts: Uint8Array[] = [];
691
+ for (const blobId of rootPromptBlobIds) {
692
+ parts.push(new Uint8Array([0x0a, blobId.length, ...blobId]));
693
+ }
694
+ const clientBytes = textEncoder.encode(clientName);
695
+ parts.push(new Uint8Array([0xb2, 0x01, clientBytes.length, ...clientBytes]));
696
+ return concatBytes(...parts);
697
+ }
698
+
699
+ function storeAsBlob(data: Uint8Array, blobStore: Map<string, Uint8Array>): Uint8Array {
700
+ const blobId = new Uint8Array(createHash("sha256").update(data).digest());
701
+ blobStore.set(blobKey(blobId), data);
702
+ return blobId;
703
+ }
704
+
705
+ function blobKey(blobId: Uint8Array): string {
706
+ return Buffer.from(blobId).toString("hex");
707
+ }
708
+
709
+ function concatBytes(...parts: readonly Uint8Array[]): Uint8Array {
710
+ const output = new Uint8Array(parts.reduce((sum, part) => sum + part.length, 0));
711
+ let offset = 0;
712
+ for (const part of parts) {
713
+ output.set(part, offset);
714
+ offset += part.length;
715
+ }
716
+ return output;
717
+ }