@playwo/opencode-cursor-oauth 0.0.0-dev.12f4642b8f6b → 0.0.0-dev.14c6316643ec

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.
@@ -1,17 +1,19 @@
1
1
  import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
2
2
  import { AgentClientMessageSchema, AgentServerMessageSchema, ExecClientMessageSchema, McpErrorSchema, McpResultSchema, McpSuccessSchema, McpTextContentSchema, McpToolResultContentItemSchema, } from "../proto/agent_pb";
3
- import { errorDetails, logPluginError, logPluginWarn } from "../logger";
3
+ import { errorDetails, logPluginError, logPluginInfo, logPluginWarn, } from "../logger";
4
4
  import { formatToolCallSummary, formatToolResultSummary, } from "../openai/messages";
5
5
  import { activeBridges, updateStoredConversationAfterCompletion, } from "./conversation-state";
6
6
  import { startBridge } from "./bridge-session";
7
7
  import { updateConversationCheckpoint, syncStoredBlobStore, } from "./state-sync";
8
8
  import { SSE_HEADERS } from "./sse";
9
9
  import { computeUsage, createConnectFrameParser, createThinkingTagFilter, parseConnectEndStream, processServerMessage, scheduleBridgeEnd, } from "./stream-dispatch";
10
+ import { createBridgeCloseController } from "./bridge-close-controller";
10
11
  const SSE_KEEPALIVE_INTERVAL_MS = 15_000;
11
- function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools, modelId, bridgeKey, convKey, metadata) {
12
+ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, cloudRule, mcpTools, modelId, bridgeKey, convKey, metadata) {
12
13
  const completionId = `chatcmpl-${crypto.randomUUID().replace(/-/g, "").slice(0, 28)}`;
13
14
  const created = Math.floor(Date.now() / 1000);
14
15
  let keepaliveTimer;
16
+ const bridgeCloseController = createBridgeCloseController(bridge);
15
17
  const stopKeepalive = () => {
16
18
  if (!keepaliveTimer)
17
19
  return;
@@ -47,6 +49,19 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
47
49
  return;
48
50
  controller.enqueue(encoder.encode("data: [DONE]\n\n"));
49
51
  };
52
+ const failStream = (message, code) => {
53
+ if (closed)
54
+ return;
55
+ sendSSE({
56
+ error: {
57
+ message,
58
+ type: "server_error",
59
+ ...(code ? { code } : {}),
60
+ },
61
+ });
62
+ sendDone();
63
+ closeController();
64
+ };
50
65
  const closeController = () => {
51
66
  if (closed)
52
67
  return;
@@ -75,7 +90,7 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
75
90
  const processChunk = createConnectFrameParser((messageBytes) => {
76
91
  try {
77
92
  const serverMessage = fromBinary(AgentServerMessageSchema, messageBytes);
78
- processServerMessage(serverMessage, blobStore, mcpTools, (data) => bridge.write(data), state, (text, isThinking) => {
93
+ processServerMessage(serverMessage, blobStore, cloudRule, mcpTools, (data) => bridge.write(data), state, (text, isThinking) => {
79
94
  if (isThinking) {
80
95
  sendSSE(makeChunk({ reasoning_content: text }));
81
96
  return;
@@ -88,7 +103,13 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
88
103
  sendSSE(makeChunk({ content }));
89
104
  }
90
105
  }, (exec) => {
91
- state.pendingExecs.push(exec);
106
+ const existingIndex = state.pendingExecs.findIndex((candidate) => candidate.toolCallId === exec.toolCallId);
107
+ if (existingIndex >= 0) {
108
+ state.pendingExecs[existingIndex] = exec;
109
+ }
110
+ else {
111
+ state.pendingExecs.push(exec);
112
+ }
92
113
  mcpExecReceived = true;
93
114
  const flushed = tagFilter.flush();
94
115
  if (flushed.reasoning)
@@ -127,6 +148,7 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
127
148
  bridge,
128
149
  heartbeatTimer,
129
150
  blobStore,
151
+ cloudRule,
130
152
  mcpTools,
131
153
  pendingExecs: state.pendingExecs,
132
154
  modelId,
@@ -138,7 +160,21 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
138
160
  sendSSE(makeChunk({}, "tool_calls"));
139
161
  sendDone();
140
162
  closeController();
141
- }, (checkpointBytes) => updateConversationCheckpoint(convKey, checkpointBytes), () => scheduleBridgeEnd(bridge), (info) => {
163
+ }, (checkpointBytes) => {
164
+ updateConversationCheckpoint(convKey, checkpointBytes);
165
+ bridgeCloseController.noteCheckpoint();
166
+ }, () => bridgeCloseController.noteTurnEnded(), (info) => {
167
+ endStreamError = new Error(`Cursor returned unsupported ${info.category}: ${info.caseName}${info.detail ? ` (${info.detail})` : ""}`);
168
+ logPluginError("Closing Cursor bridge after unsupported message", {
169
+ modelId,
170
+ bridgeKey,
171
+ convKey,
172
+ category: info.category,
173
+ caseName: info.caseName,
174
+ detail: info.detail,
175
+ });
176
+ scheduleBridgeEnd(bridge);
177
+ }, (info) => {
142
178
  endStreamError = new Error(`Cursor requested unsupported exec type: ${info.execCase}`);
143
179
  logPluginError("Closing Cursor bridge after unsupported exec", {
144
180
  modelId,
@@ -180,17 +216,30 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
180
216
  stopKeepalive();
181
217
  }
182
218
  }, SSE_KEEPALIVE_INTERVAL_MS);
219
+ logPluginInfo("Opened Cursor streaming bridge", {
220
+ modelId,
221
+ bridgeKey,
222
+ convKey,
223
+ mcpToolCount: mcpTools.length,
224
+ hasCloudRule: Boolean(cloudRule),
225
+ });
183
226
  bridge.onData(processChunk);
184
227
  bridge.onClose((code) => {
228
+ logPluginInfo("Cursor streaming bridge closed", {
229
+ modelId,
230
+ bridgeKey,
231
+ convKey,
232
+ code,
233
+ mcpExecReceived,
234
+ hadEndStreamError: Boolean(endStreamError),
235
+ });
236
+ bridgeCloseController.dispose();
185
237
  clearInterval(heartbeatTimer);
186
238
  stopKeepalive();
187
239
  syncStoredBlobStore(convKey, blobStore);
188
240
  if (endStreamError) {
189
241
  activeBridges.delete(bridgeKey);
190
- if (!closed) {
191
- closed = true;
192
- controller.error(endStreamError);
193
- }
242
+ failStream(endStreamError.message, "cursor_bridge_closed");
194
243
  return;
195
244
  }
196
245
  if (!mcpExecReceived) {
@@ -210,15 +259,12 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
210
259
  }
211
260
  activeBridges.delete(bridgeKey);
212
261
  if (code !== 0 && !closed) {
213
- sendSSE(makeChunk({ content: "\n[Error: bridge connection lost]" }));
214
- sendSSE(makeChunk({}, "stop"));
215
- sendSSE(makeUsageChunk());
216
- sendDone();
217
- closeController();
262
+ failStream("Cursor bridge connection lost", "cursor_bridge_closed");
218
263
  }
219
264
  });
220
265
  },
221
266
  cancel(reason) {
267
+ bridgeCloseController.dispose();
222
268
  stopKeepalive();
223
269
  clearInterval(heartbeatTimer);
224
270
  syncStoredBlobStore(convKey, blobStore);
@@ -238,11 +284,36 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
238
284
  return new Response(stream, { headers: SSE_HEADERS });
239
285
  }
240
286
  export async function handleStreamingResponse(payload, accessToken, modelId, bridgeKey, convKey, metadata) {
287
+ logPluginInfo("Starting Cursor streaming response", {
288
+ modelId,
289
+ bridgeKey,
290
+ convKey,
291
+ mcpToolCount: payload.mcpTools.length,
292
+ });
241
293
  const { bridge, heartbeatTimer } = await startBridge(accessToken, payload.requestBytes);
242
- return createBridgeStreamResponse(bridge, heartbeatTimer, payload.blobStore, payload.mcpTools, modelId, bridgeKey, convKey, metadata);
294
+ return createBridgeStreamResponse(bridge, heartbeatTimer, payload.blobStore, payload.cloudRule, payload.mcpTools, modelId, bridgeKey, convKey, metadata);
243
295
  }
244
- export function handleToolResultResume(active, toolResults, bridgeKey, convKey) {
245
- const { bridge, heartbeatTimer, blobStore, mcpTools, pendingExecs, modelId, metadata, } = active;
296
+ async function waitForResolvablePendingExecs(active, toolResults, timeoutMs = 2_000) {
297
+ const pendingToolCallIds = new Set(toolResults.map((result) => result.toolCallId));
298
+ const deadline = Date.now() + timeoutMs;
299
+ while (Date.now() < deadline) {
300
+ const unresolved = active.pendingExecs.filter((exec) => pendingToolCallIds.has(exec.toolCallId) && exec.execMsgId === 0);
301
+ if (unresolved.length === 0) {
302
+ return unresolved;
303
+ }
304
+ await new Promise((resolve) => setTimeout(resolve, 25));
305
+ }
306
+ const unresolved = active.pendingExecs.filter((exec) => pendingToolCallIds.has(exec.toolCallId) && exec.execMsgId === 0);
307
+ if (unresolved.length > 0) {
308
+ logPluginWarn("Cursor exec metadata did not arrive before tool-result resume", {
309
+ bridgeToolCallIds: unresolved.map((exec) => exec.toolCallId),
310
+ modelId: active.modelId,
311
+ });
312
+ }
313
+ return unresolved;
314
+ }
315
+ export async function handleToolResultResume(active, toolResults, bridgeKey, convKey) {
316
+ const { bridge, heartbeatTimer, blobStore, cloudRule, mcpTools, pendingExecs, modelId, metadata, } = active;
246
317
  const resumeMetadata = {
247
318
  ...metadata,
248
319
  assistantSeedText: [
@@ -252,6 +323,33 @@ export function handleToolResultResume(active, toolResults, bridgeKey, convKey)
252
323
  .filter(Boolean)
253
324
  .join("\n\n"),
254
325
  };
326
+ logPluginInfo("Preparing Cursor tool-result resume", {
327
+ bridgeKey,
328
+ convKey,
329
+ modelId,
330
+ toolResults,
331
+ pendingExecs,
332
+ });
333
+ const unresolved = await waitForResolvablePendingExecs(active, toolResults);
334
+ logPluginInfo("Resolved pending exec state before Cursor tool-result resume", {
335
+ bridgeKey,
336
+ convKey,
337
+ modelId,
338
+ toolResults,
339
+ pendingExecs,
340
+ unresolvedPendingExecs: unresolved,
341
+ });
342
+ if (unresolved.length > 0) {
343
+ clearInterval(heartbeatTimer);
344
+ bridge.end();
345
+ return new Response(JSON.stringify({
346
+ error: {
347
+ message: "Cursor requested a tool call but never provided resumable exec metadata. Aborting instead of retrying with synthetic ids.",
348
+ type: "invalid_request_error",
349
+ code: "cursor_missing_exec_metadata",
350
+ },
351
+ }), { status: 400, headers: { "Content-Type": "application/json" } });
352
+ }
255
353
  for (const exec of pendingExecs) {
256
354
  const result = toolResults.find((toolResult) => toolResult.toolCallId === exec.toolCallId);
257
355
  const mcpResult = result
@@ -292,7 +390,20 @@ export function handleToolResultResume(active, toolResults, bridgeKey, convKey)
292
390
  const clientMessage = create(AgentClientMessageSchema, {
293
391
  message: { case: "execClientMessage", value: execClientMessage },
294
392
  });
393
+ logPluginInfo("Sending Cursor tool-result resume message", {
394
+ bridgeKey,
395
+ convKey,
396
+ modelId,
397
+ toolCallId: exec.toolCallId,
398
+ toolName: exec.toolName,
399
+ source: exec.source,
400
+ execId: exec.execId,
401
+ execMsgId: exec.execMsgId,
402
+ cursorCallId: exec.cursorCallId,
403
+ modelCallId: exec.modelCallId,
404
+ matchedToolResult: result,
405
+ });
295
406
  bridge.write(toBinary(AgentClientMessageSchema, clientMessage));
296
407
  }
297
- return createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools, modelId, bridgeKey, convKey, resumeMetadata);
408
+ return createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, cloudRule, mcpTools, modelId, bridgeKey, convKey, resumeMetadata);
298
409
  }
@@ -1,4 +1,4 @@
1
- import { logPluginWarn } from "../logger";
1
+ import { logPluginInfo, logPluginWarn } from "../logger";
2
2
  import { buildInitialHandoffPrompt, buildTitleSourceText, buildToolResumePrompt, detectTitleRequest, parseMessages, } from "../openai/messages";
3
3
  import { buildMcpToolDefinitions, selectToolsForChoice } from "../openai/tools";
4
4
  import { activeBridges, conversationStates, createStoredConversation, deriveBridgeKey, deriveConversationKey, evictStaleConversations, hashString, normalizeAgentKey, resetStoredConversation, } from "./conversation-state";
@@ -10,6 +10,19 @@ export function handleChatCompletion(body, accessToken, context = {}) {
10
10
  const { systemPrompt, userText, turns, toolResults, pendingAssistantSummary, completedTurnsFingerprint, } = parsed;
11
11
  const modelId = body.model;
12
12
  const normalizedAgentKey = normalizeAgentKey(context.agentKey);
13
+ logPluginInfo("Handling Cursor chat completion request", {
14
+ modelId,
15
+ stream: body.stream !== false,
16
+ messageCount: body.messages.length,
17
+ toolCount: body.tools?.length ?? 0,
18
+ toolChoice: body.tool_choice,
19
+ sessionId: context.sessionId,
20
+ agentKey: normalizedAgentKey,
21
+ parsedUserText: userText,
22
+ parsedToolResults: toolResults,
23
+ hasPendingAssistantSummary: pendingAssistantSummary.trim().length > 0,
24
+ turnCount: turns.length,
25
+ });
13
26
  const titleDetection = detectTitleRequest(body);
14
27
  const isTitleAgent = titleDetection.matched;
15
28
  if (isTitleAgent) {
@@ -38,7 +51,23 @@ export function handleChatCompletion(body, accessToken, context = {}) {
38
51
  const bridgeKey = deriveBridgeKey(modelId, body.messages, context.sessionId, context.agentKey);
39
52
  const convKey = deriveConversationKey(body.messages, context.sessionId, context.agentKey);
40
53
  const activeBridge = activeBridges.get(bridgeKey);
54
+ logPluginInfo("Resolved Cursor conversation keys", {
55
+ modelId,
56
+ bridgeKey,
57
+ convKey,
58
+ hasActiveBridge: Boolean(activeBridge),
59
+ sessionId: context.sessionId,
60
+ agentKey: normalizedAgentKey,
61
+ });
41
62
  if (activeBridge && toolResults.length > 0) {
63
+ logPluginInfo("Matched OpenAI tool results to active Cursor bridge", {
64
+ bridgeKey,
65
+ convKey,
66
+ requestedModelId: modelId,
67
+ activeBridgeModelId: activeBridge.modelId,
68
+ toolResults,
69
+ pendingExecs: activeBridge.pendingExecs,
70
+ });
42
71
  activeBridges.delete(bridgeKey);
43
72
  if (activeBridge.bridge.alive) {
44
73
  if (activeBridge.modelId !== modelId) {
@@ -82,16 +111,27 @@ export function handleChatCompletion(body, accessToken, context = {}) {
82
111
  // Build the request. When tool results are present but the bridge died,
83
112
  // we must still include the last user text so Cursor has context.
84
113
  const mcpTools = buildMcpToolDefinitions(tools);
114
+ const hasPendingAssistantSummary = pendingAssistantSummary.trim().length > 0;
85
115
  const needsInitialHandoff = !stored.checkpoint &&
86
- (turns.length > 0 || pendingAssistantSummary || toolResults.length > 0);
116
+ (turns.length > 0 || hasPendingAssistantSummary || toolResults.length > 0);
87
117
  const replayTurns = needsInitialHandoff ? [] : turns;
88
118
  let effectiveUserText = needsInitialHandoff
89
119
  ? buildInitialHandoffPrompt(userText, turns, pendingAssistantSummary, toolResults)
90
- : toolResults.length > 0
120
+ : toolResults.length > 0 || hasPendingAssistantSummary
91
121
  ? buildToolResumePrompt(userText, pendingAssistantSummary, toolResults)
92
122
  : userText;
93
123
  const payload = buildCursorRequest(modelId, systemPrompt, effectiveUserText, replayTurns, stored.conversationId, stored.checkpoint, stored.blobStore);
94
124
  payload.mcpTools = mcpTools;
125
+ logPluginInfo("Built Cursor run request payload", {
126
+ modelId,
127
+ bridgeKey,
128
+ convKey,
129
+ mcpToolCount: mcpTools.length,
130
+ conversationId: stored.conversationId,
131
+ hasCheckpoint: Boolean(stored.checkpoint),
132
+ replayTurnCount: replayTurns.length,
133
+ effectiveUserText,
134
+ });
95
135
  if (body.stream === false) {
96
136
  return handleNonStreamingResponse(payload, accessToken, modelId, convKey, {
97
137
  systemPrompt,
@@ -3,3 +3,4 @@ export declare function buildCursorRequest(modelId: string, systemPrompt: string
3
3
  userText: string;
4
4
  assistantText: string;
5
5
  }>, conversationId: string, checkpoint: Uint8Array | null, existingBlobStore?: Map<string, Uint8Array>): CursorRequestPayload;
6
+ export declare function buildCursorResumeRequest(modelId: string, systemPrompt: string, conversationId: string, checkpoint: Uint8Array, existingBlobStore?: Map<string, Uint8Array>): CursorRequestPayload;
@@ -1,13 +1,8 @@
1
1
  import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
2
- import { createHash } from "node:crypto";
3
- import { AgentClientMessageSchema, AgentRunRequestSchema, ConversationActionSchema, ConversationStateStructureSchema, ConversationStepSchema, AgentConversationTurnStructureSchema, ConversationTurnStructureSchema, AssistantMessageSchema, ModelDetailsSchema, UserMessageActionSchema, UserMessageSchema, } from "../proto/agent_pb";
2
+ import { AgentClientMessageSchema, AgentRunRequestSchema, AgentConversationTurnStructureSchema, AssistantMessageSchema, ConversationActionSchema, ConversationStateStructureSchema, ConversationTurnStructureSchema, ConversationStepSchema, ModelDetailsSchema, ResumeActionSchema, UserMessageActionSchema, UserMessageSchema, } from "../proto/agent_pb";
4
3
  export function buildCursorRequest(modelId, systemPrompt, userText, turns, conversationId, checkpoint, existingBlobStore) {
5
4
  const blobStore = new Map(existingBlobStore ?? []);
6
- // System prompt → blob store (Cursor requests it back via KV handshake)
7
- const systemJson = JSON.stringify({ role: "system", content: systemPrompt });
8
- const systemBytes = new TextEncoder().encode(systemJson);
9
- const systemBlobId = new Uint8Array(createHash("sha256").update(systemBytes).digest());
10
- blobStore.set(Buffer.from(systemBlobId).toString("hex"), systemBytes);
5
+ const cloudRule = buildCloudRule(systemPrompt);
11
6
  let conversationState;
12
7
  if (checkpoint) {
13
8
  conversationState = fromBinary(ConversationStateStructureSchema, checkpoint);
@@ -40,7 +35,7 @@ export function buildCursorRequest(modelId, systemPrompt, userText, turns, conve
40
35
  turnBytes.push(toBinary(ConversationTurnStructureSchema, turnStructure));
41
36
  }
42
37
  conversationState = create(ConversationStateStructureSchema, {
43
- rootPromptMessagesJson: [systemBlobId],
38
+ rootPromptMessagesJson: [],
44
39
  turns: turnBytes,
45
40
  todos: [],
46
41
  pendingToolCalls: [],
@@ -64,6 +59,21 @@ export function buildCursorRequest(modelId, systemPrompt, userText, turns, conve
64
59
  value: create(UserMessageActionSchema, { userMessage }),
65
60
  },
66
61
  });
62
+ return buildRunRequest(modelId, conversationId, conversationState, action, blobStore, cloudRule);
63
+ }
64
+ export function buildCursorResumeRequest(modelId, systemPrompt, conversationId, checkpoint, existingBlobStore) {
65
+ const blobStore = new Map(existingBlobStore ?? []);
66
+ const cloudRule = buildCloudRule(systemPrompt);
67
+ const conversationState = fromBinary(ConversationStateStructureSchema, checkpoint);
68
+ const action = create(ConversationActionSchema, {
69
+ action: {
70
+ case: "resumeAction",
71
+ value: create(ResumeActionSchema, {}),
72
+ },
73
+ });
74
+ return buildRunRequest(modelId, conversationId, conversationState, action, blobStore, cloudRule);
75
+ }
76
+ function buildRunRequest(modelId, conversationId, conversationState, action, blobStore, cloudRule) {
67
77
  const modelDetails = create(ModelDetailsSchema, {
68
78
  modelId,
69
79
  displayModelId: modelId,
@@ -81,6 +91,11 @@ export function buildCursorRequest(modelId, systemPrompt, userText, turns, conve
81
91
  return {
82
92
  requestBytes: toBinary(AgentClientMessageSchema, clientMessage),
83
93
  blobStore,
94
+ cloudRule,
84
95
  mcpTools: [],
85
96
  };
86
97
  }
98
+ function buildCloudRule(systemPrompt) {
99
+ const content = systemPrompt.trim();
100
+ return content || undefined;
101
+ }
@@ -1,4 +1,4 @@
1
- import { errorDetails, logPluginError } from "../logger";
1
+ import { errorDetails, logPluginError, logPluginWarn } from "../logger";
2
2
  import { handleChatCompletion } from "./chat-completion";
3
3
  import { activeBridges, conversationStates } from "./conversation-state";
4
4
  let proxyServer;
@@ -42,14 +42,32 @@ export async function startProxy(getAccessToken, models = []) {
42
42
  throw new Error("Cursor proxy access token provider not configured");
43
43
  }
44
44
  const accessToken = await proxyAccessTokenProvider();
45
- const sessionId = req.headers.get("x-opencode-session-id") ??
46
- req.headers.get("x-session-id") ??
47
- undefined;
45
+ const sessionId = req.headers.get("x-session-id") ?? undefined;
48
46
  const agentKey = req.headers.get("x-opencode-agent") ?? undefined;
49
- return handleChatCompletion(body, accessToken, {
47
+ const response = await handleChatCompletion(body, accessToken, {
50
48
  sessionId,
51
49
  agentKey,
52
50
  });
51
+ if (response.status >= 400) {
52
+ let responseBody = "";
53
+ try {
54
+ responseBody = await response.clone().text();
55
+ }
56
+ catch (error) {
57
+ responseBody = `Failed to read rejected response body: ${error instanceof Error ? error.message : String(error)}`;
58
+ }
59
+ logPluginWarn("Rejected Cursor chat completion", {
60
+ path: url.pathname,
61
+ method: req.method,
62
+ sessionId,
63
+ agentKey,
64
+ status: response.status,
65
+ requestBody: body,
66
+ requestBodyText: JSON.stringify(body),
67
+ responseBody,
68
+ });
69
+ }
70
+ return response;
53
71
  }
54
72
  catch (err) {
55
73
  const message = err instanceof Error ? err.message : String(err);
@@ -7,6 +7,11 @@ export interface UnhandledExecInfo {
7
7
  execId: string;
8
8
  execMsgId: number;
9
9
  }
10
+ export interface UnsupportedServerMessageInfo {
11
+ category: "agentMessage" | "interactionUpdate" | "interactionQuery" | "execServerControl" | "toolCall";
12
+ caseName: string;
13
+ detail?: string;
14
+ }
10
15
  export declare function parseConnectEndStream(data: Uint8Array): Error | null;
11
16
  export declare function makeHeartbeatBytes(): Uint8Array;
12
17
  export declare function scheduleBridgeEnd(bridge: CursorSession): void;
@@ -34,4 +39,4 @@ export declare function computeUsage(state: StreamState): {
34
39
  completion_tokens: number;
35
40
  total_tokens: number;
36
41
  };
37
- export declare function processServerMessage(msg: AgentServerMessage, blobStore: Map<string, Uint8Array>, mcpTools: McpToolDefinition[], sendFrame: (data: Uint8Array) => void, state: StreamState, onText: (text: string, isThinking?: boolean) => void, onMcpExec: (exec: PendingExec) => void, onCheckpoint?: (checkpointBytes: Uint8Array) => void, onTurnEnded?: () => void, onUnhandledExec?: (info: UnhandledExecInfo) => void): void;
42
+ export declare function processServerMessage(msg: AgentServerMessage, blobStore: Map<string, Uint8Array>, cloudRule: string | undefined, mcpTools: McpToolDefinition[], sendFrame: (data: Uint8Array) => void, state: StreamState, onText: (text: string, isThinking?: boolean) => void, onMcpExec: (exec: PendingExec) => void, onCheckpoint?: (checkpointBytes: Uint8Array) => void, onTurnEnded?: () => void, onUnsupportedMessage?: (info: UnsupportedServerMessageInfo) => void, onUnhandledExec?: (info: UnhandledExecInfo) => void): void;